private void radixSort(int from, int to, int k) {
    int[] histogram = histograms[k];
    if (histogram == null) {
      histogram = histograms[k] = new int[HISTOGRAM_SIZE];
    } else {
      Arrays.fill(histogram, 0);
    }

    buildHistogram(from, to, k, histogram);

    // short-circuit: if all keys have the same byte at offset k, then recurse directly
    for (int i = 0; i < HISTOGRAM_SIZE; ++i) {
      if (histogram[i] == to - from) {
        // everything is in the same bucket, recurse
        if (i > 0) {
          sort(from, to, k + 1);
        }
        return;
      } else if (histogram[i] != 0) {
        break;
      }
    }

    int[] startOffsets = histogram;
    int[] endOffsets = this.endOffsets;
    sumHistogram(histogram, endOffsets);
    reorder(from, to, startOffsets, endOffsets, k);
    endOffsets = startOffsets;

    if (k + 1 < maxLength) {
      // recurse on all but the first bucket since all keys are equals in this
      // bucket (we already compared all bytes)
      for (int prev = endOffsets[0], i = 1; i < HISTOGRAM_SIZE; ++i) {
        int h = endOffsets[i];
        final int bucketLen = h - prev;
        if (bucketLen > 1) {
          sort(from + prev, from + h, k + 1);
        }
        prev = h;
      }
    }
  }