package abc168.F

import java.io.*
import java.math.*
import java.util.*
import kotlin.math.*

var reader: BufferedReader = System.`in`.bufferedReader()
var writer: PrintWriter = PrintWriter(System.`out`)

fun next() = reader.readLine()!!
fun nextInt() = next().toInt()
fun nextLong() = next().toLong()
fun nextDouble() = next().toDouble()
fun nextStrings() = next().split(" ")
fun nextInts() = nextStrings().map { it.toInt() }
fun nextLongs() = nextStrings().map { it.toLong() }
fun nextDoubles() = nextStrings().map { it.toDouble() }

fun put(v: Any?) = writer.print(v)
fun puts() = writer.println()
fun puts(v: Any?) = writer.println(v)

var debug = false
fun dp(v: Any?) {
    if (debug) println(v)
}
fun dp() {
    if (debug) println()
}

tailrec fun gcd(a: Long, b: Long): Long {
    if(b == 0L) return a
    return gcd(b, a % b)
}

class ModInt(
    val value: Long
) {
    private val POW = mutableMapOf<Int, ModInt>()

    constructor (value: Int): this(value.toLong())

    operator fun plus(rhs: ModInt): ModInt {
        return ModInt((value + rhs.value) % M)
    }
    operator fun minus(rhs: ModInt): ModInt {
        return ModInt((value - rhs.value + M) % M)
    }
    operator fun times(rhs: ModInt): ModInt {
        return ModInt((value * rhs.value) % M)
    }
    operator fun div(rhs: ModInt): ModInt {
        return ModInt(value * inv(rhs.value) % M)
    }
    private fun inv(v: Long): Long {
        return INV.computeIfAbsent(v) {
            M - (((M / v) * inv(M % v)) % M)
        }
    }
    fun pow(v: Int): ModInt {
        return POW.computeIfAbsent(v) {
            var result = 1L
            var x = v
            var vv = value
            while (x != 0) {
                if ((x and 1) != 0) {
                    result = (result * vv) % M
                }
                vv = (vv * vv) % M
                x = x ushr 1
            }
            ModInt(result)
        }
    }

    override fun toString(): String {
        return value.toString()
    }

    companion object {
        private const val M = 1000000007L

        private val INV = mutableMapOf<Long, Long>().also {
            it[1] = 1
        }

        val ZERO = ModInt(0)
        val ONE = ModInt(1)
    }
}

fun test(case: Int, input: String, expected: String) {
    reader = input.trimIndent().reader().buffered()

    val stringWriter = StringWriter()
    writer = PrintWriter(stringWriter)

    solve()
    writer.flush()

    val actual = stringWriter.toString()

    println("[test $case]")
    if ("${expected}" == actual) {
        println("OK")
    } else {
        println("NG")
        println()
        println("expected: \"${expected}\"")
        println("actual:   \"${actual}\"")
    }
    println()
}

fun main(args: Array<String>) {
    if ((args.size == 1) && (args[0] == "test")) {
        testAll()
    } else {
        solve()
        writer.flush()
    }
}

///////////////////////////////////////////////////////////

fun testAll() {
    debug = true

    test(1,
    """
    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
    """,
    "13")
    test(2,
    """
    6 1
    -3 -1 -2
    -3 -1 1
    -2 -1 2
    1 4 -2
    1 4 -1
    1 4 1
    3 1 4
    """,
    "INF")
}

fun <T> readToList(n: Int, transformer: (List<Long>) -> T): MutableList<T> {
    return mutableListOf<T>().also { list ->
        repeat(n) {
            val lines = nextLongs()
            list.add(transformer(lines))
        }
    }
}

class Indexer(
    val X: Int,
    val Y: Int
) {
    operator fun invoke(x: Int, y: Int): Int {
        return y * X + x
    }

    fun <T> contentToString(arr: Array<T>): String {
        val builder = StringBuilder()

        builder.appendln("----------")
        for (y in 0 until Y) {
            for (x in 0 until X) {
                builder.append(arr[this(x, y)])
                builder.append(", ")
            }
            builder.appendln()
        }
        builder.append("----------")

        return builder.toString()
    }
}

