@Override
  public InternalAggregation buildAggregation(long owningBucketOrdinal) {
    assert owningBucketOrdinal == 0;
    List<InternalHistogram.Bucket> buckets =
        new ArrayList<InternalHistogram.Bucket>((int) bucketOrds.size());
    for (long i = 0; i < bucketOrds.capacity(); ++i) {
      final long ord = bucketOrds.id(i);
      if (ord < 0) {
        continue; // slot is not allocated
      }
      buckets.add(
          histogramFactory.createBucket(
              rounding.valueForKey(bucketOrds.key(i)),
              bucketDocCount(ord),
              bucketAggregations(ord),
              valuesSource.formatter()));
    }

    CollectionUtil.introSort(buckets, order.comparator());

    // value source will be null for unmapped fields
    ValueFormatter formatter = valuesSource != null ? valuesSource.formatter() : null;
    InternalHistogram.EmptyBucketInfo emptyBucketInfo =
        minDocCount == 0
            ? new InternalHistogram.EmptyBucketInfo(rounding, buildEmptySubAggregations())
            : null;
    return histogramFactory.create(
        name, buckets, order, minDocCount, emptyBucketInfo, formatter, keyed);
  }
 @Override
 public InternalAggregation buildEmptyAggregation() {
   ValueFormatter formatter = valuesSource != null ? valuesSource.formatter() : null;
   InternalHistogram.EmptyBucketInfo emptyBucketInfo =
       minDocCount == 0
           ? new InternalHistogram.EmptyBucketInfo(rounding, buildEmptySubAggregations())
           : null;
   return histogramFactory.create(
       name, Collections.emptyList(), order, minDocCount, emptyBucketInfo, formatter, keyed);
 }
  @Override
  public void collect(int doc, long owningBucketOrdinal) throws IOException {
    assert valuesSource != null : "collect must only be called if #shouldCollect returns true";

    DoubleValues values = valuesSource.doubleValues();
    if (values == null || values.setDocument(doc) == 0) {
      return;
    }

    if (owningBucketOrdinal >= mins.size()) {
      long from = mins.size();
      mins = BigArrays.grow(mins, owningBucketOrdinal + 1);
      mins.fill(from, mins.size(), Double.POSITIVE_INFINITY);
    }

    mins.set(owningBucketOrdinal, Math.min(values.nextValue(), mins.get(owningBucketOrdinal)));
  }
  @Override
  public void collect(int doc, long owningBucketOrdinal) throws IOException {
    assert owningBucketOrdinal == 0;
    final LongValues values = valuesSource.longValues();
    final int valuesCount = values.setDocument(doc);

    long previousKey = Long.MIN_VALUE;
    for (int i = 0; i < valuesCount; ++i) {
      long value = values.nextValue();
      long key = rounding.roundKey(value);
      assert key >= previousKey;
      if (key == previousKey) {
        continue;
      }
      long bucketOrd = bucketOrds.add(key);
      if (bucketOrd < 0) { // already seen
        bucketOrd = -1 - bucketOrd;
      }
      collectBucket(doc, bucketOrd);
      previousKey = key;
    }
  }
 @Override
 public void setNextReader(AtomicReaderContext reader) {
   values = valuesSource.longValues();
 }