module mod_t_item
  implicit none
  type :: t_item
    integer :: pos
  end type t_item
  private
  public :: t_item, itemize
contains
  function itemize(pos) result(i)
    implicit none
    integer :: pos
    type(t_item) :: i
    i%pos = pos
    return
  end function itemize
end module mod_t_item
module mod_t_list
  use mod_t_item
  implicit none
  type :: t_list
    integer :: vol
    type(t_item), pointer :: arr(:)
  end type t_list
  private
  public :: t_list, init_list, release_list, push_back
  public :: pop_back, get_at, size_of_list
contains
  subroutine init_list(list)
    implicit none
    type(t_list), intent(inout) :: list
    allocate(list%arr(1))
    list%vol = 0
    return
  end subroutine init_list
  subroutine release_list(list)
    implicit none
    type(t_list), intent(inout) :: list
    if (associated(list%arr)) deallocate(list%arr)
    return
  end subroutine release_list
  subroutine push_back(list,item)
    implicit none
    type(t_list), intent(inout) :: list
    type(t_item), intent(in) :: item
    type(t_item), allocatable :: tmp(:)
    if (size(list%arr).eq.list%vol) then
      allocate(tmp(list%vol))
      tmp = list%arr
      deallocate(list%arr)
      allocate(list%arr(2*list%vol))
      list%arr(1:list%vol) = tmp
      deallocate(tmp)
    end if
    list%vol = list%vol+1
    list%arr(list%vol) = item
    return
  end subroutine push_back
  function pop_back(list) result(item)
    implicit none
    type(t_list), intent(inout) :: list
    type(t_item) :: item
    item = list%arr(list%vol)
    list%vol = list%vol-1
    return
  end function pop_back
  function get_at(list,i) result(item)
    implicit none
    type(t_list), intent(in) :: list
    integer, intent(in) :: i
    type(t_item) :: item
    item = list%arr(i)
    return
  end function get_at
  function size_of_list(list) result(s)
    implicit none
    type(t_list), intent(in) :: list
    integer :: s
    s = list%vol
    return
  end function size_of_list
end module mod_t_list
module mod_t_graph
  use mod_t_item
  use mod_t_list
  implicit none
  logical, allocatable :: used(:)
  type :: t_graph
    type(t_list), pointer :: edges(:)
  end type t_graph
  private
  public :: used
  public :: t_graph, init_graph, release_graph, add_edge, add, dfs
contains
  subroutine init_graph(graph,n)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer, intent(in) :: n
    integer :: i
    allocate(used(n))
    used = .false.
    allocate(graph%edges(n))
    do i = 1, n
      call init_list(graph%edges(i))
    end do
    return
  end subroutine init_graph
  subroutine release_graph(graph)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer :: n, i
    deallocate(used)
    n = size(graph%edges)
    do i = 1, n
      call release_list(graph%edges(i))
    end do
    deallocate(graph%edges)
    return
  end subroutine release_graph
  subroutine add_edge(graph,i,edge)
    implicit none
    type(t_graph), intent(inout) :: graph
    type(t_item), intent(in) :: edge
    integer, intent(in) :: i
    call push_back(graph%edges(i),edge)
    return
  end subroutine add_edge
  subroutine add(graph,i,pos)
    implicit none
    type(t_graph), intent(inout) :: graph
    integer, intent(in) :: i, pos
    call push_back(graph%edges(i),itemize(pos))
    return
  end subroutine add
  recursive subroutine dfs(graph,v,x,y)
    implicit none
    type(t_graph), intent(inout) :: graph
    type(t_item) :: e
    integer :: v, x, y, l, i
    if (used(v)) return
    used(v) = .true.
    if (v.gt.100000) then
      y = y+1
    else
      x = x+1
    end if
    l = size_of_list(graph%edges(v))
    if (l.eq.0) return
    do i = 1, l
      e = get_at(graph%edges(v),i)
      call dfs(graph,e%pos,x,y)
    end do
    return
  end subroutine dfs
end module mod_t_graph
program must_be_rectangular
  use mod_t_graph
  implicit none
  type(t_graph) :: graph
  integer :: n, x, y, i
  integer(8) :: m
  call init_graph(graph,200000)
  read(*,*) n
  do i = 1, n
    read(*,*) x, y
    call add(graph,x,100000+y)
    call add(graph,100000+y,x)
  end do
  m = 0_8
  do i = 1, 200000
    if (used(i)) cycle
    x = 0
    y = 0
    call dfs(graph,i,x,y)
    m = m+int(x,8)*int(y,8)
  end do
  write(*,'(i0)') m-int(n,8)
  call release_graph(graph)
  stop
end program must_be_rectangular