module mod_hash_map
  implicit none
  integer, private, parameter :: key_kind = 4
  integer, private, parameter :: val_kind = 4
  integer, private, parameter :: int_max_value = 2147483647
  integer, private, parameter :: default_initial_capacity = 16
  integer, private, parameter :: maximum_capacity = lshift(1, 30)
  real, private, parameter :: default_load_factor = 0.75

  type t_entry
    integer(key_kind) :: key
    integer(val_kind) :: val
    type(t_entry), private, pointer :: next => null()
    integer, private :: hash
  end type

  type t_entry_ptr
    type(t_entry), pointer :: ref => null()
  end type

  type t_hash_map
    type(t_entry_ptr), private, allocatable :: table(:)
    integer :: size = 0
    integer, private :: threshold = int(default_initial_capacity * default_load_factor)
    real, private :: load_factor = default_load_factor
  contains
    procedure :: is_empty => is_empty
    procedure :: is_not_empty => is_not_empty
    procedure :: put => put
    procedure :: get => get
    procedure :: get_or_default => get_or_default
    procedure :: remove => remove
    procedure :: clear => clear
    procedure :: contains_key => contains_key
    procedure :: contains_val => contains_val
  end type

  interface hash_map
    module procedure :: newhm0, newhm1, newhm2
  end interface
  
  interface urshift
    module procedure :: urshift0, urshift1
  end interface urshift

