# Original solution
import sys
import operator
from itertools import repeat
from functools import partial, reduce
from typing import Any, Callable, Iterable, Tuple, Union

Indices = Union[int, slice, Tuple[Union[int, slice]]]

ndgetter = (
	lambda a, i: a,
	lambda a, i: a[i[0]],
	lambda a, i: a[i[0]][i[1]],
	lambda a, i: a[i[0]][i[1]][i[2]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]][i[4]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]],
)
ndinitializor = (
	lambda s, v: [v] * s,
	lambda s, v: [v] * s[0],
	lambda s, v: [[v] * s[1] for _ in range(s[0])],
	lambda s, v: [[[v] * s[2] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[v] * s[3] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[[v] * s[4] for _ in range(s[3])] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[[[v] * s[5] for _ in range(s[4])] for _ in range(s[3])] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
)
ndshapegetter = (
	lambda a: len(a),
	lambda a: (len(a),),
	lambda a: (len(a), len(a[0])),
	lambda a: (len(a), len(a[0]), len(a[0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0]), len(a[0][0][0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0]), len(a[0][0][0][0]), len(a[0][0][0][0][0])),
)
nditerator = (
	lambda s: s,
	lambda s: ((i,) for i in range(s[0])),
	lambda s: ((i, j) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l, m) for m in range(s[4]) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l, m, n) for n in range(s[5]) for m in range(s[4]) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
)

class nd (object):
	__slots__ = ['_data', 'shape']
	
	def __init__(self, data : list, shape : Tuple[int]):
		self._data = data
		self.shape = shape
	
	def __len__(self):
		return len(self._data)
	
	def __getitem__(self, key):
		return self._data[key]
	
	def __setitem__(self, key, value):
		self._data[key] = value
	
	def __iter__(self):
		cls = type(self)
		sub_shape = self.shape[1:]
		if 0 < len(sub_shape):
			return (cls(v, sub_shape) for v in self._data)
		else:
			return iter(self._data)
	
	def at(self, indices : Tuple[int]):
		return ndgetter[len(indices)](self._data, indices)
	
	def set_at(self, indices : Tuple[int], value):
		ndgetter[len(indices) - 1](self._data, indices[:-1])[indices[-1]] = value
	
	def fill(self, value):
		for indices in nditerator[len(self.shape)](self.shape):
			ndgetter[len(indices) - 1](self._data, indices[:-1])[indices[-1]] = value
	
	def apply_indices_inplace(self, func : Callable[[Any, Tuple[int]], Any]):
		setter_getter = ndgetter[len(self.shape) - 1]
		for indices in nditerator[len(self.shape)](self.shape):
			a = setter_getter(self._data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]], indices)
	def apply_inplace(self, func : Callable[[Any], Any]):
		getter = ndgetter[len(self.shape) - 1]
		for indices in nditerator[len(self.shape)](self.shape):
			a = getter(self._data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]])
	def apply_indices(self, func : Callable[[Any, Tuple[int]], Any]):
		setter_getter = ndgetter[len(self.shape) - 1]
		new_data = ndinitializor[len(self.shape)](self.shape, None)
		for indices in nditerator[len(self.shape)](self.shape):
			a = setter_getter(new_data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]], indices)
		return nd(new_data, self.shape)
	def apply(self, func : Callable[[Any], Any]):
		setter_getter = ndgetter[len(self.shape) - 1]
		new_data = ndinitializor[len(self.shape)](self.shape, None)
		for indices in nditerator[len(self.shape)](self.shape):
			a = setter_getter(new_data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]])
		return nd(new_data, self.shape)
	
	def operate(self, other, operator):
		if not isinstance(other, list):
			return self.apply(lambda element: operator(element, other))
		else:
			getter_getter = ndgetter[len(self.shape)]
			return self.apply_indices(
				lambda element, indices: operator(
					element,
					getter_getter(other, indices)
				)
			)
	def roperate(self, other, operator):
		if not isinstance(other, list):
			return self.apply(lambda element: operator(other, element))
		else:
			getter_getter = ndgetter[len(self.shape)]
			return self.apply_indices(
				lambda element, indices: operator(
					getter_getter(other, indices),
					element
				)
			)
	def ioperate(self, other, operator):
		if not isinstance(other, list):
			self.apply_inplace(lambda element: operator(element, other))
		else:
			getter_getter = ndgetter[len(self.shape)]
			self.apply_indices_inplace(
				lambda element, indices: operator(
					element,
					getter_getter(other, indices)
				)
			)
		return self
	def rioperate(self, other, operator):
		if not isinstance(other, list):
			return self.apply_inplace(lambda element: operator(other, element))
		else:
			getter_getter = ndgetter[len(self.shape)]
			return self.apply_indices_inplace(
				lambda element, indices: operator(
					getter_getter(other, indices),
					element
				)
			)
		return self
	def __add__(self, other):
		return self.operate(other, operator.add)
	def __radd__(self, other):
		return self.roperate(other, operator.add)
	def __iadd__(self, other):
		return self.ioperate(other, operator.add)
	def __sub__(self, other):
		return self.operate(other, operator.sub)
	def __rsub__(self, other):
		return self.roperate(other, operator.sub)
	def __isub__(self, other):
		return self.ioperate(other, operator.sub)
	def __mul__(self, other):
		return self.operate(other, operator.mul)
	def __rmul__(self, other):
		return self.roperate(other, operator.mul)
	def __imul__(self, other):
		return self.ioperate(other, operator.mul)
	def __matmul__(self, other):
		return self.operate(other, operator.matmul)
	def __rmatmul__(self, other):
		return self.roperate(other, operator.matmul)
	def __imatmul__(self, other):
		return self.ioperate(other, operator.matmul)
	def __truediv__(self, other):
		return self.operate(other, operator.truediv)
	def __rtruediv__(self, other):
		return self.roperate(other, operator.truediv)
	def __itruediv__(self, other):
		return self.ioperate(other, operator.truediv)
	def __floordiv__(self, other):
		return self.operate(other, operator.floordiv)
	def __rfloordiv__(self, other):
		return self.roperate(other, operator.floordiv)
	def __ifloordiv__(self, other):
		return self.ioperate(other, operator.floordiv)
	def __mod__(self, other):
		return self.operate(other, operator.mod)
	def __rmod__(self, other):
		return self.roperate(other, operator.mod)
	def __imod__(self, other):
		return self.ioperate(other, operator.mod)
	def __divmod__(self, other):
		return self.operate(other, operator.divmod)
	def __rdivmod__(self, other):
		return self.roperate(other, operator.divmod)
	def __idivmod__(self, other):
		return self.ioperate(other, operator.divmod)
	def __pow__(self, other):
		return self.operate(other, operator.pow)
	def __rpow__(self, other):
		return self.roperate(other, operator.pow)
	def __ipow__(self, other):
		return self.ioperate(other, operator.pow)
	def __lshift__(self, other):
		return self.operate(other, operator.lshift)
	def __rlshift__(self, other):
		return self.roperate(other, operator.lshift)
	def __ilshift__(self, other):
		return self.ioperate(other, operator.lshift)
	def __rshift__(self, other):
		return self.operate(other, operator.rshift)
	def __rrshift__(self, other):
		return self.roperate(other, operator.rshift)
	def __irshift__(self, other):
		return self.ioperate(other, operator.rshift)
	def __and__(self, other):
		return self.operate(other, operator.and_)
	def __rand__(self, other):
		return self.roperate(other, operator.and_)
	def __iand__(self, other):
		return self.ioperate(other, operator.and_)
	def __xor__(self, other):
		return self.operate(other, operator.xor)
	def __rxor__(self, other):
		return self.roperate(other, operator.xor)
	def __ixor__(self, other):
		return self.ioperate(other, operator.xor)
	def __or__(self, other):
		return self.operate(other, operator.or_)
	def __ror__(self, other):
		return self.roperate(other, operator.or_)
	def __ior__(self, other):
		return self.ioperate(other, operator.or_)
	def __neg__(self, other):
		return self.apply(other, operator.neg)
	def __pos__(self, other):
		return self.apply(other, operator.pos)
	def __abs__(self, other):
		return self.apply(other, operator.abs)
	def __invert__(self, other):
		return self.apply(other, operator.invert)
	def __lt__(self, other):
		return self.operate(other, operator.lt)
	def __le__(self, other):
		return self.operate(other, operator.le)
	def __eq__(self, other):
		return self.operate(other, operator.eq)
	def __ne__(self, other):
		return self.operate(other, operator.ne)
	def __gt__(self, other):
		return self.operate(other, operator.gt)
	def __ge__(self, other):
		return self.operate(other, operator.ge)
	
	@classmethod
	def full(cls, fill_value, shape : Union[int, Tuple[int], Iterable[int]]):
		if isinstance(shape, int):
			return cls(ndinitializor[0](shape, fill_value), (shape,))
		elif not isinstance(shape, tuple):
			shape = tuple(shape)
		return cls(ndinitializor[len(shape)](shape, fill_value), shape)
	
	@classmethod
	def list_fromiter(cls, iterable, ndim=1):
		if ndim == 1:
			return list(iterable)
		else:
			return list(map(cls.list_fromiter, iterable))
	
	@classmethod
	def fromiter(cls, iterable, ndim=1):
		new_data = cls.list_fromiter(iterable, ndim)
		return cls(new_data, ndshapegetter[ndim](new_data))
	
	@classmethod
	def nones(cls, shape : Union[int, Tuple[int], Iterable[int]]):
		return cls.full(None, shape)
	
	@classmethod
	def zeros(cls, shape : Union[int, Tuple[int], Iterable[int]], type=int):
		return cls.full(type(0), shape)
	
	@classmethod
	def ones(cls, shape : Union[int, Tuple[int], Iterable[int]], type=int):
		return cls.full(type(1), shape)
	
	@classmethod
	def partial_map(cls, func, *left_args, **left_kwargs):
		def nd_func(a : nd, *right_args, **right_kwargs):
			return cls.fromiter(
				func(*left_args, v, *right_args, **left_kwargs, **right_kwargs)
				for v in a._data
			)
		return nd_func
	
	@classmethod
	def vectorize(cls, func, *left_args, **left_kwargs):
		def nd_func(a : nd, *right_args, **right_kwargs):
			return a.apply(lambda v: func(
				*left_args, v, *right_args, **left_kwargs, **right_kwargs
			))
		return nd_func



