class StatisticsTracker {
  public void addNumber(int number) {
    q.offer(number);
    count.merge(number, 1, Integer::sum);
    sortedList.merge(number, 1, Integer::sum);
    modeMaxHeap.offer(new Pair<>(count.get(number), number));
    sum += number;
  }
  public void removeFirstAddedNumber() {
    final int number = q.poll();
    count.merge(number, -1, Integer::sum);
    if (count.get(number) == 0)
      count.remove(number);
    sortedList.merge(number, -1, Integer::sum);
    if (sortedList.get(number) == 0)
      sortedList.remove(number);
    sum -= number;
  }
  public int getMean() {
    return (int) (sum / q.size());
  }
  public int getMedian() {
    final int midIndex = q.size() / 2; // Median depends on the queue size.
    int count = 0;
    for (Map.Entry<Integer, Integer> entry : sortedList.entrySet()) {
      count += entry.getValue();
      if (count > midIndex)
        return entry.getKey();
    }
    throw new IllegalArgumentException();
  }
  public int getMode() {
    // Removes stale entries from the top of the heap.
    while (!modeMaxHeap.isEmpty()) {
      final int frequency = modeMaxHeap.peek().getKey();
      final int number = modeMaxHeap.peek().getValue();
      if (count.containsKey(number) && count.get(number) == frequency)
        return number;
      modeMaxHeap.poll();
    }
    throw new IllegalArgumentException();
  }
  private Queue<Integer> q = new LinkedList<>();
  private Map<Integer, Integer> count = new HashMap<>();
  private TreeMap<Integer, Integer> sortedList = new TreeMap<>();
  // (frequency, number)
  private PriorityQueue<Pair<Integer, Integer>> modeMaxHeap =
      new PriorityQueue<>(Comparator.comparingInt(Pair<Integer, Integer>::getKey)
                              .reversed()
                              .thenComparingInt(Pair<Integer, Integer>::getValue));
  private long sum = 0;
}