@Override
 protected void doWriteTo(StreamOutput out) throws IOException {
   ValueFormatterStreams.writeOptional(valueFormatter, out);
   out.writeInt(keys.length);
   for (int i = 0; i < keys.length; ++i) {
     out.writeDouble(keys[i]);
   }
   out.writeLong(state.getHighestToLowestValueRatio());
   ByteBuffer stateBuffer = ByteBuffer.allocate(state.getNeededByteBufferCapacity());
   state.encodeIntoCompressedByteBuffer(stateBuffer);
   out.writeByteArray(stateBuffer.array());
   out.writeBoolean(keyed);
 }
 @Override
 public AbstractInternalHDRPercentiles doReduce(
     List<InternalAggregation> aggregations, ReduceContext reduceContext) {
   DoubleHistogram merged = null;
   for (InternalAggregation aggregation : aggregations) {
     final AbstractInternalHDRPercentiles percentiles =
         (AbstractInternalHDRPercentiles) aggregation;
     if (merged == null) {
       merged = new DoubleHistogram(percentiles.state);
       merged.setAutoResize(true);
     }
     merged.add(percentiles.state);
   }
   return createReduced(getName(), keys, merged, keyed, pipelineAggregators(), getMetaData());
 }
 @Override
 protected void doReadFrom(StreamInput in) throws IOException {
   valueFormatter = ValueFormatterStreams.readOptional(in);
   keys = new double[in.readInt()];
   for (int i = 0; i < keys.length; ++i) {
     keys[i] = in.readDouble();
   }
   long minBarForHighestToLowestValueRatio = in.readLong();
   ByteBuffer stateBuffer = ByteBuffer.wrap(in.readByteArray());
   try {
     state =
         DoubleHistogram.decodeFromCompressedByteBuffer(
             stateBuffer, minBarForHighestToLowestValueRatio);
   } catch (DataFormatException e) {
     throw new IOException("Failed to decode DoubleHistogram for aggregation [" + name + "]", e);
   }
   keyed = in.readBoolean();
 }
 public long getEstimatedMemoryFootprint() {
   return state.getEstimatedFootprintInBytes();
 }