docs: @SuppressWarnings("unchecked")
    private static <T> double[] internalBuildHistogram(final T[] splitPoints, final ItemsSketch<T> sketch) {
      final Object[] samples  = sketch.getCombinedBuffer();
      final int bbCount = sketch.getBaseBufferCount();
      ItemsUtil.validateValues(splitPoints, sketch.getComparator());
  
      final int numSplitPoints = splitPoints.length;
      final int numCounters = numSplitPoints + 1;
      final double[] counters = new double[numCounters];
  
      long weight = 1;
      if (numSplitPoints < 50) { // empirically determined crossover
        // sort not worth it when few split points
        ItemsPmfCdfImpl.bilinearTimeIncrementHistogramCounters(
            (T[]) samples, 0, bbCount, weight, splitPoints, counters, sketch.getComparator());
      } else {
        // sort is worth it when many split points
        Arrays.sort((T[]) samples, 0, bbCount, sketch.getComparator());
        linearTimeIncrementHistogramCounters(
            (T[]) samples, 0, bbCount, weight, splitPoints, counters, sketch.getComparator()
        );
      }
  
      long myBitPattern = sketch.getBitPattern();
      final int k = sketch.getK();
      assert myBitPattern == (sketch.getN() / (2L * k)); // internal consistency check
      for (int lvl = 0; myBitPattern != 0L; lvl++, myBitPattern >>>= 1) {
        weight <<= 1; // double the weight
        if ((myBitPattern & 1L) > 0L) { //valid level exists
          // the levels are already sorted so we can use the fast version
          linearTimeIncrementHistogramCounters(
              (T[]) samples, (2 + lvl) * k, k, weight, splitPoints, counters, sketch.getComparator());
        }
      }
      return counters;
    }