def bytes_to_str(x : bytes):
	return x.decode('utf-8')

def inputs(func=bytes_to_str, sep=None, maxsplit=-1):
	return list(map(func, sys.stdin.buffer.readline().split(sep=sep, maxsplit=maxsplit)))

def inputs_1d(func=bytes_to_str, **kwargs):
	return nd(inputs(func, **kwargs))

def inputs_2d(nrows : int, func=bytes_to_str, **kwargs):
	return nd.fromiter(
		(inputs(func, **kwargs) for _ in range(nrows)),
		ndim=2
	)

def inputs_2d_T(nrows : int, func=bytes_to_str, **kwargs):
	return nd.fromiter(
		list(zip(*(inputs(func, **kwargs) for _ in range(nrows)))),
		ndim=2
	)

from collections import deque
import operator
from typing import Optional


class BreadthFirstSearch (object):
	def __init__(self, shape : Tuple[int]):
		self.shape = shape
		self.pushed = nd.zeros(shape, type=bool)
		self._deque = deque()
	
	def push(self, position : Tuple[int]):
		if not self.pushed.at(position):
			self.pushed.set_at(position, True)
			self._deque.append(position)
	
	def peek(self) -> Optional[Tuple[int]]:
		return self._deque[0] if self._deque else None
	
	def pop(self) -> Optional[Tuple[int]]:
		return self._deque.popleft() if self._deque else None
	
	def __bool__(self):
		return bool(self._deque)