contains

  function new_entry(key, val, h) result(res)
    integer(key_kind), intent(in) :: key
    integer(val_kind), intent(in) :: val
    integer, intent(in) :: h
    type(t_entry), pointer :: res
    allocate(res)
    res%key = key
    res%val = val
    res%hash = h
  end

  integer function hash_code(key) result(res)
    integer(key_kind), intent(in) :: key
    res = merge(xor(key, urshift(key, 32)), key, key_kind == 8)
  end

  type(t_hash_map) function newhm0() result(res)
    allocate(res%table(default_initial_capacity))
  end

  type(t_hash_map) function newhm1(initial_capacity) result(res)
    integer, intent(in) :: initial_capacity
    res = newhm2(initial_capacity, default_load_factor)
  end

  type(t_hash_map) function newhm2(initial_capacity, load_factor) result(res)
    integer, intent(in) :: initial_capacity
    real, intent(in) :: load_factor
    integer :: capacity
    if (initial_capacity < 0) then
      capacity = default_initial_capacity
    else
      capacity = 1
      do while (capacity < min(initial_capacity, maximum_capacity))
        capacity = lshift(capacity, 1)
      end do
    end if

    if (load_factor <= 0 .or. load_factor /= load_factor) then
      res%load_factor = default_load_factor
    else
      res%load_factor = load_factor
    end if

    res%threshold = int(capacity * res%load_factor)
    allocate(res%table(capacity))
  end
  
  integer function urshift0(i, n) result(res)
    integer, intent(in) :: i
    integer, intent(in) :: n
    integer :: m
    m = and(n, 31)
    res = merge(i, rshift(ibclr(rshift(i, 1), 31), m - 1), m == 0)
  end

  integer(8) function urshift1(i, n) result(res)
    integer(8), intent(in) :: i
    integer, intent(in) :: n
    integer :: m
    m = and(n, 63)
    res = merge(i, rshift(ibclr(rshift(i, 1), 63), m - 1), m == 0)
  end

  integer function hash(i) result(res)
    integer, intent(in) :: i
    integer :: h
    h = xor(i, xor(urshift(i, 20), urshift(i, 12)))
    res = xor(h, xor(urshift(h, 7), urshift(h, 4)))
  end

  integer function index_for(h, length) result(res)
    integer, intent(in) :: h, length
    res = and(h, length - 1) + 1
  end

  logical function is_empty(this) result(res)
    class(t_hash_map), intent(in) :: this
    res = this%size == 0
  end

  logical function is_not_empty(this) result(res)
    class(t_hash_map), intent(in) :: this
    res = this%size /= 0
  end

  integer(val_kind) function get(this, key) result(res)
    class(t_hash_map), intent(in) :: this
    integer(key_kind), intent(in) :: key
    integer :: h
    type(t_entry), pointer :: e
    h = hash(hash_code(key))
    e => this%table(index_for(h, size(this%table)))%ref
    do while (associated(e))
      if (e%hash == h .and. e%key == key) then
        res = e%val
        return
      end if
      e => e%next
    end do
    res = 0
  end

  integer(val_kind) function get_or_default(this, key, def) result(res)
    class(t_hash_map), intent(in) :: this
    integer(key_kind), intent(in) :: key
    integer(val_kind), intent(in) :: def
    integer :: h
    type(t_entry), pointer :: e
    h = hash(hash_code(key))
    e => this%table(index_for(h, size(this%table)))%ref
    do while (associated(e))
      if (e%hash == h .and. e%key == key) then
        res = e%val
        return
      end if
      e => e%next
    end do
    res = def
  end

  logical function contains_key(this, key) result(res)
    class(t_hash_map), intent(in) :: this
    integer(key_kind), intent(in) :: key
    type(t_entry), pointer :: e
    e => get_entry(this, key)
    res = associated(e)
  end

  function get_entry(this, key) result(res)
    class(t_hash_map), intent(in) :: this
    integer(key_kind), intent(in) :: key
    integer :: h
    type(t_entry), pointer :: e
    type(t_entry), pointer :: res
    h = hash(hash_code(key))
    e => this%table(index_for(h, size(this%table)))%ref
    do while (associated(e))
      if (e%hash == h .and. e%key == key) then
        res => e
        return
      end if
      e => e%next
    end do
    res => null()
  end

  subroutine put(this, key, val)
    class(t_hash_map), intent(inout) :: this
    integer(key_kind), intent(in) :: key
    integer(val_kind), intent(in) :: val
    integer :: h, i
    type(t_entry), pointer :: e
    h = hash(hash_code(key))
    i = index_for(h, size(this%table))
    e => this%table(i)%ref
    do while (associated(e))
      if (e%hash == h .and. e%key == key) then
        e%val = val
        return
      end if
      e => e%next
    end do
    call add_entry(this, key, val, h, i)
  end

  subroutine add_entry(this, key, val, h, idx)
    class(t_hash_map), intent(inout) :: this
    integer(key_kind), intent(in) :: key
    integer(val_kind), intent(in) :: val
    integer, intent(in) :: h, idx
    type(t_entry), pointer :: e
    e => this%table(idx)%ref
    this%table(idx)%ref => new_entry(key, val, h)
    this%table(idx)%ref%next => e
    this%size = this%size + 1
    if (this%size >= this%threshold) call resize(this, 2 * size(this%table))
  end

  subroutine resize(this, new_capacity)
    class(t_hash_map), intent(inout) :: this
    integer, intent(in) :: new_capacity
    integer :: capacity, i, j
    type(t_entry), pointer :: e, next
    type(t_entry_ptr) :: table(new_capacity)
    capacity = size(this%table)
    if (capacity == maximum_capacity) then
      this%threshold = int_max_value
      return
    end if

    do j = 1, capacity
      e => this%table(j)%ref
      if (associated(e)) then
        this%table(j)%ref => null()
        do
          next => e%next
          i = index_for(e%hash, new_capacity)
          e%next => table(i)%ref
          table(i)%ref => e
          e => next
          if (.not.associated(e)) exit
        end do
      end if
    end do

    deallocate(this%table)
    allocate(this%table(new_capacity))
    do j = 1, new_capacity
      this%table(j)%ref => table(j)%ref
    end do
    this%threshold = int(new_capacity * this%load_factor)
  end

  subroutine remove(this, key)
    class(t_hash_map), intent(inout) :: this
    integer(key_kind), intent(in) :: key
    integer :: h, i
    type(t_entry), pointer :: e, prev, next
    h = hash(hash_code(key))
    i = index_for(h, size(this%table))
    prev => this%table(i)%ref
    e => prev
    do while (associated(e))
      next => e%next
      if (e%hash == h .and. e%key == key) then
        this%size = this%size - 1
        if (associated(prev, e)) then
          this%table(i)%ref => next
        else
          prev%next => next
        end if
        return
      end if
      prev => e
      e => next
    end do
  end

  subroutine clear(this)
    class(t_hash_map), intent(inout) :: this
    deallocate(this%table)
    allocate(this%table(default_initial_capacity))
    this%size = 0
  end

  logical function contains_val(this, val) result(res)
    class(t_hash_map), intent(in) :: this
    integer(val_kind), intent(in) :: val
    integer :: i
    type(t_entry), pointer :: e
    do i = 1, size(this%table)
      e => this%table(i)%ref
      do while (associated(e))
        if (e%val == val) then
          res = .true.
          return
        end if
        e => e%next
      end do
    end do
    res = .false.
  end
