#include <bits/stdc++.h>
using namespace std;

template<typename T>
vector<T> compress(vector<T> A){
    sort(A.begin(), A.end());
    A.erase(unique(A.begin(), A.end()), A.end());
    return A;
}

struct UnionFind {
    vector<int> par;
    vector<int> sz;

    UnionFind(int n=0){ if(n>0) initialize(n); }
    void initialize(int n){
        par.resize(n);
        sz.assign(n, 1);
        for(int i=0; i<n; i++){
            par[i] = i;
        }
    }

    int find(int x){
        if(par[x] == x){
            return x;
        }else{
            return par[x] = find(par[x]);
        }
    }

    bool unite(int x, int y){
        x = find(x), y = find(y);
        if(x == y) return false;
        if(sz[x] > sz[y]) swap(x, y);
        par[x] = y;
        sz[y] += sz[x];
        return true;
    }

    bool same(int x, int y){ return find(x) == find(y); }
    int size(int x){ return sz[find(x)]; }
};

int main(){
    int N, M;
    cin >> N >> M;
    
    // 無限に遠いところの座標値
    int64_t INF = 2e9;
    
    // 座標圧縮前の座標（正負の無限遠を含む）を入れていく。
    vector<int64_t> xs = {-INF, INF}, ys = {-INF, INF};
    auto add = [&](int64_t x, int64_t y){
        xs.push_back(x);
        ys.push_back(y);
    };

    vector<int64_t> A(N), B(N), C(N), D(M), E(M), F(M);
    for(int i=0; i<N; i++){
        cin >> A[i] >> B[i] >> C[i];
        add(A[i], C[i]);
        add(B[i], C[i]);
    }
    for(int i=0; i<M; i++){
        cin >> D[i] >> E[i] >> F[i];
        add(D[i], E[i]);
        add(D[i], F[i]);
    }
    xs = compress(xs);
    ys = compress(ys);

    int xsz = xs.size(), ysz = ys.size();

    // リアル座標値が圧縮座標のどのインデックスに含まれるかを求める
    auto find_x = [&](int a)->int{ return upper_bound(xs.begin(), xs.end(), a) - xs.begin() - 1; };
    auto find_y = [&](int a)->int{ return upper_bound(ys.begin(), ys.end(), a) - ys.begin() - 1; };

    // 圧縮後の座標の、真上に線がある/真左に線がある
    static bitset<3100> above[3100], left[3100];

    // 線を引く
    for(int i=0; i<N; i++){
        A[i] = find_x(A[i]);
        B[i] = find_x(B[i]);
        C[i] = find_y(C[i]);
        for(int x=A[i]; x<B[i]; x++) left[x][C[i]] = 1;
    }
    for(int i=0; i<M; i++){
        D[i] = find_x(D[i]);
        E[i] = find_y(E[i]);
        F[i] = find_y(F[i]);
        for(int y=E[i]; y<F[i]; y++) above[D[i]][y] = 1;
    }

    // UnionFindで扱うために1次元に潰す
    auto encode = [&](int i, int j){ return i*ysz+j; };
    UnionFind uf(xsz*ysz);

    // 線が引いていないところを連結
    for(int i=1; i<xsz; i++) for(int j=0; j<ysz-1; j++){
        if(!above[i][j]) uf.unite(encode(i, j), encode(i-1, j));
        if(!left[i][j]) uf.unite(encode(i, j), encode(i, j-1));
    }

    // 牛と同じ連結成分にある座標について面積を足す。無限遠と連結ならINF
    auto fail = [](){
        cout << "INF" << endl;
        exit(0);
    };
    int cow = encode(find_x(0), find_y(0));
    int64_t ans = 0;
    for(int i=0; i<xsz; i++) for(int j=0; j<ysz; j++) if(uf.same(cow, encode(i, j))){
        if(i==0 || i==xsz-1 || j==0 || j==ysz-1) fail();
        int64_t dx = xs[i+1] - xs[i], dy = ys[j+1] - ys[j];
        ans += dx*dy;
    }
    cout << ans << endl;
    return 0;
}
