description: Monocarp is the coach of the Berland State University programming teams. He decided to compose a problemset for a training session for his teams.
  
  Monocarp has n problems that none of his students have seen yet. The i-th problem has a topic a_i (an integer from 1 to n) and a difficulty b_i (an integer from 1 to n). All problems are different, that is, there are no two tasks that have the same topic and difficulty at the same time.
  
  Monocarp decided to select exactly 3 problems from n problems for the problemset. The problems should satisfy at least one of two conditions (possibly, both):
  
    * the topics of all three selected problems are different; 
    * the difficulties of all three selected problems are different. 
  
  
  
  Your task is to determine the number of ways to select three problems for the problemset.
  
  Input
  
  The first line contains a single integer t (1 ≤ t ≤ 50000) — the number of testcases.
  
  The first line of each testcase contains an integer n (3 ≤ n ≤ 2 ⋅ 10^5) — the number of problems that Monocarp have.
  
  In the i-th of the following n lines, there are two integers a_i and b_i (1 ≤ a_i, b_i ≤ n) — the topic and the difficulty of the i-th problem.
  
  It is guaranteed that there are no two problems that have the same topic and difficulty at the same time.
  
  The sum of n over all testcases doesn't exceed 2 ⋅ 10^5.
  
  Output
  
  Print the number of ways to select three training problems that meet either of the requirements described in the statement.
  
  Example
  
  Input
  
  
  2
  4
  2 4
  3 4
  2 1
  1 3
  5
  1 5
  2 4
  3 3
  4 2
  5 1
  
  
  Output
  
  
  3
  10
  
  Note
  
  In the first example, you can take the following sets of three problems:
  
    * problems 1, 2, 4; 
    * problems 1, 3, 4; 
    * problems 2, 3, 4. 
  
  
  
  Thus, the number of ways is equal to three.