from bisect import bisect_left, bisect_right


N, M = inputs(int)
A, B, C = inputs_2d_T(N, int)
D, E, F = inputs_2d_T(M, int)
xs = sorted(set(C))
ys = sorted(set(D))

x_guard = nd.zeros((len(xs)+1, len(ys)+1), type=bool)
y_guard = nd.zeros((len(xs)+1, len(ys)+1), type=bool)

C = nd.partial_map(bisect_right, xs)(C)
A = nd.partial_map(bisect_left, ys)(A)
A += 1
B = nd.partial_map(bisect_right, ys)(B)

D = nd.partial_map(bisect_right, ys)(D)
E = nd.partial_map(bisect_left, xs)(E)
E += 1
F = nd.partial_map(bisect_right, xs)(F)

for a, b, c in zip(A, B, C):
	for y in range(a, b):
		x_guard[c][y] = True

for d, e, f in zip(D, E, F):
	for x in range(e, f):
		y_guard[x][d] = True

cow = (
	bisect_right(xs, 0),
	bisect_right(ys, 0)
)

bfs = BreadthFirstSearch(shape=(len(xs)+1, len(ys)+1))
bfs.push(cow)

area = 0
while bfs:
	xi, yi = bfs.pop()
	
	if 0 < xi < len(xs) and 0 < yi < len(ys):
		area += (xs[xi] - xs[xi-1]) * (ys[yi] - ys[yi-1])
		if not x_guard[xi][yi]:
			bfs.push((xi-1, yi))
		if not x_guard[xi+1][yi]:
			bfs.push((xi+1, yi))
		if not y_guard[xi][yi]:
			bfs.push((xi, yi-1))
		if not y_guard[xi][yi+1]:
			bfs.push((xi, yi+1))
	else:
		area = 'INF'
		break