val TOP = 1
val BOTTOM = 2
val LEFT = 4
val RIGHT = 8

typealias Line = Pair<Long, Long>
typealias Point = Pair<Int, Int>

fun covered(l: Long, r: Long, lines: List<Line>): Boolean {
    for (line in lines) {
        if (l < line.first) {
            break
        }
        if (r <= line.second) {
            return true
        }
    }
    return false
}

fun doSolve(): Long {

    val (N, M) = nextInts()

    val horizontal = TreeMap<Long, MutableList<Line>>()
    repeat(N) {
        val (a, b, c) = nextLongs()

        horizontal.compute(c) { _, list ->
            (list ?: mutableListOf<Line>()).also {
                it.add(Line(a, b))
            }
        }
    }
    horizontal.values.forEach {
        it.sortBy(Line::first)
    }

    val vertical = TreeMap<Long, MutableList<Line>>()
    repeat(M) {
        val (d, e, f) = nextLongs()

        vertical.compute(d) { _, list ->
            (list ?: mutableListOf<Line>()).also {
                it.add(Line(e, f))
            }
        }
    }
    vertical.values.forEach {
        it.sortBy(Line::first)
    }

    dp(horizontal)
    dp(vertical)

    val X = vertical.size + 2
    val Y = horizontal.size + 2
    val index = Indexer(X, Y)

    val areas =  Array<Long>(X*Y) { -1 }
    val walls =  Array<Int>(X*Y) { 0 }

    var startX = 0
    var startY = 0

    val windowedVertical = vertical.entries.windowed(2)
    horizontal.entries.asSequence().windowed(2).forEachIndexed { tmpY, (entry1, entry2) ->
        val y = tmpY + 1
        val (c1, hLines1) = entry1
        val (c2, hLines2) = entry2

        windowedVertical.forEachIndexed { tmpX, (entry3, entry4) ->
            val x = tmpX + 1
            //dp("-- $x, $y")
            val (d1, vLines1) = entry3
            val (d2, vLines2) = entry4

            val area = (c2 - c1) * (d2 - d1)
            areas[index(x, y)] = area

            var wall = 0
                                         /*
            if (covered(d1, d2, hLines2)) {
                wall = (wall or TOP)
            }
            if (covered(d1, d2, hLines1)) {
                wall = (wall or BOTTOM)
            }
            if (covered(c1, c2, vLines1)) {
                wall = (wall or LEFT)
            }
            if (covered(c1, c2, vLines2)) {
                wall = (wall or RIGHT)
            }
            */
            walls[index(x, y)] = wall

            if ((c1 <= 0) && (0 < c2) && (d1 <= 0) && (0 < d2)) {
                startX = x
                startY = y
            }
        }
    }

    //dp("[$X, $Y]")
    //dp("${index.contentToString(areas)}")
    //dp("${index.contentToString(walls)}")
    //dp("$startX, $startY")
    dp()

    var area = 0L
    val visited = mutableSetOf<Point>()

    val queue = ArrayDeque<Pair<Int, Int>>()
    queue.add(Pair(startX, startY))

    while (!queue.isEmpty()) {
        val p= queue.remove()
        dp("pop: $p")
        if (!visited.add(p)) {
            continue;
        }

        val (x, y) = p
        val a = areas[index(x, y)]
        dp("area: $a")
        if (a < 0) {
            return -1
        }

        area += a
        dp("-> $area")

        val wall = walls[index(x, y)]
        dp("wall: $wall")
        if ((wall and TOP) == 0) {
            dp("queue: ($x, ${y+1})")
            queue.add(Point(x, y+1))
        }
        if ((wall and BOTTOM) == 0) {
            dp("queue: ($x, ${y-1})")
            queue.add(Point(x, y-1))
        }
        if ((wall and LEFT) == 0) {
            dp("queue: (${x-1}, $y)")
            queue.add(Point(x-1, y))
        }
        if ((wall and RIGHT) == 0) {
            dp("queue: (${x+1}, $y)")
            queue.add(Point(x+1, y))
        }

        dp()
    }

    return area
}

fun solve() {
    val ret = doSolve()
    if (ret >= 0) {
        put(ret)
    } else {
        put("INF")
    }
}
