#include <algorithm>
#include <cmath>
#include <iostream>
#include <iterator>
#include <map>
#include <queue>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using namespace std;

typedef long long ll;
typedef vector<int> vi;
typedef vector<vi> vi2;
typedef vector<vi2> vi3;
typedef pair<int, int> pii;
typedef vector<pii> vii;
typedef vector<ll> vll;
typedef vector<vll> vll2;
typedef vector<vll2> vll3;
typedef vector<bool> vb;
typedef vector<vb> vb2;

#define el '\n'
#define rep(i, a, b) for (int i = a; i < b; i++)
#define repi(i, a, b) for (int i = a; i >= b; i--)
#define umap unordered_map
#define uset unordered_set
#define vec vector
#define loop(a) for (auto &x : a)
#define all(a) a.begin(), a.end()
#define mp make_pair

struct Segment {
    int a, b, c;
    Segment(int a, int b, int c) : a(a), b(b), c(c) {}

    bool operator<(const Segment that) const {
        return c < that.c || (c == that.c && mp(a, b) < mp(that.a, that.b));
    }
};

const ll INF = 1e9 + 123;

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    int n, m;
    cin >> n >> m;
    vec<Segment> ns;
    set<int> xs, ys;  // 行，列
    rep(i, 0, n) {
        int a, b, c;
        cin >> a >> b >> c;
        ns.emplace_back(a, b, c);
        xs.insert(a);
        xs.insert(b);
        ys.insert(c);
    }
    xs.insert(-INF);
    xs.insert(INF);
    ns.emplace_back(-INF, INF, -INF);
    ns.emplace_back(-INF, INF, INF);
    n += 2;
    vec<Segment> ew;
    rep(i, 0, m) {
        int d, e, f;
        cin >> d >> e >> f;
        ew.emplace_back(e, f, d);
        ys.insert(e);
        ys.insert(f);
        xs.insert(d);
    }
    ys.insert(-INF);
    ys.insert(INF);
    ew.emplace_back(-INF, INF, -INF);
    ew.emplace_back(-INF, INF, INF);
    m += 2;

    sort(all(ns));
    sort(all(ew));

    // 先看行
    vi xss(xs.begin(), xs.end());
    vi yss(ys.begin(), ys.end());
    int szx = xss.size(), szy = yss.size();
    int total = (szx - 1) * szy;
#ifdef LOCAL
    cout << "total:" << total << el;
#endif
    vll area(total);
    vec<set<int>> no_adj(total);

    int cow = -1;

#ifdef LOCAL
    cout << "finish reading" << el;
    cout << "xss.sz=" << szx << el;
    // cout << "xss: ";
    // loop(xss) {
    //     cout << x << ' ';
    // }
    // cout << el;
    cout << "yss.sz=" << szy << el;
#endif

    // 查看行与行之间的分割
    rep(i, 0, xss.size() - 2) {
        int x1 = xss[i], x2 = xss[i + 1];
        vii delta;
        rep(k, 0, m) {
            if (ew[k].c == x2) {
                delta.emplace_back(ew[k].a, 1);
                delta.emplace_back(ew[k].b, -1);
            }
        }
        sort(all(delta));
        int covering = 0;
        int k = 0;
        rep(j, 0, yss.size() - 1) {
            int y1 = yss[j], y2 = yss[j + 1];
            int number = i * szy + j;
            if (abs(x1) == INF || abs(x2) == INF || abs(y1) == INF || abs(y2) == INF) {
                area[number] = -1;
            } else {
                area[number] = (ll)(y2 - y1) * (x2 - x1);
            }
#ifdef LOCAL
            // cout << "[" << x1 << ',' << y1 << ',' << x2 << ',' << y2 << "]" << el;
#endif
            // 考虑矩形[x1,y1,x2,y2]下边有没有线段
            while (k < delta.size() && delta[k].first <= y1) {
                covering += delta[k].second;
                k++;
            }
#ifdef LOCAL
            assert(number < total);
#endif
            if (covering > 0) {
                int below = (i + 1) * szy + j;
#ifdef LOCAL
                assert(below < total);
                // cout << i << "," << j << "--" << i + 1 << "," << j << el;
#endif
                no_adj[number].insert(below);
                no_adj[below].insert(number);
            }

            if (x1 <= 0 && 0 <= x2 && y1 <= 0 && 0 <= y2) {
#ifdef LOCAL
                cout << "cow: " << i << ',' << j << el;
#endif
                cow = number;
            }
        }
    }

#ifdef LOCAL
    cout << "cow: " << cow << el;
    cout << "finish row checking" << el;
#endif

    // 查看列与列之间的分割
    rep(j, 0, yss.size() - 2) {
        int y1 = yss[j], y2 = yss[j + 1];
        vii delta;
        rep(k, 0, n) {
            if (ns[k].c == y2) {
                delta.emplace_back(ns[k].a, 1);
                delta.emplace_back(ns[k].b, -1);
            }
        }
        sort(all(delta));
        int covering = 0;
        int k = 0;
        rep(i, 0, xss.size() - 1) {
            int x1 = xss[i], x2 = xss[i + 1];
            // 考虑矩形[x1,y1,x2,y2]下边有没有线段
            while (k < delta.size() && delta[k].first <= x1) {
                covering += delta[k].second;
                k++;
            }
            int number = i * szy + j;
            if (covering > 0) {
                int right = i * szy + j + 1;
#ifdef LOCAL
                // cout << i << "," << j << "--" << i << "," << j + 1 << el;

#endif
                no_adj[number].insert(right);
                no_adj[right].insert(number);
            }
            if (x1 <= 0 && 0 <= x2 && y1 <= 0 && 0 <= y2) {
#ifdef LOCAL
                cout << "cow: " << i << ',' << j << el;
#endif
                cow = number;
            }
        }
    }

    if (cow == -1) {
        cout << "INF" << el;
    }

#ifdef LOCAL
    cout << "finish column checking" << el;
#endif

    queue<int> q;
    q.push(cow);
    ll ans = 0;
    vb visited(total);
    visited[cow] = true;
    while (!q.empty()) {
        int h = q.front();
        q.pop();
        if (area[h] == -1) {
#ifdef LOCAL
            cout << "boarder: " << h / szy << ',' << h % szy << el;
#endif
            ans = -1;
            break;
        }
        ans += area[h];
        const vi DIR{szy, 1, -szy, -1};
        loop(DIR) {
            int next = h + x;
            if (0 <= next && next < total && !visited[next] && no_adj[h].count(next) == 0) {
                visited[next] = true;
                q.push(next);
            }
        }
    }
    if (ans == -1) {
        cout << "INF" << el;
    } else {
        cout << ans << el;
    }
}


Measured memory_bytes: 615252