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.OutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.PrintWriter;
  import java.io.BufferedWriter;
  import java.util.InputMismatchException;
  import java.io.IOException;
  import java.util.Random;
  import java.util.ArrayList;
  import java.util.Map;
  import java.io.Writer;
  import java.util.Map.Entry;
  import java.io.OutputStreamWriter;
  import java.util.NoSuchElementException;
  import java.io.InputStream;
  
  /**
   * Built using CHelper plug-in
   * Actual solution is at the top
   */
  public class Main {
      public static void main(String[] args) {
          InputStream inputStream = System.in;
          OutputStream outputStream = System.out;
          InputReader in = new InputReader(inputStream);
          OutputWriter out = new OutputWriter(outputStream);
          DTrainingSession solver = new DTrainingSession();
          solver.solve(1, in, out);
          out.close();
      }
  
      static class DTrainingSession {
          public void solve(int testNumber, InputReader in, OutputWriter out) {
              var tc = in.nextInt();
              for (int i = 0; i < tc; i++) {
                  solution(i, in, out);
              }
          }
  
          void solution(int testNumber, InputReader in, OutputWriter out) {
              int n = in.nextInt();
              EzIntIntHashMap s = new EzIntIntHashMap();
              EzIntIntHashMap d = new EzIntIntHashMap();
              ArrayList<EzIntIntPair> pp = new ArrayList<>();
              for (int i = 0; i < n; i++) {
                  var a = in.nextInt();
                  var b = in.nextInt();
                  s.put(a, s.get(a) + 1);
                  d.put(b, d.get(b) + 1);
                  pp.add(new EzIntIntPair(a, b));
              }
              long combos = BinomialCoefficient.nCr4(n, 3);
              for (var xx : pp) {
                  // for every one, what combos, (not including myself) do I block out?
                  combos -= (long) (s.get(xx.first) - 1) * (long) (d.get(xx.second) - 1);
              }
              out.println(combos);
          }
  
      }
  
      static class Arrays {
          private Arrays() {
          }
  
          public static void sort(int[] a) {
              EzIntSort.sort(a);
          }
  
      }
  
      static class EzIntIntPair implements Comparable<EzIntIntPair> {
          private static final int HASHCODE_INITIAL_VALUE = 0x811c9dc5;
          private static final int HASHCODE_MULTIPLIER = 0x01000193;
          public int first;
          public int second;
  
          public EzIntIntPair(int first, int second) {
              this.first = first;
              this.second = second;
          }
  
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              EzIntIntPair that = (EzIntIntPair) o;
  
              return first == that.first && second == that.second;
          }
  
          public int hashCode() {
              int hash = HASHCODE_INITIAL_VALUE;
              hash = (hash ^ PrimitiveHashCalculator.getHash(first)) * HASHCODE_MULTIPLIER;
              hash = (hash ^ PrimitiveHashCalculator.getHash(second)) * HASHCODE_MULTIPLIER;
              return hash;
          }
  
          public int compareTo(EzIntIntPair o) {
              int res = Integer.compare(first, o.first);
              if (res == 0) {
                  res = Integer.compare(second, o.second);
              }
              return res;
          }
  
          public String toString() {
              return "(" + first + ", " + second + ")";
          }
  
      }
  
      static class EzIntIntHashMap implements EzIntIntMap {
          private static final int DEFAULT_CAPACITY = 8;
          private static final int HASHCODE_INITIAL_VALUE = 0x811c9dc5;
          private static final int HASHCODE_MULTIPLIER = 0x01000193;
          private static final byte FREE = 0;
          private static final byte FILLED = 2;
          public static final int DEFAULT_NULL_VALUE = 0;
          private static final Random rnd = new Random(System.nanoTime());
          private static final int POS_RANDOM_SHIFT_1;
          private static final int POS_RANDOM_SHIFT_2;
          private static final int STEP_RANDOM_SHIFT_1;
          private static final int STEP_RANDOM_SHIFT_2;
          private int[] keys;
          private int[] values;
          private byte[] status;
          private int size;
          private int removedCount;
          private int mask;
          private boolean returnedNull;
          private final int hashSeed;
  
          static {
              POS_RANDOM_SHIFT_1 = rnd.nextInt(10) + 11;
              POS_RANDOM_SHIFT_2 = rnd.nextInt(10) + 21;
              STEP_RANDOM_SHIFT_1 = rnd.nextInt(10) + 11;
              STEP_RANDOM_SHIFT_2 = rnd.nextInt(10) + 21;
          }
  
          public EzIntIntHashMap() {
              this(DEFAULT_CAPACITY);
          }
  
          public EzIntIntHashMap(int capacity) {
              if (capacity < 0) {
                  throw new IllegalArgumentException("Capacity must be non-negative");
              }
              // Actually we need 4x more memory
              int length = 4 * Math.max(1, capacity);
              if ((length & (length - 1)) != 0) {
                  length = Integer.highestOneBit(length) << 1;
              }
              // Length is a power of 2 now
              initEmptyTable(length);
              returnedNull = false;
              hashSeed = rnd.nextInt();
          }
  
          public EzIntIntHashMap(EzIntIntMap map) {
              this(map.size());
              for (EzIntIntMapIterator it = map.iterator(); it.hasNext(); it.next()) {
                  put(it.getKey(), it.getValue());
              }
          }
  
          public EzIntIntHashMap(Map<Integer, Integer> javaMap) {
              this(javaMap.size());
              for (Map.Entry<Integer, Integer> e : javaMap.entrySet()) {
                  put(e.getKey(), e.getValue());
              }
          }
  
          private int getStartPos(int h) {
              h ^= hashSeed;
              h ^= (h >>> POS_RANDOM_SHIFT_1) ^ (h >>> POS_RANDOM_SHIFT_2);
              return h & mask;
          }
  
          private int getStep(int h) {
              h ^= hashSeed;
              h ^= (h >>> STEP_RANDOM_SHIFT_1) ^ (h >>> STEP_RANDOM_SHIFT_2);
              return ((h << 1) | 1) & mask;
          }
  
          public int size() {
              return size;
          }
  
          public int get(int key) {
              final int keyHash = PrimitiveHashCalculator.getHash(key);
              int pos = getStartPos(keyHash);
              final int step = getStep(keyHash);
              for (; status[pos] != FREE; pos = (pos + step) & mask) {
                  if (status[pos] == FILLED && keys[pos] == key) {
                      returnedNull = false;
                      return values[pos];
                  }
              }
              returnedNull = true;
              return DEFAULT_NULL_VALUE;
          }
  
          public int put(int key, int value) {
              final int keyHash = PrimitiveHashCalculator.getHash(key);
              int pos = getStartPos(keyHash);
              final int step = getStep(keyHash);
              for (; status[pos] == FILLED; pos = (pos + step) & mask) {
                  if (keys[pos] == key) {
                      final int oldValue = values[pos];
                      values[pos] = value;
                      returnedNull = false;
                      return oldValue;
                  }
              }
              if (status[pos] == FREE) {
                  status[pos] = FILLED;
                  keys[pos] = key;
                  values[pos] = value;
                  size++;
                  if ((size + removedCount) * 2 > keys.length) {
                      rebuild(keys.length * 2); // enlarge the table
                  }
                  returnedNull = true;
                  return DEFAULT_NULL_VALUE;
              }
              final int removedPos = pos;
              for (pos = (pos + step) & mask; status[pos] != FREE; pos = (pos + step) & mask) {
                  if (status[pos] == FILLED && keys[pos] == key) {
                      final int oldValue = values[pos];
                      values[pos] = value;
                      returnedNull = false;
                      return oldValue;
                  }
              }
              status[removedPos] = FILLED;
              keys[removedPos] = key;
              values[removedPos] = value;
              size++;
              removedCount--;
              returnedNull = true;
              return DEFAULT_NULL_VALUE;
          }
  
          public EzIntIntMapIterator iterator() {
              return new EzIntIntHashMapIterator();
          }
  
          private void rebuild(int newLength) {
              int[] oldKeys = keys;
              int[] oldValues = values;
              byte[] oldStatus = status;
              initEmptyTable(newLength);
              for (int i = 0; i < oldKeys.length; i++) {
                  if (oldStatus[i] == FILLED) {
                      put(oldKeys[i], oldValues[i]);
                  }
              }
          }
  
          private void initEmptyTable(int length) {
              keys = new int[length];
              values = new int[length];
              status = new byte[length];
              size = 0;
              removedCount = 0;
              mask = length - 1;
          }
  
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              EzIntIntHashMap that = (EzIntIntHashMap) o;
  
              if (size != that.size) {
                  return false;
              }
              for (int i = 0; i < keys.length; i++) {
                  if (status[i] == FILLED) {
                      int thatValue = that.get(keys[i]);
                      if (that.returnedNull || thatValue != values[i]) {
                          return false;
                      }
                  }
              }
              return true;
          }
  
          private void randomShuffle(int[] array) {
              int n = array.length;
              for (int i = 0; i < n; i++) {
                  int j = i + rnd.nextInt(n - i);
                  int tmp = array[i];
                  array[i] = array[j];
                  array[j] = tmp;
              }
          }
  
          public int hashCode() {
              int[] entryHashes = new int[size];
              for (int i = 0, j = 0; i < status.length; i++) {
                  if (status[i] == FILLED) {
                      int hash = HASHCODE_INITIAL_VALUE;
                      hash = (hash ^ PrimitiveHashCalculator.getHash(keys[i])) * HASHCODE_MULTIPLIER;
                      hash = (hash ^ PrimitiveHashCalculator.getHash(values[i])) * HASHCODE_MULTIPLIER;
                      entryHashes[j++] = hash;
                  }
              }
              randomShuffle(entryHashes);
              Arrays.sort(entryHashes);
              int hash = HASHCODE_INITIAL_VALUE;
              for (int i = 0; i < size; i++) {
                  hash = (hash ^ entryHashes[i]) * HASHCODE_MULTIPLIER;
              }
              return hash;
          }
  
          public String toString() {
              StringBuilder sb = new StringBuilder();
              sb.append('{');
              for (int i = 0; i < keys.length; i++) {
                  if (status[i] == FILLED) {
                      if (sb.length() > 1) {
                          sb.append(", ");
                      }
                      sb.append(keys[i]);
                      sb.append('=');
                      sb.append(values[i]);
                  }
              }
              sb.append('}');
              return sb.toString();
          }
  
          private class EzIntIntHashMapIterator implements EzIntIntMapIterator {
              private int curIndex;
  
              private EzIntIntHashMapIterator() {
                  curIndex = 0;
                  while (curIndex < status.length && status[curIndex] != FILLED) {
                      curIndex++;
                  }
              }
  
              public boolean hasNext() {
                  return curIndex < status.length;
              }
  
              public int getKey() {
                  if (curIndex == keys.length) {
                      throw new NoSuchElementException("Iterator doesn't have more entries");
                  }
                  return keys[curIndex];
              }
  
              public int getValue() {
                  if (curIndex == values.length) {
                      throw new NoSuchElementException("Iterator doesn't have more entries");
                  }
                  return values[curIndex];
              }
  
              public void next() {
                  if (curIndex == status.length) {
                      return;
                  }
                  curIndex++;
                  while (curIndex < status.length && status[curIndex] != FILLED) {
                      curIndex++;
                  }
              }
  
          }
  
      }
  
      static interface EzIntIntMap {
          int size();
  
          EzIntIntMapIterator iterator();
  
          boolean equals(Object object);
  
          int hashCode();
  
          String toString();
  
      }
  
      static final class EzIntSort {
          private static final double HEAPSORT_DEPTH_COEFFICIENT = 2.0;
          private static final Random rnd = new Random(System.nanoTime());
  
          private EzIntSort() {
          }
  
          private static int maxQuickSortDepth(int length) {
              if (length <= 1) {
                  return 0;
              }
              int log = Integer.numberOfTrailingZeros(Integer.highestOneBit(length - 1)) + 1;
              return (int) (HEAPSORT_DEPTH_COEFFICIENT * log);
          }
  
          public static void sort(int[] a) {
              quickSort(a, 0, a.length, 0, maxQuickSortDepth(a.length));
          }
  
          private static void quickSort(int[] a, int left, int right, int depth, int maxDepth) {
              if (right - left <= 1) {
                  return;
              }
              if (depth > maxDepth) {
                  heapSort(a, left, right - left);
                  return;
              }
              final int pivot = a[left + rnd.nextInt(right - left)];
              int i = left;
              int j = right - 1;
              do {
                  while (a[i] < pivot) i++;
                  while (pivot < a[j]) j--;
                  if (i <= j) {
                      int tmp = a[i];
                      a[i++] = a[j];
                      a[j--] = tmp;
                  }
              } while (i <= j);
              quickSort(a, left, j + 1, depth + 1, maxDepth);
              quickSort(a, i, right, depth + 1, maxDepth);
          }
  
          private static void heapSort(int[] a, int offset, int size) {
              // If size <= 1, nothing is executed
              for (int i = (size >>> 1) - 1; i >= 0; i--) {
                  down(a, i, offset, size);
              }
              for (int i = size - 1; i > 0; i--) {
                  int tmp = a[offset];
                  a[offset] = a[offset + i];
                  a[offset + i] = tmp;
                  down(a, 0, offset, i);
              }
          }
  
          private static void down(int[] a, int index, int offset, int size) {
              final int element = a[offset + index];
              final int firstLeaf = (size >>> 1);
              while (index < firstLeaf) {
                  int largestChild = (index << 1) + 1;
                  if (largestChild + 1 < size && a[offset + largestChild + 1] > a[offset + largestChild]) {
                      largestChild++;
                  }
                  if (a[offset + largestChild] <= element) {
                      break;
                  }
                  a[offset + index] = a[offset + largestChild];
                  index = largestChild;
              }
              a[offset + index] = element;
          }
  
      }
  
      static class OutputWriter {
          private final PrintWriter writer;
  
          public OutputWriter(OutputStream outputStream) {
              writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream)));
          }
  
          public OutputWriter(Writer writer) {
              this.writer = new PrintWriter(writer);
          }
  
          public void close() {
              writer.close();
          }
  
          public void println(long i) {
              writer.println(i);
          }
  
      }
  
      static class InputReader {
          private InputStream stream;
          private byte[] buf = new byte[1024];
          private int curChar;
          private int numChars;
          private InputReader.SpaceCharFilter filter;
  
          public InputReader(InputStream stream) {
              this.stream = stream;
          }
  
          public int read() {
              if (numChars == -1) {
                  throw new InputMismatchException();
              }
              if (curChar >= numChars) {
                  curChar = 0;
                  try {
                      numChars = stream.read(buf);
                  } catch (IOException e) {
                      throw new InputMismatchException();
                  }
                  if (numChars <= 0) {
                      return -1;
                  }
              }
              return buf[curChar++];
          }
  
          public int nextInt() {
              int c = read();
              while (isSpaceChar(c)) {
                  c = read();
              }
              int sgn = 1;
              if (c == '-') {
                  sgn = -1;
                  c = read();
              }
              int res = 0;
              do {
                  if (c < '0' || c > '9') {
                      throw new InputMismatchException();
                  }
                  res *= 10;
                  res += c - '0';
                  c = read();
              } while (!isSpaceChar(c));
              return res * sgn;
          }
  
          public boolean isSpaceChar(int c) {
              if (filter != null) {
                  return filter.isSpaceChar(c);
              }
              return isWhitespace(c);
          }
  
          public static boolean isWhitespace(int c) {
              return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
          }
  
          public interface SpaceCharFilter {
              public boolean isSpaceChar(int ch);
  
          }
  
      }
  
      static final class PrimitiveHashCalculator {
          private PrimitiveHashCalculator() {
          }
  
          public static int getHash(int x) {
              return x;
          }
  
      }
  
      static class BinomialCoefficient {
          public static long nCr4(long N, long K) {
              if (K > N)
                  return 0;
              long res = 1;
              for (long i = 1; i <= K; ++i)
                  res = (N - K + i) * res / i;
              return res;
          }
  
      }
  
      static interface EzIntIntMapIterator {
          boolean hasNext();
  
          int getKey();
  
          int getValue();
  
          void next();
  
      }
  }
  
  