solution: import java.io.BufferedReader;
  import java.io.InputStreamReader;
  import java.util.StringTokenizer;
  import java.util.*;
  import java.io.*;
  public class Main {
      // Graph
      // prefix sums
      //inputs
      public static void main(String args[])throws Exception{
          Input sc=new Input();
          precalculates p=new precalculates();
          StringBuilder sb=new StringBuilder();
          int t=sc.readInt();
          for(int f=0;f<t;f++){
              int n=sc.readInt();
              HashMap<Long,Long> map1=new HashMap<>();
              HashMap<Long,Long> map2=new HashMap<>();
              long a[][]=new long[n][2];
              for(int i=0;i<n;i++){
                  long d[]=sc.readArrayLong();
                  a[i][0]=d[0];
                  a[i][1]=d[1];
                  map1.put(d[0],map1.getOrDefault(d[0],0l)+1);
                  map2.put(d[1],map2.getOrDefault(d[1],0l)+1);
              }
              long s1=0,s2=0;
              long ss1=0,ss2=0;
              //System.out.println(fun(10));
              if(map1.size()>=3)
              s1=fun(map1.size());
              for(Long i:map1.keySet()){
                  s1*=map1.get(i);
                  if(map1.get(i)>=2){
                      ss1+=(n-map1.get(i))*fun2(map1.get(i)-1);
                  }
              }
              if(map2.size()>=3)
              s2=fun(map2.size());
              for(Long i:map2.keySet()){
                  s2*=map2.get(i);
                  if(map2.get(i)>=2){
                      ss2+=(n-map2.get(i))*fun2(map2.get(i)-1);
                  }
              }
              long ans=0;
              for(int i=0;i<n;i++){
                  ans+=fun2(n-1-i)-(map1.get(a[i][0])-1)*(map2.get(a[i][1])-1);
              }
              //long ss=0;
  
              //System.out.println(s1+" "+s2+" "+fun(Math.min(map1.size(),map2.size())));
              sb.append(ans+"\n");
  
          }
          System.out.print(sb);
      }
      public static long fun2(long n){
          if(n<2)
              return 0;
          if(n==2)
              return 1;
          if(n-2>=2){
              return (n*(n-1))/2;
          }else{
              if(n-3==1){
                  return n;
              }else{
                  return n;
              }
          }
      }
      public static long fun(long n){
          if(n<3)
              return 0;
          if(n==3)
              return 1;
          if(n-3>=3){
              return (n*(n-1)*(n-2))/6;
          }else{
              if(n-3==2){
                  return (n*(n-1))/2;
              }else{
                  return n;
              }
          }
      }
  }
  class Input{
      BufferedReader br;
      StringTokenizer st;
      Input(){
          br=new BufferedReader(new InputStreamReader(System.in));
          st=new StringTokenizer("");
      }
      public int[] readArray() throws Exception{
          st=new StringTokenizer(br.readLine());
          int a[]=new int[st.countTokens()];
          for(int i=0;i<a.length;i++){
              a[i]=Integer.parseInt(st.nextToken());
          }
          return a;
      }
      public long[] readArrayLong() throws Exception{
          st=new StringTokenizer(br.readLine());
          long a[]=new long[st.countTokens()];
          for(int i=0;i<a.length;i++){
              a[i]=Long.parseLong(st.nextToken());
          }
          return a;
      }
      public int readInt() throws Exception{
          st=new StringTokenizer(br.readLine());
          return Integer.parseInt(st.nextToken());
      }
      public long readLong() throws Exception{
          st=new StringTokenizer(br.readLine());
          return Long.parseLong(st.nextToken());
      }
      public String readString() throws Exception{
          return br.readLine();
      }
      public int[][] read2dArray(int n,int m)throws Exception{
          int a[][]=new int[n][m];
          for(int i=0;i<n;i++){
              st=new StringTokenizer(br.readLine());
              for(int j=0;j<m;j++){
                  a[i][j]=Integer.parseInt(st.nextToken());
              }
          }
          return a;
      }
  }
  class precalculates{
      public int[] prefixSumOneDimentional(int a[]){
          int n=a.length;
          int dp[]=new int[n];
          for(int i=0;i<n;i++){
              if(i==0)
                  dp[i]=a[i];
              else
                  dp[i]=dp[i-1]+a[i];
          }
          return dp;
      }
      public int[] postSumOneDimentional(int a[]) {
          int n = a.length;
          int dp[] = new int[n];
          for (int i = n - 1; i >= 0; i--) {
              if (i == n - 1)
                  dp[i] = a[i];
              else
                  dp[i] = dp[i + 1] + a[i];
          }
          return dp;
      }
      public int[][] prefixSum2d(int a[][]){
          int n=a.length;int m=a[0].length;
          int dp[][]=new int[n+1][m+1];
          for(int i=1;i<=n;i++){
              for(int j=1;j<=m;j++){
                  dp[i][j]=a[i-1][j-1]+dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1];
              }
          }
          return dp;
      }
  
  }
  class GraphInteger{
      HashMap<Integer,vertex> vtces;
      class vertex{
          HashMap<Integer,Integer> children;
          public vertex(){
              children=new HashMap<>();
          }
      }
      public GraphInteger(){
          vtces=new HashMap<>();
      }
      public void addVertex(int a){
          vtces.put(a,new vertex());
      }
      public void addEdge(int a,int b,int cost){
          if(!vtces.containsKey(a)){
              vtces.put(a,new vertex());
          }
          if(!vtces.containsKey(b)){
              vtces.put(b,new vertex());
          }
          vtces.get(a).children.put(b,cost);
  //        vtces.get(b).children.put(a,cost);
      }
      public boolean isCyclicDirected(){
          boolean isdone[]=new boolean[vtces.size()+1];
          boolean check[]=new boolean[vtces.size()+1];
          for(int i=1;i<=vtces.size();i++) {
              if (!isdone[i] && isCyclicDirected(i,isdone, check)) {
                  return true;
              }
          }
          return false;
      }
      private boolean isCyclicDirected(int i,boolean isdone[],boolean check[]){
          if(check[i])
              return true;
          if(isdone[i])
              return false;
          check[i]=true;
          isdone[i]=true;
          Set<Integer> set=vtces.get(i).children.keySet();
          for(Integer ii:set){
              if(isCyclicDirected(ii,isdone,check))
                  return true;
          }
          check[i]=false;
          return false;
      }
  }
  
  
  
  