#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 ll N = 3050;
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;
int 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;
ll ans = 0;
bool wea = false;


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

void dfs(int x, int y){
    if(wea or x <= 0 or x >= curr1 - 1 or y <= 0 or y >= curr2 - 1) {wea = true; return;} 
    vis[x][y] = true;
    ans += d_l[x] * d_r[y];
    //ahora visitamos llos cuatro lados
    if(adj_r[y][x] and !vis[x][y - 1]) dfs(x, y - 1);
    if(adj_r[y + 1][x] and !vis[x][y + 1]) dfs(x, y + 1);
    if(adj_l[x][y] and !vis[x - 1][y]) dfs(x - 1, y);
    if(adj_l[x + 1][y] and !vis[x + 1][y]) dfs(x + 1, y);
}

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}});
        la.push_back(c);
        ra.push_back(a); ra.push_back(b);
    }
    for(int i = 0; i < m; i++){
        int d, e, f; cin >> d >> e >> f;
        Y.push_back({d, {e, f}});
        la.push_back(e); la.push_back(f);
        ra.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());
    //for(auto x: ra) trace(x);
    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];
    }
    //trace(curr1, curr2);
    //for(int i = 0; i < 12; i++) trace(d_r[i]);
    //ya ahora tenemos mapeado todos las weadas
    
    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 = r[a]; j < r[b]; j++) adj_l[l[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 = l[e]; j < l[f]; j++) adj_r[r[d]][j] = false; 
    }
    /*
    for(int i = 0; i < 12; i++){
        for(int j = 0; j < 12; j++){
            if(adj_r[i][j]) cout << ".";
            else cout << "#";
        }
        cout << endl;
    }*/
    //ya llenamos los vacios de mrd que no se pueden cruzar xd
    
    //ahora el dfs
    dfs(l[0], r[0]);
    
    if(wea){
        cout << "INF" << endl;
        return;
    }
    cout << ans << endl;
}

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