#include <bits/stdc++.h>
using namespace std;
int main();
using ll = long long;
using ull = unsigned long long;
using VB = vector<bool>;
using VVB = vector<vector<bool>>;
using VC = vector<char>;
using VVC = vector<vector<char>>;
using VI = vector<int>;
using VVI = vector<vector<int>>;
using VL = vector<ll>;
using VVL = vector<vector<ll>>;
using VD = vector<double>;
using VVD = vector<vector<double>>;
using P = pair<int, int>;
using VP = vector<P>;
using VVP = vector<vector<P>>;
using Edge = pair<ll, int>;
using Graph = vector<vector<Edge>>;
const double PI = 3.14159265358979323846;
const int INF = 1 << 29;
const ll INFLL = 1LL << 61;
template <class T>
bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return true;
  }
  return false;
}
template <class T>
bool chmin(T &a, const T &b) {
  if (b < a) {
    a = b;
    return true;
  }
  return false;
}
template <class T>
void dump(const vector<T> &v) {
  if (((int)(v).size()) == 0) return;
  for (int i = 0; i < (int)(((int)(v).size()) - 1); i++) {
    cout << v[i] << " ";
  }
  cout << v[((int)(v).size()) - 1] << endl;
}
template <class T>
void dump(const vector<vector<T>> &v) {
  for (int j = 0; j < (int)(((int)(v).size())); j++) {
    dump(v[j]);
  }
}
template <class T>
void dump(int w, int h, const vector<T> &v) {
  for (int j = 0; j < (int)(h); j++) {
    for (int i = 0; i < (int)(w - 1); i++) {
      cout << v[j * w + i] << " ";
    }
    cout << v[j * w + w - 1] << endl;
  }
}
template <class T>
T accumulate(const vector<T> &v) {
  T sum = 0;
  for (int i = 0; i < (int)(((int)(v).size())); i++) {
    sum += v[i];
  }
  return sum;
}
template <class T>
T accumulate(const vector<T> &v, int start, int length) {
  T sum = 0;
  for (int i = 0; i < (int)(length); i++) {
    sum += v[start + i];
  }
  return sum;
}
template <class T>
T average(const vector<T> &v) {
  return accumulate(v) / ((int)(v).size());
}
template <class T>
struct Matrix {
  T w, h;
  vector<T> A;
  Matrix(T w_, T h_) : w(w_), h(h_), A(w * h) {}
  T get(T x, T y) const { return A[y * w + x]; }
};
template <class T>
void input(Matrix<T> &m) {
  for (int j = 0; j < (int)(m.h); j++) {
    for (int i = 0; i < (int)(m.w); i++) {
      cin >> m.A[j * m.w + i];
    }
  }
}
template <class T>
Matrix<T> prod(const Matrix<T> &a, const Matrix<T> &b) {
  Matrix<T> m(b.w, a.h);
  for (int j = 0; j < (int)(m.h); j++) {
    for (int i = 0; i < (int)(m.w); i++) {
      ll c = 0;
      for (int k = 0; k < (int)(a.w); k++) {
        c += a.A[j * a.w + k] * b.A[k * b.w + i];
      }
      m.A[j * m.w + i] = c;
    }
  }
  return m;
}
void dump(const Matrix<int> &m) {
  for (int j = 0; j < (int)(m.h); j++) {
    for (int i = 0; i < (int)(m.w - 1); i++) {
      printf("%d ", m.A[j * m.w + i]);
    }
    printf("%d\n", m.A[j * m.w + m.w - 1]);
  }
}
void dump(const Matrix<ll> &m) {
  for (int j = 0; j < (int)(m.h); j++) {
    for (int i = 0; i < (int)(m.w - 1); i++) {
      printf("%lld ", m.A[j * m.w + i]);
    }
    printf("%lld\n", m.A[j * m.w + m.w - 1]);
  }
}
void dump(const Matrix<double> &m) {
  for (int j = 0; j < (int)(m.h); j++) {
    for (int i = 0; i < (int)(m.w - 1); i++) {
      printf("%f ", m.A[j * m.w + i]);
    }
    printf("%f\n", m.A[j * m.w + m.w - 1]);
  }
}
string &to_upper(string &s) {
  for (int i = 0; i < (int)(s.length()); i++) {
    if ('a' <= s[i] && s[i] <= 'z') {
      s[i] -= 32;
    }
  }
  return s;
}
string &to_lower(string &s) {
  for (int i = 0; i < (int)(s.length()); i++) {
    if ('A' <= s[i] && s[i] <= 'Z') {
      s[i] += 32;
    }
  }
  return s;
}
VB get_prime(int n) {
  VB prime_flag(n, true);
  prime_flag[0] = false;
  prime_flag[1] = false;
  for (int i = 2; i < n; i++) {
    if (!prime_flag[i]) continue;
    for (int j = i * 2; j < n; j += i) prime_flag[j] = false;
  }
  return prime_flag;
}
template <class T>
bool is_prime(T value) {
  for (ll i = 2; i * i <= value; i++) {
    if (value % i == 0) return false;
  }
  return true;
}
template <class T>
vector<T> get_divisors(T n) {
  vector<T> ret;
  for (T i = 1; i * i <= n; i++) {
    if (n % i == 0) {
      ret.push_back(i);
      if (i * i != n) ret.push_back(n / i);
    }
  }
  sort((ret).begin(), (ret).end());
  return ret;
}
template <class T>
vector<T> get_divisors2(T n) {
  vector<T> ret;
  for (T i = 1; i * i <= n; i++) {
    if (n % i == 0) {
      ret.push_back(i);
    }
  }
  return ret;
}
template <class T>
T gcd(T a, T b) {
  if (a < b) return gcd(b, a);
  if (a == 0 && b == 0) return 0;
  if (b == 0) return a;
  T r;
  while ((r = a % b)) {
    a = b;
    b = r;
  }
  return b;
}
template <class T>
T gcd(T a, T b, T c) {
  return gcd(gcd(a, b), c);
}
template <class T>
T gcd(const vector<T> v) {
  if (((int)(v).size()) == 0) return 0;
  if (((int)(v).size()) == 1) return v[0];
  if (((int)(v).size()) == 2) return gcd(v[0], v[1]);
  T g = v[0];
  for (int i = 1; i < ((int)(v).size()); i++) {
    g = gcd(g, v[i]);
  }
  return g;
}
template <class T>
T lcm(T a, T b) {
  return a / gcd(a, b) * b;
}
struct mint {
  static ll MOD;
  ll a;
  mint(const ll x = 0) noexcept : a(x % MOD) {}
  const mint operator+(const mint rhs) const noexcept {
    return mint(*this) += rhs;
  }
  const mint operator-(const mint rhs) const noexcept {
    return mint(*this) -= rhs;
  }
  const mint operator*(const mint rhs) const noexcept {
    return mint(*this) *= rhs;
  }
  const mint operator/(const mint rhs) const noexcept {
    return mint(*this) /= rhs;
  }
  mint &operator+=(const mint rhs) noexcept {
    a += rhs.a;
    if (a >= MOD) {
      a -= MOD;
    }
    return *this;
  }
  mint &operator-=(const mint rhs) noexcept {
    if (a < rhs.a) {
      a += MOD;
    }
    a -= rhs.a;
    return *this;
  }
  mint &operator*=(const mint rhs) noexcept {
    a = a * rhs.a % MOD;
    return *this;
  }
  mint &operator/=(mint rhs) noexcept {
    ll exp = MOD - 2;
    while (exp) {
      if (exp % 2) {
        *this *= rhs;
      }
      rhs *= rhs;
      exp /= 2;
    }
    return *this;
  }
  mint pow(ll n) const noexcept {
    ll x = a, r = 1;
    while (n > 0) {
      if (n & 1) r = r * x % MOD;
      x = x * x % MOD;
      n >>= 1;
    }
    return mint(r);
  }
  mint inverse() const noexcept {
    ll x = a, b = MOD, u = 1, v = 0;
    while (b) {
      ll t = x / b;
      x -= t * b;
      swap(x, b);
      u -= t * v;
      swap(u, v);
    }
    u %= MOD;
    if (u < 0) u += MOD;
    return mint(u);
  }
  friend ostream &operator<<(ostream &os, const mint &x);
};
ll mint::MOD = 0;
ostream &operator<<(ostream &os, const mint &x) {
  os << x.a;
  return os;
}
VI tsort(const VVI &G) {
  int N = G.size();
  VI indeg(N);
  stack<int> S;
  for (auto &v : G) {
    for (auto &e : v) {
      indeg[e]++;
    }
  }
  for (int i = 0; i < (int)(N); i++) {
    if (indeg[i] == 0) S.push(i);
  }
  VI ans;
  while (!S.empty()) {
    int v = S.top();
    S.pop();
    ans.push_back(v);
    for (auto &e : G[v]) {
      indeg[e]--;
      if (indeg[e] == 0) S.push(e);
    }
  }
  return ans;
}
VL dijkstra(const Graph &G, int start) {
  int N = G.size();
  priority_queue<Edge, vector<Edge>, greater<Edge>> que;
  VL dists(N, INFLL);
  que.push(make_pair(0LL, start));
  dists[start] = 0;
  while (!que.empty()) {
    Edge p = que.top();
    que.pop();
    if (dists[p.second] < p.first) continue;
    for (auto &e : G[p.second]) {
      if (dists[e.second] > dists[p.second] + e.first) {
        dists[e.second] = dists[p.second] + e.first;
        que.push(make_pair(dists[e.second], e.second));
      }
    }
  }
  return dists;
}
int dijkstra_get_prev_node(const Graph &G, const VL &dists, int node) {
  const vector<Edge> &edges = G[node];
  int min_index = -1;
  ll min_dist = INFLL;
  for (Edge e : edges) {
    int nv = e.second;
    if (dists[nv] < min_dist) {
      min_dist = dists[nv];
      min_index = nv;
    }
  }
  return min_index;
}
VI dijkstra_restore_path(const Graph &G, const VL &dists, int node) {
  VI path;
  path.push_back(node);
  while ((node = dijkstra_get_prev_node(G, dists, node)) >= 0) {
    path.push_back(node);
  }
  reverse((path).begin(), (path).end());
  return path;
}
struct UnionFind {
  VI parent;
  UnionFind(int size) {
    parent.resize(size);
    for (int i = 0; i < (int)(size); i++) {
      parent[i] = i;
    }
  }
  int root(int x) {
    if (parent[x] == x) return x;
    parent[x] = root(parent[x]);
    return parent[x];
  }
  bool same(int x, int y) { return root(x) == root(y); }
  void unite(int x, int y) {
    x = root(x);
    y = root(y);
    if (x != y) parent[x] = y;
  }
};
typedef tuple<int, int, int> STEdge;
struct SpanningTree {
  vector<STEdge> edges;
  void add(int cost, int a, int b) { edges.push_back(make_tuple(cost, a, b)); }
  int kuraskal(int size) {
    sort((edges).begin(), (edges).end());
    UnionFind tree(size);
    int min_cost = 0;
    for (int i = 0; i < (int)(((int)(edges).size())); i++) {
      auto &e = edges[i];
      if (!tree.same(get<1>(e), get<2>(e))) {
        min_cost += get<0>(e);
        tree.unite(get<1>(e), get<2>(e));
      }
    }
    return min_cost;
  }
};
struct L {
  int a, b, c;
  L(int a = 0, int b = 0, int c = 0) : a(a), b(b), c(c) {}
};
const int dx[] = {
    -1,
    0,
    1,
    0,
};
const int dy[] = {
    0,
    -1,
    0,
    1,
};
int main() {
  int N, M;
  cin >> N >> M;
  map<int, int> mpx, mpy;
  mpx[0] = 0;
  mpy[0] = 0;
  vector<L> lh, lv;
  for (int i = 0; i < (int)(N); i++) {
    int a, b, c;
    cin >> a >> b >> c;
    mpx[a] = 0;
    mpx[b] = 0;
    mpy[c] = 0;
    lh.emplace_back(a, b, c);
  }
  for (int i = 0; i < (int)(M); i++) {
    int a, b, c;
    cin >> c >> a >> b;
    mpy[a] = 0;
    mpy[b] = 0;
    mpx[c] = 0;
    lh.emplace_back(a, b, c);
  }
  VI xs, ys;
  for (auto &p : mpx) {
    p.second = xs.size();
    xs.push_back(p.first);
  }
  for (auto &p : mpy) {
    p.second = ys.size();
    ys.push_back(p.first);
  }
  int h = xs.size() * 2;
  int w = ys.size() * 2;
  VVI d(h, VI(w));
  for (int i = 0; i < (int)(N); i++) {
    int a = mpx[lh[i].a] * 2;
    int b = mpx[lh[i].b] * 2;
    int c = mpy[lh[i].c] * 2;
    for (int x = a; x <= b; x++) d[x][c] = -1;
  }
  for (int i = 0; i < (int)(M); i++) {
    int a = mpy[lv[i].a] * 2;
    int b = mpy[lv[i].b] * 2;
    int c = mpx[lv[i].c] * 2;
    for (int y = a; y <= b; y++) d[c][y] = -1;
  }
  queue<P> q;
  int sx = mpx[0] * 2;
  int sy = mpy[0] * 2;
  d[sx][sy] = 1;
  q.emplace(sx, sy);
  while (!q.empty()) {
    int x = q.front().first;
    int y = q.front().second;
    q.pop();
    for (int k = 0; k < (int)(4); k++) {
      int nx = x + dx[k];
      int ny = y + dy[k];
      if (nx < 0 || nx >= h) continue;
      if (ny < 0 || ny >= w) continue;
      if (d[nx][ny] != 0) continue;
      d[nx][ny] = 1;
      q.emplace(nx, ny);
    }
  }
  ll ans = 0;
  for (int x = 0; x < (int)(h); x++) {
    for (int y = 0; y < (int)(w); y++) {
      if (d[x][y] != 1) continue;
      if (x == 0 || x == h - 1 || y == 0 || y == w - 1) {
        cout << "INF" << endl;
        return 0;
      }
      if (x % 2 == 0 || y % 2 == 0) continue;
      ll ex = xs[x / 2 + 1] - xs[x / 2];
      ll ey = ys[y / 2 + 1] - ys[y / 2];
      ans += ex * ey;
    }
  }
  cout << ans << endl;
  return 0;
}
