There is a grass field that stretches infinitely.

In this field, there is a negligibly small cow. Let (x, y) denote the point that is x\ \mathrm{cm} south and y\ \mathrm{cm} east of the point where the cow stands now. The cow itself is standing at (0, 0).

There are also N north-south lines and M east-west lines drawn on the field. The i-th north-south line is the segment connecting the points (A_i, C_i) and (B_i, C_i), and the j-th east-west line is the segment connecting the points (D_j, E_j) and (D_j, F_j).

What is the area of the region the cow can reach when it can move around as long as it does not cross the segments (including the endpoints)? If this area is infinite, print `INF` instead.

Constraints

* All values in input are integers between -10^9 and 10^9 (inclusive).
* 1 \leq N, M \leq 1000
* A_i < B_i\ (1 \leq i \leq N)
* E_j < F_j\ (1 \leq j \leq M)
* The point (0, 0) does not lie on any of the given segments.

Input

Input is given from Standard Input in the following format:


N M
A_1 B_1 C_1
:
A_N B_N C_N
D_1 E_1 F_1
:
D_M E_M F_M


Output

If the area of the region the cow can reach is infinite, print `INF`; otherwise, print an integer representing the area in \mathrm{cm^2}.

(Under the constraints, it can be proved that the area of the region is always an integer if it is not infinite.)

Examples

Input

5 6
1 2 0
0 1 1
0 2 2
-3 4 -1
-2 6 3
1 0 1
0 1 2
2 0 2
-1 -4 5
3 -2 4
1 2 4


Output

13


Input

6 1
-3 -1 -2
-3 -1 1
-2 -1 2
1 4 -2
1 4 -1
1 4 1
3 1 4


Output

INF
#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;
}