print(area)



# Optimized solution
import sys
import operator
from itertools import repeat
from functools import partial, reduce
from typing import Any, Callable, Iterable, Tuple, Union

Indices = Union[int, slice, Tuple[Union[int, slice]]]

ndgetter = (
	lambda a, i: a,
	lambda a, i: a[i[0]],
	lambda a, i: a[i[0]][i[1]],
	lambda a, i: a[i[0]][i[1]][i[2]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]][i[4]],
	lambda a, i: a[i[0]][i[1]][i[2]][i[3]][i[4]][i[5]],
)
ndinitializor = (
	lambda s, v: [v] * s,
	lambda s, v: [v] * s[0],
	lambda s, v: [[v] * s[1] for _ in range(s[0])],
	lambda s, v: [[[v] * s[2] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[v] * s[3] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[[v] * s[4] for _ in range(s[3])] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
	lambda s, v: [[[[[[v] * s[5] for _ in range(s[4])] for _ in range(s[3])] for _ in range(s[2])] for _ in range(s[1])] for _ in range(s[0])],
)
ndshapegetter = (
	lambda a: len(a),
	lambda a: (len(a),),
	lambda a: (len(a), len(a[0])),
	lambda a: (len(a), len(a[0]), len(a[0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0]), len(a[0][0][0][0])),
	lambda a: (len(a), len(a[0]), len(a[0][0]), len(a[0][0][0]), len(a[0][0][0][0]), len(a[0][0][0][0][0])),
)
nditerator = (
	lambda s: s,
	lambda s: ((i,) for i in range(s[0])),
	lambda s: ((i, j) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l, m) for m in range(s[4]) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
	lambda s: ((i, j, k, l, m, n) for n in range(s[5]) for m in range(s[4]) for l in range(s[3]) for k in range(s[2]) for j in range(s[1]) for i in range(s[0])),
)

class nd (object):
	__slots__ = [
		'_data',
		'shape',
		'_getter_getter',
		'_setter_getter',
		'_iterator',
		'_initializer'
	]
	
	def __init__(self, data : list, shape : Tuple[int]):
		self._data = data
		self.shape = shape
		self._getter_getter = ndgetter[len(self.shape)]
		self._setter_getter = ndgetter[len(self.shape) - 1]
		self._iterator = nditerator[len(self.shape)]
		self._initializer = ndinitializor[len(self.shape)]
	
	def __len__(self):
		return len(self._data)
	
	def __getitem__(self, key):
		return self._data[key]
	
	def __setitem__(self, key, value):
		self._data[key] = value
	
	def __iter__(self):
		cls = type(self)
		sub_shape = self.shape[1:]
		if 0 < len(sub_shape):
			return (cls(v, sub_shape) for v in self._data)
		else:
			return iter(self._data)
	
	def at(self, indices : Tuple[int]):
		return self._getter_getter(self._data, indices)
	
	def set_at(self, indices : Tuple[int], value):
		self._setter_getter(self._data, indices[:-1])[indices[-1]] = value
	
	def fill(self, value):
		for indices in self._iterator(self.shape):
			self._setter_getter(self._data, indices[:-1])[indices[-1]] = value
	
	def apply_indices_inplace(self, func : Callable[[Any, Tuple[int]], Any]):
		for indices in self._iterator(self.shape):
			a = self._setter_getter(self._data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]], indices)
	def apply_inplace(self, func : Callable[[Any], Any]):
		for indices in self._iterator(self.shape):
			a = self._setter_getter(self._data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]])
	def apply_indices(self, func : Callable[[Any, Tuple[int]], Any]):
		new_data = self._initializer(self.shape, None)
		for indices in self._iterator(self.shape):
			a = self._setter_getter(new_data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]], indices)
		return nd(new_data, self.shape)
	def apply(self, func : Callable[[Any], Any]):
		new_data = self._initializer(self.shape, None)
		for indices in self._iterator(self.shape):
			a = self._setter_getter(new_data, indices[:-1])
			a[indices[-1]] = func(a[indices[-1]])
		return nd(new_data, self.shape)
	
	def operate(self, other, operator):
		if not isinstance(other, list):
			return self.apply(lambda element: operator(element, other))
		else:
			return self.apply_indices(
				lambda element, indices: operator(
					element,
					self._getter_getter(other, indices)
				)
			)
	def roperate(self, other, operator):
		if not isinstance(other, list):
			return self.apply(lambda element: operator(other, element))
		else:
			return self.apply_indices(
				lambda element, indices: operator(
					self._getter_getter(other, indices),
					element
				)
			)
	def ioperate(self, other, operator):
		if not isinstance(other, list):
			self.apply_inplace(lambda element: operator(element, other))
		else:
			self.apply_indices_inplace(
				lambda element, indices: operator(
					element,
					self._getter_getter(other, indices)
				)
			)
		return self
	def rioperate(self, other, operator):
		if not isinstance(other, list):
			return self.apply_inplace(lambda element: operator(other, element))
		else:
			return self.apply_indices_inplace(
				lambda element, indices: operator(
					self._getter_getter(other, indices),
					element
				)
			)
		return self
	def __add__(self, other):
		return self.operate(other, operator.add)
	def __radd__(self, other):
		return self.roperate(other, operator.add)
	def __iadd__(self, other):
		return self.ioperate(other, operator.add)
	def __sub__(self, other):
		return self.operate(other, operator.sub)
	def __rsub__(self, other):
		return self.roperate(other, operator.sub)
	def __isub__(self, other):
		return self.ioperate(other, operator.sub)
	def __mul__(self, other):
		return self.operate(other, operator.mul)
	def __rmul__(self, other):
		return self.roperate(other, operator.mul)
	def __imul__(self, other):
		return self.ioperate(other, operator.mul)
	def __matmul__(self, other):
		return self.operate(other, operator.matmul)
	def __rmatmul__(self, other):
		return self.roperate(other, operator.matmul)
	def __imatmul__(self, other):
		return self.ioperate(other, operator.matmul)
	def __truediv__(self, other):
		return self.operate(other, operator.truediv)
	def __rtruediv__(self, other):
		return self.roperate(other, operator.truediv)
	def __itruediv__(self, other):
		return self.ioperate(other, operator.truediv)
	def __floordiv__(self, other):
		return self.operate(other, operator.floordiv)
	def __rfloordiv__(self, other):
		return self.roperate(other, operator.floordiv)
	def __ifloordiv__(self, other):
		return self.ioperate(other, operator.floordiv)
	def __mod__(self, other):
		return self.operate(other, operator.mod)
	def __rmod__(self, other):
		return self.roperate(other, operator.mod)
	def __imod__(self, other):
		return self.ioperate(other, operator.mod)
	def __divmod__(self, other):
		return self.operate(other, operator.divmod)
	def __rdivmod__(self, other):
		return self.roperate(other, operator.divmod)
	def __idivmod__(self, other):
		return self.ioperate(other, operator.divmod)
	def __pow__(self, other):
		return self.operate(other, operator.pow)
	def __rpow__(self, other):
		return self.roperate(other, operator.pow)
	def __ipow__(self, other):
		return self.ioperate(other, operator.pow)
	def __lshift__(self, other):
		return self.operate(other, operator.lshift)
	def __rlshift__(self, other):
		return self.roperate(other, operator.lshift)
	def __ilshift__(self, other):
		return self.ioperate(other, operator.lshift)
	def __rshift__(self, other):
		return self.operate(other, operator.rshift)
	def __rrshift__(self, other):
		return self.roperate(other, operator.rshift)
	def __irshift__(self, other):
		return self.ioperate(other, operator.rshift)
	def __and__(self, other):
		return self.operate(other, operator.and_)
	def __rand__(self, other):
		return self.roperate(other, operator.and_)
	def __iand__(self, other):
		return self.ioperate(other, operator.and_)
	def __xor__(self, other):
		return self.operate(other, operator.xor)
	def __rxor__(self, other):
		return self.roperate(other, operator.xor)
	def __ixor__(self, other):
		return self.ioperate(other, operator.xor)
	def __or__(self, other):
		return self.operate(other, operator.or_)
	def __ror__(self, other):
		return self.roperate(other, operator.or_)
	def __ior__(self, other):
		return self.ioperate(other, operator.or_)
	def __neg__(self, other):
		return self.apply(other, operator.neg)
	def __pos__(self, other):
		return self.apply(other, operator.pos)
	def __abs__(self, other):
		return self.apply(other, operator.abs)
	def __invert__(self, other):
		return self.apply(other, operator.invert)
	def __lt__(self, other):
		return self.operate(other, operator.lt)
	def __le__(self, other):
		return self.operate(other, operator.le)
	def __eq__(self, other):
		return self.operate(other, operator.eq)
	def __ne__(self, other):
		return self.operate(other, operator.ne)
	def __gt__(self, other):
		return self.operate(other, operator.gt)
	def __ge__(self, other):
		return self.operate(other, operator.ge)
	
	@classmethod
	def full(cls, fill_value, shape : Union[int, Tuple[int], Iterable[int]]):
		if isinstance(shape, int):
			return cls(ndinitializor[0](shape, fill_value), (shape,))
		elif not isinstance(shape, tuple):
			shape = tuple(shape)
		return cls(ndinitializor[len(shape)](shape, fill_value), shape)
	
	@classmethod
	def list_fromiter(cls, iterable, ndim=1):
		if ndim == 1:
			return list(iterable)
		else:
			return list(map(cls.list_fromiter, iterable))
	
	@classmethod
	def fromiter(cls, iterable, ndim=1):
		new_data = cls.list_fromiter(iterable, ndim)
		return cls(new_data, ndshapegetter[ndim](new_data))
	
	@classmethod
	def nones(cls, shape : Union[int, Tuple[int], Iterable[int]]):
		return cls.full(None, shape)
	
	@classmethod
	def zeros(cls, shape : Union[int, Tuple[int], Iterable[int]], type=int):
		return cls.full(type(0), shape)
	
	@classmethod
	def ones(cls, shape : Union[int, Tuple[int], Iterable[int]], type=int):
		return cls.full(type(1), shape)
	
	@classmethod
	def partial_map(cls, func, *left_args, **left_kwargs):
		def nd_func(a : nd, *right_args, **right_kwargs):
			return cls.fromiter(
				func(*left_args, v, *right_args, **left_kwargs, **right_kwargs)
				for v in a._data
			)
		return nd_func
	
	@classmethod
	def vectorize(cls, func, *left_args, **left_kwargs):
		def nd_func(a : nd, *right_args, **right_kwargs):
			return a.apply(lambda v: func(
				*left_args, v, *right_args, **left_kwargs, **right_kwargs
			))
		return nd_func



