package main

import (
	"bufio"
	"fmt"
	"sort"
	"os"
	"strconv"
	"strings"
)

var sc = bufio.NewScanner(os.Stdin)

// 線分構造体
type line struct {
	x1,y1 int	// 元の位置
	x2,y2 int
	sx1,sy1 int	// 圧縮後の位置
	sx2,sy2 int
}

func main() {
	list := getStdinIntArr()
	N := list[0]
	M := list[1]

	lines := make([]line, 0)		// 線分
	scalex := make([]int, 0)		// idx -> pos
	scaley := make([]int, 0)
	rscalex := make(map[int]int, 0)	// pos -> idx
	rscaley := make(map[int]int, 0)
	scalex = append(scalex, 0)		// 牛の位置
	scaley = append(scaley, 0)
	// 線分取得
	for i:=0 ; i<N ; i++ {
		list2 := getStdinIntArr()
		A := list2[0]
		B := list2[1]
		C := list2[2]
		lines = append(lines, line{A,C,B,C,0,0,0,0})
		scalex = append(scalex, A)
		scalex = append(scalex, B)
		scaley = append(scaley, C)
	}
	for i:=0 ; i<M ; i++ {
		list2 := getStdinIntArr()
		D := list2[0]
		E := list2[1]
		F := list2[2]
		lines = append(lines, line{D,E,D,F,0,0,0,0})
		scalex = append(scalex, D)
		scaley = append(scaley, E)
		scaley = append(scaley, F)
	}
	sort.Ints(scalex)
	sort.Ints(scaley)
	// 検索用に逆ハッシュを持っておく
	for idx, val := range scalex {
		rscalex[val] = idx
	}
	for idx, val := range scaley {
		rscaley[val] = idx
	}
	// 線分情報更新
	for idx, val := range lines {
		lines[idx].sx1 = rscalex[val.x1]
		lines[idx].sx2 = rscalex[val.x2]
		lines[idx].sy1 = rscaley[val.y1]
		lines[idx].sy2 = rscaley[val.y2]
	}

	// 探索用フラグ
	//（線分を入れるために２倍にする）
	mp := make([][]int, len(scaley)*2+1)
	for idx := range mp {
		mp[idx] = make([]int, len(scalex)*2+1)
	}
	// フラグに線分を入れる
	for _, val := range lines {
		if val.sx1 == val.sx2 {
			// 縦線
			x := val.sx1 * 2
			for y:=val.sy1*2 ; y<=val.sy2*2 ; y++ {
				mp[y][x] = -1
			}
		} else {
			// 横線
			y := val.sy1 * 2
			for x:=val.sx1*2 ; x<=val.sx2*2 ; x++ {
				mp[y][x] = -1
			}
		}
	}
	// 面積を入れる
	for y:=0 ; y<len(scaley)-1 ; y++ {
		for x:=0 ; x<len(scalex)-1 ; x++ {
			mp[y*2+1][x*2+1] = (scalex[x+1]-scalex[x]) * (scaley[y+1]-scaley[y])
		}
	}
	// 幅優先探索で面積を計算する
	parentX := []int {rscalex[0] * 2}	// 牛の位置
	parentY := []int {rscaley[0] * 2}
	ans := 0							// 牛の位置の面積
	for {
		pX := parentX[:]
		pY := parentY[:]
		parentX = parentX[len(parentX):]
		parentY = parentY[len(parentY):]

		for idx := range pX {
			x := pX[idx]
			y := pY[idx]

			// 境界チェック
			if x-1 <= 1 || x+1 >= len(mp[0])-1 || y-1 <= 1 || y+1 >= len(mp)-1 {
				fmt.Printf("INF\n")
				return
			}

			// ４方向
			if mp[y-1][x] != -1 {
				if x % 2 == 1 && (y-1) % 2 == 1 {
					ans += mp[y-1][x]
				}
				parentX = append(parentX, x)
				parentY = append(parentY, y-1)
				mp[y-1][x] = -1
			}
			if mp[y+1][x] != -1 {
				if x % 2 == 1 && (y+1) % 2 == 1 {
					ans += mp[y+1][x]
				}
				parentX = append(parentX, x)
				parentY = append(parentY, y+1)
				mp[y+1][x] = -1
			}
			if mp[y][x-1] != -1 {
				if (x-1) % 2 == 1 && (y) % 2 == 1 {
					ans += mp[y][x-1]
				}
				parentX = append(parentX, x-1)
				parentY = append(parentY, y)
				mp[y][x-1] = -1
			}
			if mp[y][x+1] != -1 {
				if (x+1) % 2 == 1 && (y) % 2 == 1 {
					ans += mp[y][x+1]
				}
				parentX = append(parentX, x+1)
				parentY = append(parentY, y)
				mp[y][x+1] = -1
			}
		}

		if( len(parentX) == 0 ) {
			break
		}
	}

	fmt.Printf("%d", ans)
	fmt.Printf("\n")
}

func getStdin() string {
	sc.Scan()
	return sc.Text()
}
func getStdinInt() int {
	sc.Scan()
	rtn, _ := strconv.Atoi(sc.Text())
	return rtn
}
func getStdinInt64() int64 {
	sc.Scan()
	rtn, _ := strconv.ParseInt(sc.Text(), 10, 64)
	return rtn
}
func getStdinIntArr() []int {
	sc.Scan()
	str := sc.Text()
	list := strings.Split(str, " ")
	rtn := make([]int, len(list))
	for idx, val := range list {
		rtn[idx], _ = strconv.Atoi(val)
	}
	return rtn
}
func getStdinIntArr64() []int64 {
	sc.Scan()
	str := sc.Text()
	list := strings.Split(str, " ")
	rtn := make([]int64, len(list))
	for idx, val := range list {
		rtn[idx], _ = strconv.ParseInt(val, 10, 64)
	}
	return rtn
}


Measured memory_bytes: 600192