2727import java .util .List ;
2828import java .util .OptionalLong ;
2929
30+ import static org .elasticsearch .exponentialhistogram .ExponentialHistogramUtils .bucketIteratorHash ;
31+ import static org .elasticsearch .exponentialhistogram .ExponentialHistogramUtils .bucketIteratorsEqual ;
32+
3033/**
31- * Interface for implementations of exponential histograms adhering to the
34+ * Base class for implementations of exponential histograms adhering to the
3235 * <a href="https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponentialhistogram">OpenTelemetry definition</a>.
33- * This interface supports sparse implementations, allowing iteration over buckets without requiring direct index access.<br>
36+ * This class supports sparse implementations, allowing iteration over buckets without requiring direct index access.<br>
3437 * The most important properties are:
3538 * <ul>
3639 * <li>The histogram has a scale parameter, which defines the accuracy. A higher scale implies a higher accuracy.
4548 * Additionally, all algorithms assume that samples within a bucket are located at a single point: the point of least relative error
4649 * (see {@link ExponentialScaleUtils#getPointOfLeastRelativeError(long, int)}).
4750 */
48- public interface ExponentialHistogram extends Accountable {
51+ public abstract class ExponentialHistogram implements Accountable {
4952
5053 // TODO(b/128622): support min/max storage and merging.
5154 // TODO(b/128622): Add special positive and negative infinity buckets
@@ -56,42 +59,42 @@ public interface ExponentialHistogram extends Accountable {
5659 // Theoretically, a MAX_SCALE of 51 would work and would still cover the entire range of double values.
5760 // For that to work, the math for converting from double to indices and back would need to be reworked.
5861 // One option would be to use "Quadruple": https://github.com/m-vokhm/Quadruple
59- int MAX_SCALE = 38 ;
62+ public static final int MAX_SCALE = 38 ;
6063
6164 // At this scale, all double values fall into a single bucket.
62- int MIN_SCALE = -11 ;
65+ public static final int MIN_SCALE = -11 ;
6366
6467 // Only use 62 bits (plus the sign bit) at max to allow computing the difference between the smallest and largest index without causing
6568 // an overflow.
6669 // The extra bit also provides room for compact storage tricks.
67- int MAX_INDEX_BITS = 62 ;
68- long MAX_INDEX = (1L << MAX_INDEX_BITS ) - 1 ;
69- long MIN_INDEX = -MAX_INDEX ;
70+ public static final int MAX_INDEX_BITS = 62 ;
71+ public static final long MAX_INDEX = (1L << MAX_INDEX_BITS ) - 1 ;
72+ public static final long MIN_INDEX = -MAX_INDEX ;
7073
7174 /**
7275 * The scale of the histogram. Higher scales result in higher accuracy but potentially more buckets.
7376 * Must be less than or equal to {@link #MAX_SCALE} and greater than or equal to {@link #MIN_SCALE}.
7477 *
7578 * @return the scale of the histogram
7679 */
77- int scale ();
80+ public abstract int scale ();
7881
7982 /**
8083 * @return the {@link ZeroBucket} representing the number of zero (or close-to-zero) values and its threshold
8184 */
82- ZeroBucket zeroBucket ();
85+ public abstract ZeroBucket zeroBucket ();
8386
8487 /**
8588 * @return a {@link Buckets} instance for the populated buckets covering the positive value range of this histogram.
8689 * The {@link BucketIterator#scale()} of iterators obtained via {@link Buckets#iterator()} must be the same as {@link #scale()}.
8790 */
88- Buckets positiveBuckets ();
91+ public abstract Buckets positiveBuckets ();
8992
9093 /**
9194 * @return a {@link Buckets} instance for the populated buckets covering the negative value range of this histogram.
9295 * The {@link BucketIterator#scale()} of iterators obtained via {@link Buckets#iterator()} must be the same as {@link #scale()}.
9396 */
94- Buckets negativeBuckets ();
97+ public abstract Buckets negativeBuckets ();
9598
9699 /**
97100 * Returns the sum of all values represented by this histogram.
@@ -100,12 +103,12 @@ public interface ExponentialHistogram extends Accountable {
100103 *
101104 * @return the sum, guaranteed to be zero for empty histograms
102105 */
103- double sum ();
106+ public abstract double sum ();
104107
105108 /**
106109 * Represents a bucket range of an {@link ExponentialHistogram}, either the positive or the negative range.
107110 */
108- interface Buckets {
111+ public interface Buckets {
109112
110113 /**
111114 * @return a {@link BucketIterator} for the populated buckets of this bucket range.
@@ -125,60 +128,37 @@ interface Buckets {
125128
126129 }
127130
128- static ExponentialHistogram empty () {
129- return EmptyExponentialHistogram .INSTANCE ;
130- }
131+ @ Override
132+ public boolean equals (Object other ) {
133+ if (this == other ) return true ;
134+ if (other == null ) return false ;
131135
132- static boolean equals (ExponentialHistogram a , ExponentialHistogram b ) {
133- if (a == b ) {
134- return true ;
135- }
136- if (a == null || b == null ) {
137- return false ;
138- }
139- if (a .scale () != b .scale ()) {
140- return false ;
141- }
142- if (a .zeroBucket ().count () != b .zeroBucket ().count ()) {
136+ if ((other instanceof ExponentialHistogram ) == false ) {
143137 return false ;
144138 }
145- if (Double .compare (a .zeroBucket ().zeroThreshold (), b .zeroBucket ().zeroThreshold ()) != 0 ) {
146- return false ;
147- }
148- if (a .positiveBuckets ().valueCount () != b .positiveBuckets ().valueCount ()) {
149- return false ;
150- }
151- if (a .negativeBuckets ().valueCount () != b .negativeBuckets ().valueCount ()) {
152- return false ;
153- }
154- BucketIterator itA = a .positiveBuckets ().iterator ();
155- BucketIterator itB = b .positiveBuckets ().iterator ();
156- while (itA .hasNext () && itB .hasNext ()) {
157- if (itA .peekIndex () != itB .peekIndex ()) {
158- return false ;
159- }
160- if (itA .peekCount () != itB .peekCount ()) {
161- return false ;
162- }
163- itA .advance ();
164- itB .advance ();
165- }
166- if (itA .hasNext () || itB .hasNext ()) {
167- return false ;
168- }
169- itA = a .negativeBuckets ().iterator ();
170- itB = b .negativeBuckets ().iterator ();
171- while (itA .hasNext () && itB .hasNext ()) {
172- if (itA .peekIndex () != itB .peekIndex ()) {
173- return false ;
174- }
175- if (itA .peekCount () != itB .peekCount ()) {
176- return false ;
177- }
178- itA .advance ();
179- itB .advance ();
180- }
181- return itA .hasNext () == false && itB .hasNext () == false ;
139+ ExponentialHistogram that = (ExponentialHistogram ) other ;
140+
141+ if (scale () != that .scale ()) return false ;
142+ if (sum () != that .sum ()) return false ;
143+ if (zeroBucket ().equals (that .zeroBucket ()) == false ) return false ;
144+ if (bucketIteratorsEqual (negativeBuckets ().iterator (), that .negativeBuckets ().iterator ()) == false ) return false ;
145+ if (bucketIteratorsEqual (positiveBuckets ().iterator (), that .positiveBuckets ().iterator ()) == false ) return false ;
146+
147+ return true ;
148+ }
149+
150+ @ Override
151+ public int hashCode () {
152+ int hash = scale ();
153+ hash = 31 * hash + Double .hashCode (sum ());
154+ hash = 31 * hash + zeroBucket ().hashCode ();
155+ hash = 31 * hash + bucketIteratorHash (negativeBuckets ().iterator ());
156+ hash = 31 * hash + bucketIteratorHash (positiveBuckets ().iterator ());
157+ return hash ;
158+ }
159+
160+ public static ExponentialHistogram empty () {
161+ return EmptyExponentialHistogram .INSTANCE ;
182162 }
183163
184164 /**
@@ -191,7 +171,7 @@ static boolean equals(ExponentialHistogram a, ExponentialHistogram b) {
191171 * @param values the values to be added to the histogram
192172 * @return a new {@link ReleasableExponentialHistogram}
193173 */
194- static ReleasableExponentialHistogram create (int maxBucketCount , ExponentialHistogramCircuitBreaker breaker , double ... values ) {
174+ public static ReleasableExponentialHistogram create (int maxBucketCount , ExponentialHistogramCircuitBreaker breaker , double ... values ) {
195175 try (ExponentialHistogramGenerator generator = ExponentialHistogramGenerator .create (maxBucketCount , breaker )) {
196176 for (double val : values ) {
197177 generator .add (val );
@@ -208,7 +188,7 @@ static ReleasableExponentialHistogram create(int maxBucketCount, ExponentialHist
208188 * @param histograms the histograms to merge
209189 * @return the merged histogram
210190 */
211- static ReleasableExponentialHistogram merge (
191+ public static ReleasableExponentialHistogram merge (
212192 int maxBucketCount ,
213193 ExponentialHistogramCircuitBreaker breaker ,
214194 Iterator <ExponentialHistogram > histograms
@@ -229,7 +209,7 @@ static ReleasableExponentialHistogram merge(
229209 * @param histograms the histograms to merge
230210 * @return the merged histogram
231211 */
232- static ReleasableExponentialHistogram merge (
212+ public static ReleasableExponentialHistogram merge (
233213 int maxBucketCount ,
234214 ExponentialHistogramCircuitBreaker breaker ,
235215 ExponentialHistogram ... histograms
0 commit comments