1e9f970f82798d19eef61498663dbd27e56ede7f
[aai/sparky-be.git] / src / main / java / org / onap / aai / sparky / analytics / HistogramSampler.java
1 /**
2  * ============LICENSE_START=======================================================
3  * org.onap.aai
4  * ================================================================================
5  * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright © 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *       http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  *
21  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22  */
23 package org.onap.aai.sparky.analytics;
24
25 /**
26  * A class that models a histogram for reporting and tracking long values with variable steps, bins,
27  * and floating point accuracy.
28  * 
29  * @author davea.
30  */
31 public final class HistogramSampler {
32
33   private String label;
34
35   private long binMaxValue;
36
37   private int numBins;
38
39   private double stepSize;
40
41   private long sampleValueTotal;
42
43   private long minValue = -1;
44
45   private long maxValue = 0;
46
47   private long numSamples = 0;
48
49   private long decimalPointAccuracy = 0;
50
51   private static String FORMAT_FLOAT_TEMPLATE = "%%.%df";
52
53   private String floatFormatStr;
54
55   private long[] histogramBins;
56
57   /**
58    * Instantiates a new histogram sampler.
59    *
60    * @param label the label
61    * @param maxValue the max value
62    * @param numBins the num bins
63    * @param decimalPointAccuracy the decimal point accuracy
64    */
65   public HistogramSampler(String label, long maxValue, int numBins, int decimalPointAccuracy) {
66     this.label = label;
67     this.binMaxValue = maxValue;
68     this.numBins = numBins;
69     this.stepSize = ((double) binMaxValue / (double) numBins);
70     this.decimalPointAccuracy = decimalPointAccuracy;
71     this.floatFormatStr = String.format(FORMAT_FLOAT_TEMPLATE, this.decimalPointAccuracy);
72
73     /*
74      * [numBins + 1] => last bin is catch-all for outliers
75      */
76
77     initializeHistogramBins(numBins + 1);
78
79   }
80
81   /**
82    * Initialize histogram bins.
83    *
84    * @param numBins the num bins
85    */
86   private void initializeHistogramBins(int numBins) {
87
88     histogramBins = new long[numBins];
89     int counter = 0;
90     while (counter < numBins) {
91       histogramBins[counter] = 0;
92       counter++;
93     }
94
95   }
96
97   /*
98    * Is it really necessary to synchronize the collection, or should we simply switch the underlying
99    * data type to an AtomicLong
100    */
101
102   /**
103    * Track.
104    *
105    * @param value the value
106    */
107   public synchronized void track(long value) {
108
109     if (value < 0) {
110       return;
111     }
112
113     sampleValueTotal += value;
114     numSamples++;
115
116     if (minValue == -1) {
117       minValue = value;
118     }
119
120     if (value < minValue) {
121       minValue = value;
122     }
123
124     if (value > maxValue) {
125       maxValue = value;
126     }
127
128     /*
129      * One step bin determination
130      */
131
132     if (value < (numBins * stepSize)) {
133
134       int index = (int) (value / stepSize);
135       histogramBins[index]++;
136
137     } else {
138       // peg the metric in the outlier bin
139       histogramBins[numBins - 1]++;
140     }
141
142   }
143
144   /**
145    * Clear.
146    */
147   public void clear() {
148
149     int counter = 0;
150     while (counter < numBins) {
151       histogramBins[counter] = 0;
152       counter++;
153     }
154
155     minValue = -1;
156     maxValue = 0;
157     numSamples = 0;
158     sampleValueTotal = 0;
159
160   }
161
162   /**
163    * Re initialize bins.
164    *
165    * @param label the label
166    * @param numBins the num bins
167    * @param maxValue the max value
168    * @param decimalPointAccuracy the decimal point accuracy
169    */
170   public void reInitializeBins(String label, int numBins, long maxValue, int decimalPointAccuracy) {
171     this.label = label;
172     this.decimalPointAccuracy = decimalPointAccuracy;
173     this.floatFormatStr = String.format(FORMAT_FLOAT_TEMPLATE, this.decimalPointAccuracy);
174     this.numBins = numBins;
175     this.minValue = -1;
176     this.maxValue = 0;
177     initializeHistogramBins(numBins);
178     this.stepSize = (maxValue / numBins);
179     clear();
180   }
181
182   public long getNumberOfSamples() {
183     return numSamples;
184   }
185
186   public long getTotalValueSum() {
187     return sampleValueTotal;
188   }
189
190   /**
191    * Gets the stats.
192    *
193    * @param formatted the formatted
194    * @param indentPadding the indent padding
195    * @return the stats
196    */
197   public String getStats(boolean formatted, String indentPadding) {
198
199     StringBuilder sb = new StringBuilder(128);
200
201
202     if (!formatted) {
203       // generate CSV in the following format
204
205       /*
206        * label,minValue,maxValue,avgValue,numSamples,stepSize,numSteps,stepCounters
207        */
208       sb.append(indentPadding);
209       sb.append(label).append(",");
210       sb.append(minValue).append(",");
211       sb.append(maxValue).append(",");
212       if (numSamples == 0) {
213         sb.append(0).append(",");
214       } else {
215         sb.append((sampleValueTotal / numSamples)).append(",");
216       }
217       sb.append(numSamples).append(",");
218       sb.append(numBins).append(",");
219       sb.append(String.format(floatFormatStr, stepSize));
220
221       int counter = 0;
222       while (counter < numBins) {
223
224         if (counter != (numBins)) {
225           sb.append(",");
226         }
227
228         sb.append(histogramBins[counter]);
229
230         counter++;
231
232       }
233
234       return sb.toString();
235
236     }
237
238     sb.append("\n");
239     sb.append(indentPadding).append("Label = ").append(label).append("\n");
240     sb.append(indentPadding).append("Min = ").append(minValue).append("\n");
241     sb.append(indentPadding).append("Max = ").append(maxValue).append("\n");
242     sb.append(indentPadding).append("numSamples = ").append(numSamples).append("\n");
243
244     if (numSamples == 0) {
245       sb.append(indentPadding).append("Avg = ").append(0).append("\n");
246     } else {
247       sb.append(indentPadding).append("Avg = ").append((sampleValueTotal / numSamples))
248           .append("\n");
249     }
250
251     sb.append(indentPadding).append("StepSize = ").append(String.format(floatFormatStr, stepSize))
252         .append("\n");
253
254     sb.append(indentPadding).append("Sample Histogram:").append("\n");
255
256     int counter = 0;
257     while (counter < numBins) {
258
259       if (counter == (numBins - 1)) {
260         // outlier bin
261         double leftBound = (stepSize * counter);
262         sb.append(indentPadding).append("\t")
263             .append(" x >= " + String.format(floatFormatStr, leftBound) + " : " 
264                 + histogramBins[counter])
265             .append("\n");
266
267       } else {
268         double leftBound = (stepSize * counter);
269         double rightBound = ((stepSize) * (counter + 1));
270         sb.append(indentPadding).append("\t")
271             .append((String.format(floatFormatStr, leftBound) + " < x < "
272                 + String.format(floatFormatStr, rightBound) + " : " + histogramBins[counter]))
273             .append("\n");
274       }
275
276       counter++;
277
278     }
279
280     return sb.toString();
281
282   }
283
284 }