2727import static org .elasticsearch .exponentialhistogram .ExponentialHistogram .MAX_SCALE ;
2828import static org .elasticsearch .exponentialhistogram .ExponentialHistogram .MIN_INDEX ;
2929import static org .elasticsearch .exponentialhistogram .ExponentialHistogram .MIN_SCALE ;
30+ import static org .elasticsearch .exponentialhistogram .ExponentialScaleUtils .adjustScale ;
3031import static org .elasticsearch .exponentialhistogram .ExponentialScaleUtils .compareExponentiallyScaledValues ;
3132import static org .elasticsearch .exponentialhistogram .ExponentialScaleUtils .computeIndex ;
3233import static org .elasticsearch .exponentialhistogram .ExponentialScaleUtils .exponentiallyScaledToDoubleValue ;
34+ import static org .elasticsearch .exponentialhistogram .ExponentialScaleUtils .normalizeScale ;
3335
3436/**
3537 * Represents the bucket for values around zero in an exponential histogram.
@@ -62,13 +64,7 @@ public final class ZeroBucket {
6264 // A singleton for an empty zero bucket with the smallest possible threshold.
6365 private static final ZeroBucket MINIMAL_EMPTY = new ZeroBucket (MIN_INDEX , MIN_SCALE , 0 );
6466
65- /**
66- * Creates a new zero bucket with a specific threshold and count.
67- *
68- * @param zeroThreshold The threshold defining the bucket's range [-zeroThreshold, +zeroThreshold].
69- * @param count The number of values in the bucket.
70- */
71- public ZeroBucket (double zeroThreshold , long count ) {
67+ private ZeroBucket (double zeroThreshold , long count ) {
7268 assert zeroThreshold >= 0.0 : "zeroThreshold must not be negative" ;
7369 this .index = Long .MAX_VALUE ; // compute lazily when needed
7470 this .scale = MAX_SCALE ;
@@ -85,11 +81,11 @@ private ZeroBucket(long index, int scale, long count) {
8581 this .count = count ;
8682 }
8783
88- private ZeroBucket (double realThreshold , long index , int scale , long count ) {
89- this .realThreshold = realThreshold ;
90- this .index = index ;
91- this .scale = scale ;
92- this .count = count ;
84+ private ZeroBucket (ZeroBucket toCopy , long newCount ) {
85+ this .realThreshold = toCopy . realThreshold ;
86+ this .index = toCopy . index ;
87+ this .scale = toCopy . scale ;
88+ this .count = newCount ;
9389 }
9490
9591 /**
@@ -109,8 +105,37 @@ public static ZeroBucket minimalWithCount(long count) {
109105 if (count == 0 ) {
110106 return MINIMAL_EMPTY ;
111107 } else {
112- return new ZeroBucket (MINIMAL_EMPTY .zeroThreshold (), MINIMAL_EMPTY .index (), MINIMAL_EMPTY .scale (), count );
108+ return new ZeroBucket (MINIMAL_EMPTY , count );
109+ }
110+ }
111+
112+ /**
113+ * Creates a zero bucket from the given threshold represented as double.
114+ *
115+ * @param zeroThreshold the zero threshold defining the bucket range [-zeroThreshold, +zeroThreshold], must be non-negative
116+ * @param count the number of values in the bucket
117+ * @return the new {@link ZeroBucket}
118+ */
119+ public static ZeroBucket create (double zeroThreshold , long count ) {
120+ if (zeroThreshold == 0 ) {
121+ return minimalWithCount (count );
113122 }
123+ return new ZeroBucket (zeroThreshold , count );
124+ }
125+
126+ /**
127+ * Creates a zero bucket from the given threshold represented as exponentially scaled number.
128+ *
129+ * @param index the index of the exponentially scaled number defining the zero threshold
130+ * @param scale the corresponding scale for the index
131+ * @param count the number of values in the bucket
132+ * @return the new {@link ZeroBucket}
133+ */
134+ public static ZeroBucket create (long index , int scale , long count ) {
135+ if (index == MINIMAL_EMPTY .index && scale == MINIMAL_EMPTY .scale ) {
136+ return minimalWithCount (count );
137+ }
138+ return new ZeroBucket (index , scale , count );
114139 }
115140
116141 /**
@@ -158,9 +183,9 @@ public ZeroBucket merge(ZeroBucket other) {
158183 long totalCount = count + other .count ;
159184 // Both are populated, so we need to use the higher zero-threshold.
160185 if (this .compareZeroThreshold (other ) >= 0 ) {
161- return new ZeroBucket (realThreshold , index , scale , totalCount );
186+ return new ZeroBucket (this , totalCount );
162187 } else {
163- return new ZeroBucket (other . realThreshold , other . index , other . scale , totalCount );
188+ return new ZeroBucket (other , totalCount );
164189 }
165190 }
166191 }
@@ -219,10 +244,33 @@ public ZeroBucket collapseOverlappingBuckets(BucketIterator buckets) {
219244 long collapsedUpperBoundIndex = highestCollapsedIndex + 1 ;
220245 if (compareExponentiallyScaledValues (index (), scale (), collapsedUpperBoundIndex , buckets .scale ()) >= 0 ) {
221246 // Our current zero-threshold is larger than the upper boundary of the largest collapsed bucket, so we keep it.
222- return new ZeroBucket (realThreshold , index , scale , newZeroCount );
247+ return new ZeroBucket (this , newZeroCount );
223248 } else {
224249 return new ZeroBucket (collapsedUpperBoundIndex , buckets .scale (), newZeroCount );
225250 }
226251 }
227252 }
253+
254+ @ Override
255+ public boolean equals (Object o ) {
256+ if (o == null || getClass () != o .getClass ()) return false ;
257+ ZeroBucket that = (ZeroBucket ) o ;
258+ if (count () != that .count ()) return false ;
259+ if (Double .compare (zeroThreshold (), that .zeroThreshold ()) != 0 ) return false ;
260+ if (compareExponentiallyScaledValues (index (), scale (), that .index (), that .scale ()) != 0 ) return false ;
261+ return true ;
262+ }
263+
264+ @ Override
265+ public int hashCode () {
266+ int normalizedScale = normalizeScale (index (), scale );
267+ int scaleAdjustment = normalizedScale - scale ;
268+ long normalizedIndex = adjustScale (index (), scale , scaleAdjustment );
269+
270+ int result = normalizedScale ;
271+ result = 31 * result + Long .hashCode (normalizedIndex );
272+ result = 31 * result + Double .hashCode (zeroThreshold ());
273+ result = 31 * result + Long .hashCode (count );
274+ return result ;
275+ }
228276}
0 commit comments