end module mod_hash_map
module mod_queue
  implicit none
  integer, private, parameter :: intkind = 4

  type t_node
    private
    integer(intkind) :: item
    type(t_node), pointer :: next => null()
  end type t_node

  type t_queue
    integer :: size = 0
    type(t_node), private, pointer :: head => null()
    type(t_node), private, pointer :: tail => null()
  contains
    procedure :: enqueue => enqueue
    procedure :: dequeue => dequeue
    procedure :: peek => peek
    procedure :: clear => clear
    final :: finalize
  end type t_queue

contains

  subroutine finalize(this)
    type(t_queue), intent(inout) :: this
    call clear(this)
  end

  function new_node(item) result(res)
    integer(intkind), intent(in) :: item
    type(t_node), pointer :: res
    allocate(res)
    res%item = item
  end

  subroutine enqueue(this, item)
    class(t_queue), intent(inout) :: this
    integer(intkind), intent(in) :: item
    type(t_node), pointer :: node
    node => new_node(item)
    if (associated(this%head)) then
      this%tail%next => node
    else
      this%head => node
    end if
    this%tail => node
    this%size = this%size + 1
  end

  integer(intkind) function dequeue(this) result(res)
    class(t_queue), intent(inout) :: this
    type(t_node), pointer :: node
    res = this%head%item
    node => this%head%next
    deallocate(this%head)
    this%head => node
    if (.not.associated(node)) then
      this%tail => null()
    end if
    this%size = this%size - 1
  end

  integer(intkind) function peek(this) result(res)
    class(t_queue), intent(in) :: this
    res = this%head%item
  end

  subroutine clear(this)
    class(t_queue), intent(inout) :: this
    type(t_node), pointer :: node, next
    if (.not.associated(this%head)) return
    node => this%head
    do while (associated(node%next))
      next => node%next
      deallocate(node)
      node => next
    end do
    this%head => null()
    this%tail => null()
    this%size = 0
  end

end module mod_queue
module mod_quick_sort
  implicit none
  integer, private, parameter :: intkind = 4
contains
  logical function less(a, b) result(res)
    integer(intkind), intent(in) :: a, b
    res = a <= b
  end
  subroutine swap(a, b)
    integer(intkind), intent(inout) :: a, b
    a = xor(a, b)
    b = xor(a, b)
    a = xor(a, b)
  end
  recursive subroutine quick_sort(a)
    integer(intkind), intent(inout) :: a(:)
    integer(intkind) :: p
    integer :: n, l, r, m
    n = size(a)
    l = 1
    r = n
    m = (n + 1) / 2
    p = a(l) + a(m) + a(r) - min(a(l), a(m), a(r)) - max(a(l), a(m), a(r))
    do
      do while (.not.less(p, a(l)))
        l = l + 1
      end do
      do while (.not.less(a(r), p))
        r = r - 1
      end do
      if (l >= r) exit
      call swap(a(l), a(r))
      l = l + 1
      r = r - 1
    end do
    if (1 < l - 1) call quick_sort(a(1:l - 1))
    if (r + 1 < n) call quick_sort(a(r + 1:n))
  end
