#include <bits/stdc++.h>
#include <ext/pb_ds/detail/standard_policies.hpp>
#include <ext/pb_ds/assoc_container.hpp> // Common file
#include <ext/pb_ds/tree_policy.hpp> // Including tree_order_statistics_node_update
//v.erase(unique(v.begin(),v.end()),v.end());

#define ff first
#define ss second
#define fastio ios_base::sync_with_stdio(false);cin.tie(NULL)

 
typedef long double ld;
typedef long long ll;
const int N = 3550;
const ll mod = 998244353;
const int INF = 1e+9;
using namespace std;
//using namespace __gnu_pbds;
 
//typedef tree<pair<ll, ll>,null_type,less<pair<ll, ll>>,rb_tree_tag,tree_order_statistics_node_update> ordered_set;
 
#define TRACE
#ifdef TRACE
#define trace(...) __f(#__VA_ARGS__, __VA_ARGS__)
template <typename Arg1>
void __f(const char* name, Arg1&& arg1) {
  cerr << name << " : " << arg1 << endl;}
 
template <typename Arg1, typename... Args>
void __f(const char* names, Arg1&& arg1, Args&&... args) {
  const char* comma = strchr(names + 1, ',');
  cerr.write(names, comma - names) << " : " << arg1<<" | ";
  __f(comma+1, args...);}
#else
#define trace(...)
#endif

map <int, int> l, r;
ll d_l[N], d_r[N];
vector <int> la, ra;
vector <pair <int, pair <int, int>>> X, Y;
bool adj_l[N][N], adj_r[N][N], vis[N][N];
int curr1 = 1, curr2 = 1;

void llenar(){
    for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            adj_l[i][j] = true;
            adj_r[i][j] = true;
        }
    }
    
}

ll bfs(int x, int y){
    
    queue <pair <int, int>> pq;
    pq.push({x, y});
    vis[x][y] = true;
    ll ans = 0;
    while(!pq.empty()){
        int u = pq.front().ff;
        int v = pq.front().ss;
        pq.pop();
        if((u <= 0) or (u >= (curr1 - 1)) or (v <= 0) or (v >= (curr2 - 1))) return -1;
        ans += (ll)(d_l[u] * d_r[v]);

     //ahora visitamos llos cuatro lados

        if(adj_r[v][u] and !vis[u][v - 1]) {vis[u][v - 1] = true; pq.push({u, v - 1});}
        if(adj_r[v + 1][u] and !vis[u][v + 1]) {vis[u][v + 1] = true; pq.push({u, v + 1});}
        if(adj_l[u][v] and !vis[u - 1][v]) {vis[u - 1][v] = true; pq.push({u - 1, v});}
        if(adj_l[u + 1][v] and !vis[u + 1][v]) {vis[u + 1][v] = true; pq.push({u + 1, v});}
    }
    return ans;
}



void solve(){
    int n, m; cin >> n >> m;
    la.push_back(0);
    ra.push_back(0);
    for(int i = 0; i < n; i++){
        int a, b, c; cin >> a >> b >> c;
        X.push_back({c, {a, b}});
        ra.push_back(c);
        la.push_back(a); la.push_back(b);
    }
    for(int i = 0; i < m; i++){
        int d, e, f; cin >> d >> e >> f;
        Y.push_back({d, {e, f}});
        ra.push_back(e); ra.push_back(f);
        la.push_back(d);
    }
    
    sort(la.begin(), la.end()); sort(ra.begin(), ra.end());
    la.erase(unique(la.begin(),la.end()),la.end()); ra.erase(unique(ra.begin(),ra.end()),ra.end());

    if((la.size() <= 2) or (ra.size() <= 2)){
        cout << "INF" << endl;
        return;
    }
    for(auto val: la) l[val] = curr1++;
    for(int i = 0; i < la.size() - 1; i++){
        d_l[i + 1] = la[i + 1] - la[i];
    }

    for(auto val: ra) r[val] = curr2++;
    for(int i = 0; i < ra.size() - 1; i++){
        d_r[i + 1] = ra[i + 1] - ra[i];
    }

    llenar();
    
    //ahora recorremos de nuevo la mrd
    
    for(int i = 0; i < n; i++){
        int a = X[i].ss.ff, b = X[i].ss.ss, c = X[i].ff;
        for(int j = l[a]; j < l[b]; j++) adj_r[r[c]][j] = false; 
    }
    for(int i = 0; i < m; i++){
        int d = Y[i].ff, e = Y[i].ss.ff, f = Y[i].ss.ss;
        for(int j = r[e]; j < r[f]; j++) adj_l[l[d]][j] = false; 
    }
    
    ll uwu = bfs(l[0], r[0]);
    if(uwu != -1) cout << uwu << endl; 
    else cout << "INF" << endl;
}

int main(){
    fastio;
    int tc = 1; 
    //cin >> tc;
    
    while(tc--) solve();
    
    return 0;
}


Measured memory_bytes: 39036