def bytes_to_str(x : bytes):
	return x.decode('utf-8')

def inputs(func=bytes_to_str, sep=None, maxsplit=-1):
	return list(map(func, sys.stdin.buffer.readline().split(sep=sep, maxsplit=maxsplit)))

def inputs_1d(func=bytes_to_str, **kwargs):
	return nd(inputs(func, **kwargs))

def inputs_2d(nrows : int, func=bytes_to_str, **kwargs):
	return nd.fromiter(
		(inputs(func, **kwargs) for _ in range(nrows)),
		ndim=2
	)

def inputs_2d_T(nrows : int, func=bytes_to_str, **kwargs):
	return nd.fromiter(
		list(zip(*(inputs(func, **kwargs) for _ in range(nrows)))),
		ndim=2
	)

from collections import deque
import operator
from typing import Optional


class BreadthFirstSearch (object):
	def __init__(self, shape : Tuple[int]):
		self.shape = shape
		self.pushed = nd.zeros(shape, type=bool)
		self._deque = deque()
	
	def push(self, position : Tuple[int]):
		if not self.pushed.at(position):
			self.pushed.set_at(position, True)
			self._deque.append(position)
	
	def peek(self) -> Optional[Tuple[int]]:
		return self._deque[0] if self._deque else None
	
	def pop(self) -> Optional[Tuple[int]]:
		return self._deque.popleft() if self._deque else None
	
	def __bool__(self):
		return bool(self._deque)

