source: trip-planner-front/node_modules/hdr-histogram-js/assembly/PercentileIterator.ts@ e29cc2e

Last change on this file since e29cc2e was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 8.1 KB
RevLine 
[6a3a178]1/*
2 * This is a AssemblyScript port of the original Java version, which was written by
3 * Gil Tene as described in
4 * https://github.com/HdrHistogram/HdrHistogram
5 * and released to the public domain, as explained at
6 * http://creativecommons.org/publicdomain/zero/1.0/
7 */
8
9import Histogram from "./Histogram";
10import HistogramIterationValue from "./HistogramIterationValue";
11
12/**
13 * Used for iterating through histogram values according to percentile levels. The iteration is
14 * performed in steps that start at 0% and reduce their distance to 100% according to the
15 * <i>percentileTicksPerHalfDistance</i> parameter, ultimately reaching 100% when all recorded histogram
16 * values are exhausted.
17 */
18class PercentileIterator<T, U> {
19 percentileTicksPerHalfDistance: i32;
20 percentileLevelToIterateTo: f64;
21 percentileLevelToIterateFrom: f64;
22 reachedLastRecordedValue: boolean;
23 histogram: Histogram<T, U>;
24 savedHistogramTotalRawCount: u64;
25 currentIndex: i32;
26 currentValueAtIndex: u64;
27 nextValueAtIndex: u64;
28 prevValueIteratedTo: u64;
29 totalCountToPrevIndex: u64;
30 totalCountToCurrentIndex: u64;
31 totalValueToCurrentIndex: u64;
32 arrayTotalCount: u64;
33 countAtThisValue: u64;
34
35 private freshSubBucket: boolean;
36
37 currentIterationValue: HistogramIterationValue = new HistogramIterationValue();
38
39 /**
40 * @param histogram The histogram this iterator will operate on
41 * @param percentileTicksPerHalfDistance The number of equal-sized iteration steps per half-distance to 100%.
42 */
43 public constructor(
44 histogram: Histogram<T, U>,
45 percentileTicksPerHalfDistance: i32
46 ) {
47 this.percentileTicksPerHalfDistance = 0;
48 this.percentileLevelToIterateTo = 0;
49 this.percentileLevelToIterateFrom = 0;
50 this.reachedLastRecordedValue = false;
51 this.doReset(histogram, percentileTicksPerHalfDistance);
52 }
53
54 /**
55 * Reset iterator for re-use in a fresh iteration over the same histogram data set.
56 *
57 * @param percentileTicksPerHalfDistance The number of iteration steps per half-distance to 100%.
58 */
59 reset(percentileTicksPerHalfDistance: i32): void {
60 this.doReset(this.histogram, percentileTicksPerHalfDistance);
61 }
62
63 private doReset(
64 histogram: Histogram<T, U>,
65 percentileTicksPerHalfDistance: i32
66 ): void {
67 this.resetIterator(histogram);
68 this.percentileTicksPerHalfDistance = percentileTicksPerHalfDistance;
69 this.percentileLevelToIterateTo = 0;
70 this.percentileLevelToIterateFrom = 0;
71 this.reachedLastRecordedValue = false;
72 }
73
74 incrementIterationLevel(): void {
75 this.percentileLevelToIterateFrom = this.percentileLevelToIterateTo;
76
77 // The choice to maintain fixed-sized "ticks" in each half-distance to 100% [starting
78 // from 0%], as opposed to a "tick" size that varies with each interval, was made to
79 // make the steps easily comprehensible and readable to humans. The resulting percentile
80 // steps are much easier to browse through in a percentile distribution output, for example.
81 //
82 // We calculate the number of equal-sized "ticks" that the 0-100 range will be divided
83 // by at the current scale. The scale is detemined by the percentile level we are
84 // iterating to. The following math determines the tick size for the current scale,
85 // and maintain a fixed tick size for the remaining "half the distance to 100%"
86 // [from either 0% or from the previous half-distance]. When that half-distance is
87 // crossed, the scale changes and the tick size is effectively cut in half.
88
89 // percentileTicksPerHalfDistance = 5
90 // percentileReportingTicks = 10,
91
92 const percentileReportingTicks =
93 <f64>this.percentileTicksPerHalfDistance *
94 Math.pow(
95 2,
96 floor(
97 Math.log2(<f64>100 / (<f64>100 - this.percentileLevelToIterateTo))
98 ) + <f64>1
99 );
100
101 this.percentileLevelToIterateTo += <f64>100 / percentileReportingTicks;
102 }
103
104 reachedIterationLevel(): boolean {
105 if (this.countAtThisValue === 0) {
106 return false;
107 }
108 const currentPercentile =
109 (<f64>100 * <f64>this.totalCountToCurrentIndex) /
110 <f64>this.arrayTotalCount;
111 return currentPercentile >= this.percentileLevelToIterateTo;
112 }
113
114 resetIterator(histogram: Histogram<T, U>): void {
115 this.histogram = histogram;
116 this.savedHistogramTotalRawCount = histogram.totalCount;
117 this.arrayTotalCount = histogram.totalCount;
118 this.currentIndex = 0;
119 this.currentValueAtIndex = 0;
120 this.nextValueAtIndex = 1 << histogram.unitMagnitude;
121 this.prevValueIteratedTo = 0;
122 this.totalCountToPrevIndex = 0;
123 this.totalCountToCurrentIndex = 0;
124 this.totalValueToCurrentIndex = 0;
125 this.countAtThisValue = 0;
126 this.freshSubBucket = true;
127 this.currentIterationValue.reset();
128 }
129
130 /**
131 * Returns true if the iteration has more elements. (In other words, returns true if next would return an
132 * element rather than throwing an exception.)
133 *
134 * @return true if the iterator has more elements.
135 */
136 public hasNext(): boolean {
137 if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
138 throw "Concurrent Modification Exception";
139 }
140 if (this.totalCountToCurrentIndex < this.arrayTotalCount) {
141 return true;
142 }
143 if (!this.reachedLastRecordedValue && this.arrayTotalCount > 0) {
144 this.percentileLevelToIterateTo = 100;
145 this.reachedLastRecordedValue = true;
146 return true;
147 }
148 return false;
149 }
150
151 /**
152 * Returns the next element in the iteration.
153 *
154 * @return the {@link HistogramIterationValue} associated with the next element in the iteration.
155 */
156 public next(): HistogramIterationValue {
157 // Move through the sub buckets and buckets until we hit the next reporting level:
158 while (!this.exhaustedSubBuckets()) {
159 this.countAtThisValue = this.histogram.getCountAtIndex(this.currentIndex);
160 if (this.freshSubBucket) {
161 // Don't add unless we've incremented since last bucket...
162 this.totalCountToCurrentIndex += this.countAtThisValue;
163 this.totalValueToCurrentIndex +=
164 this.countAtThisValue *
165 this.histogram.highestEquivalentValue(this.currentValueAtIndex);
166 this.freshSubBucket = false;
167 }
168 if (this.reachedIterationLevel()) {
169 const valueIteratedTo = this.getValueIteratedTo();
170
171 //Object.assign(this.currentIterationValue, {
172 this.currentIterationValue.valueIteratedTo = valueIteratedTo;
173 this.currentIterationValue.valueIteratedFrom = this.prevValueIteratedTo;
174 this.currentIterationValue.countAtValueIteratedTo = this.countAtThisValue;
175 this.currentIterationValue.countAddedInThisIterationStep =
176 this.totalCountToCurrentIndex - this.totalCountToPrevIndex;
177 this.currentIterationValue.totalCountToThisValue = this.totalCountToCurrentIndex;
178 this.currentIterationValue.totalValueToThisValue = this.totalValueToCurrentIndex;
179 this.currentIterationValue.percentile =
180 (<f64>100 * <f64>this.totalCountToCurrentIndex) /
181 <f64>this.arrayTotalCount;
182 this.currentIterationValue.percentileLevelIteratedTo = this.getPercentileIteratedTo();
183 this.prevValueIteratedTo = valueIteratedTo;
184 this.totalCountToPrevIndex = this.totalCountToCurrentIndex;
185 this.incrementIterationLevel();
186 if (this.histogram.totalCount !== this.savedHistogramTotalRawCount) {
187 throw new Error("Concurrent Modification Exception");
188 }
189 return this.currentIterationValue;
190 }
191 this.incrementSubBucket();
192 }
193 throw new Error("Index Out Of Bounds Exception");
194 }
195
196 getPercentileIteratedTo(): f64 {
197 return this.percentileLevelToIterateTo;
198 }
199
200 getPercentileIteratedFrom(): f64 {
201 return this.percentileLevelToIterateFrom;
202 }
203
204 getValueIteratedTo(): u64 {
205 return this.histogram.highestEquivalentValue(this.currentValueAtIndex);
206 }
207
208 private exhaustedSubBuckets(): boolean {
209 return this.currentIndex >= this.histogram.countsArrayLength;
210 }
211
212 incrementSubBucket(): void {
213 this.freshSubBucket = true;
214 this.currentIndex++;
215 this.currentValueAtIndex = this.histogram.valueFromIndex(this.currentIndex);
216 this.nextValueAtIndex = this.histogram.valueFromIndex(
217 this.currentIndex + 1
218 );
219 }
220}
221
222export default PercentileIterator;
Note: See TracBrowser for help on using the repository browser.