/*
 *     Copyright (C) 2015  higherfrequencytrading.com
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 *
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package net.openhft.chronicle.core.util;

/**
 * Created by peter on 10/07/15.
 */
public class Histogram {
    private final int fractionBits;
    private long totalCount, underRange, overRange;
    private int[] sampleCount;
    private long floor;

    public Histogram(int powersOf2, int fractionBits) {
        this.fractionBits = fractionBits;
        sampleCount = new int[powersOf2 << fractionBits];
        floor = Double.doubleToRawLongBits(1) >> (52 - fractionBits);
    }

    public int sample(double time) {
        int bucket = (int) ((Double.doubleToRawLongBits(time) >> (52 - fractionBits)) - floor);
        if (bucket < 0)
            underRange++;
        else if (bucket >= sampleCount.length)
            overRange++;
        else
            sampleCount[bucket]++;
        totalCount++;
        return bucket;
    }

    public double percentile(double fraction) {
        long value = (long) (totalCount * (1 - fraction));
        value -= overRange;
        if (value < 0)
            return Double.POSITIVE_INFINITY;
        for (int i = sampleCount.length - 1; i >= 0; i--) {
            value -= sampleCount[i];
            if (value < 0) {
                long bits = ((((i + floor) << 1) + 1) << (51 - fractionBits));
                return Double.longBitsToDouble(bits);
            }
        }
        return 1;
    }

    public String toMicrosFormat() {
        return String.format("50/90 99/99.9 99.99/99.999 - worst was %.2f/%.2f %.1f/%.1f %,d/%,d - %,d",
                percentile(0.5) / 1e3, percentile(0.9) / 1e3,
                percentile(0.99) / 1e3, percentile(0.999) / 1e3,
                (long) percentile(0.9999) / 1000, (long) percentile(0.99999) / 1000,
                (long) percentile(1) / 1000);
    }
}

@@ -971,16 +971,69 @@
 floor;%0A%0A
+    public Histogram() %7B%0A        this(42, 4);%0A    %7D%0A%0A
     publ
@@ -2179,22 +2179,8 @@
 urn 
-String.format(
 %2250/
@@ -2219,43 +2219,11 @@
 was 
-%25.2f/%25.2f %25.1f/%25.1f %25,d/%25,d - %25,d%22,
+%22 +
 %0A   
@@ -2227,32 +2227,34 @@
                 
+p(
 percentile(0.5) 
@@ -2254,26 +2254,54 @@
 e(0.5) / 1e3
-, 
+) + %22 / %22 +%0A                p(
 percentile(0
@@ -2301,33 +2301,42 @@
 ntile(0.9) / 1e3
-,
+) + %22  %22 +
 %0A               
@@ -2336,16 +2336,18 @@
         
+p(
 percenti
@@ -2360,18 +2360,46 @@
 9) / 1e3
-, 
+) + %22 / %22 +%0A                p(
 percenti
@@ -2413,17 +2413,26 @@
 9) / 1e3
-,
+) + %22  %22 +
 %0A       
@@ -2432,39 +2432,34 @@
                 
+p
 (
-long) 
 percentile(0.999
@@ -2468,20 +2468,40 @@
  / 1
-000, (long) 
+e3) + %22 / %22 +%0A                p(
 perc
@@ -2523,12 +2523,21 @@
  / 1
-000,
+e3) + %22 - %22 +
 %0A   
@@ -2553,34 +2553,393 @@
     
-(lo
+p(percentile(1) / 1000);%0A    %7D%0A%0A    private Stri
 ng
-)
  p
-ercentile(1) / 100
+(double v) %7B%0A        return v %3C 0.1 ? String.format(%22%25.3f%22, v) :%0A                v %3C 1 ? String.format(%22%25.2f%22, v) :%0A                        v %3C 10 ? String.format(%22%25.1f%22, v) :%0A                                v %3C 1000 ? Long.toString(Math.round(v)) :%0A                                        String.format(%22%25,d%22, Math.round(v / 10) * 1
 0);%0A

Add a high range, precision histogram for tests