from bisect import bisect_left, bisect_right


N, M = inputs(int)
A, B, C = inputs_2d_T(N, int)
D, E, F = inputs_2d_T(M, int)
xs = sorted(set(C))
ys = sorted(set(D))

x_guard = nd.zeros((len(xs)+1, len(ys)+1), type=bool)
y_guard = nd.zeros((len(xs)+1, len(ys)+1), type=bool)

C = nd.partial_map(bisect_right, xs)(C)
A = nd.partial_map(bisect_left, ys)(A)
A += 1
B = nd.partial_map(bisect_right, ys)(B)

D = nd.partial_map(bisect_right, ys)(D)
E = nd.partial_map(bisect_left, xs)(E)
E += 1
F = nd.partial_map(bisect_right, xs)(F)

for a, b, c in zip(A, B, C):
	for y in range(a, b):
		x_guard[c][y] = True

for d, e, f in zip(D, E, F):
	for x in range(e, f):
		y_guard[x][d] = True

cow = (
	bisect_right(xs, 0),
	bisect_right(ys, 0)
)

bfs = BreadthFirstSearch(shape=(len(xs)+1, len(ys)+1))
bfs.push(cow)

area = 0
while bfs:
	xi, yi = bfs.pop()
	
	if 0 < xi < len(xs) and 0 < yi < len(ys):
		area += (xs[xi] - xs[xi-1]) * (ys[yi] - ys[yi-1])
		if not x_guard[xi][yi]:
			bfs.push((xi-1, yi))
		if not x_guard[xi+1][yi]:
			bfs.push((xi+1, yi))
		if not y_guard[xi][yi]:
			bfs.push((xi, yi-1))
		if not y_guard[xi][yi+1]:
			bfs.push((xi, yi+1))
	else:
		area = 'INF'
		break

print(area)