end module mod_quick_sort
program single_dot
  use mod_hash_map
  use mod_queue
  implicit none
  integer :: n, m, nx = 1, ny = 1, h, w, i, j, p, q, r
  integer :: dx(4) = (/1, 0, -1, 0/), dy(4) = (/0, 1, 0, -1/)
  integer, dimension(1000) :: a = 0, b = 0, c = 0, d = 0, e = 0, f = 0
  integer, dimension(3001) :: x = 0, y = 0
  integer, allocatable :: z(:, :)
  integer(8) :: ans = 0, xx, yy
  type(t_hash_map) :: mx, my
  type(t_queue) :: qx, qy
  mx = hash_map()
  my = hash_map()
  read(*,*) n, m
  do i = 1, n
    read(*,*) a(i), b(i), c(i)
    nx = nx + 1
    x(nx) = a(i)
    nx = nx + 1
    x(nx) = b(i)
    ny = ny + 1
    y(ny) = c(i)
  end do
  do i = 1, m
    read(*,*) d(i), e(i), f(i)
    nx = nx + 1
    x(nx) = d(i)
    ny = ny + 1
    y(ny) = e(i)
    ny = ny + 1
    y(ny) = f(i)
  end do
  call distinct(nx, x(1:nx))
  call distinct(ny, y(1:ny))
  do i = 1, nx
    call mx%put(x(i), i - 1)
  end do
  do i = 1, ny
    call my%put(y(i), i - 1)
  end do
  h = 2 * nx
  w = 2 * ny
  allocate(z(0:h - 1, 0:w - 1))
  z = 0
  do i = 1, n
    p = 2 * mx%get(a(i))
    q = 2 * mx%get(b(i))
    r = 2 * my%get(c(i))
    z(p:q, r) = -1
  end do
  do i = 1, m
    p = 2 * mx%get(d(i))
    q = 2 * my%get(e(i))
    r = 2 * my%get(f(i))
    z(p, q:r) = -1
  end do
  p = 2 * mx%get(0)
  q = 2 * my%get(0)
  z(p, q) = 1
  call qx%enqueue(p)
  call qy%enqueue(q)
  do while (qx%size > 0)
    p = qx%dequeue()
    q = qy%dequeue()
    do r = 1, 4
      i = p + dx(r)
      if (i < 0 .or. i >= h) cycle
      j = q + dy(r)
      if (j < 0 .or. j >= w) cycle
      if (z(i, j) /= 0) cycle
      z(i, j) = 1
      call qx%enqueue(i)
      call qy%enqueue(j)
    end do
  end do
  do i = 0, h - 1
    do j = 0, w - 1
      if (z(i, j) /= 1) cycle
      if (i == 0 .or. i == h - 1 .or. j == 0 .or. j == w - 1) then
        write(*,'(a)') 'INF'
        stop
      end if
      if (mod(i, 2) * mod(j, 2) == 0) cycle
      p = i / 2 + 1
      q = j / 2 + 1
      xx = x(p + 1) - x(p)
      yy = y(q + 1) - y(q)
      ans = ans + xx * yy
    end do
  end do
  write(*,'(i0)') ans
contains
  subroutine distinct(n, a)
    use mod_quick_sort
    integer, intent(inout) :: n, a(:)
    integer :: b(n), i
    b = a(1:n)
    call quick_sort(b)
    a(1) = b(1)
    n = 1
    do i = 2, size(b)
      if (b(i) == a(n)) cycle
      n = n + 1
      a(n) = b(i)
    end do
  end
end program single_dot

Measured memory_bytes: 143860