import EDS from '../EDS' ; export default class Histogram { private _measurement ; private _callFn ; private _sample = new EDS ( ) ; private _min ; private _max ; private _count : number = 0 ; private _sum : number = 0 ; private _varianceM : number = 0 ; private _varianceS : number = 0 ; private _ema : number = 0 ; private used : boolean = false ; constructor ( opts ? ) { opts = opts || { } ; this . _measurement = opts . measurement ; this . _callFn = null ; const methods = { min : this . getMin , max : this . getMax , sum : this . getSum , count : this . getCount , variance : this . _calculateVariance , mean : this . _calculateMean , ema : this . getEma ( ) } ; if ( methods . hasOwnProperty ( this . _measurement ) ) { this . _callFn = methods [ this . _measurement ] ; } else { this . _callFn = function ( ) { const percentiles = this . percentiles ( [ 0.5 , 0.75 , 0.95 , 0.99 , 0.999 ] ) ; const medians = { median : percentiles [ 0.5 ] , p75 : percentiles [ 0.75 ] , p95 : percentiles [ 0.95 ] , p99 : percentiles [ 0.99 ] , p999 : percentiles [ 0.999 ] } ; return medians [ this . _measurement ] ; } ; } } update ( value ) { this . used = true ; this . _count ++ ; this . _sum += value ; this . _sample . update ( value ) ; this . _updateMin ( value ) ; this . _updateMax ( value ) ; this . _updateVariance ( value ) ; this . _updateEma ( value ) ; } percentiles ( percentiles ) { const values = this . _sample . toArray ( ) . sort ( function ( a , b ) { return ( a === b ) ? 0 : a - b ; } ) ; const results = { } ; for ( let i = 0 ; i < percentiles . length ; i ++ ) { const percentile = percentiles [ i ] ; if ( ! values . length ) { results [ percentile ] = null ; continue ; } const pos = percentile * ( values . length + 1 ) ; if ( pos < 1 ) { results [ percentile ] = values [ 0 ] ; } else if ( pos >= values . length ) { results [ percentile ] = values [ values . length - 1 ] ; } else { const lower = values [ Math . floor ( pos ) - 1 ] ; const upper = values [ Math . ceil ( pos ) - 1 ] ; results [ percentile ] = lower + ( pos - Math . floor ( pos ) ) * ( upper - lower ) ; } } return results ; } val ( ) { if ( typeof ( this . _callFn ) === 'function' ) { return this . _callFn ( ) ; } else { return this . _callFn ; } } getMin ( ) { return this . _min ; } getMax ( ) { return this . _max ; } getSum ( ) { return this . _sum ; } getCount ( ) { return this . _count ; } getEma ( ) { return this . _ema ; } fullResults ( ) { const percentiles = this . percentiles ( [ 0.5 , 0.75 , 0.95 , 0.99 , 0.999 ] ) ; return { min : this . _min , max : this . _max , sum : this . _sum , variance : this . _calculateVariance ( ) , mean : this . _calculateMean ( ) , count : this . _count , median : percentiles [ 0.5 ] , p75 : percentiles [ 0.75 ] , p95 : percentiles [ 0.95 ] , p99 : percentiles [ 0.99 ] , p999 : percentiles [ 0.999 ] , ema : this . _ema } ; } _updateMin ( value ) { if ( this . _min === undefined || value < this . _min ) { this . _min = value ; } } _updateMax ( value ) { if ( this . _max === undefined || value > this . _max ) { this . _max = value ; } } _updateVariance ( value ) { if ( this . _count === 1 ) return this . _varianceM = value ; const oldM = this . _varianceM ; this . _varianceM += ( ( value - oldM ) / this . _count ) ; this . _varianceS += ( ( value - oldM ) * ( value - this . _varianceM ) ) ; } _updateEma ( value ) { if ( this . _count <= 1 ) return this . _ema = this . _calculateMean ( ) ; const alpha = 2 / ( 1 + this . _count ) ; this . _ema = value * alpha + this . _ema * ( 1 - alpha ) ; } _calculateMean ( ) { return ( this . _count === 0 ) ? 0 : this . _sum / this . _count ; } _calculateVariance ( ) { return ( this . _count <= 1 ) ? null : this . _varianceS / ( this . _count - 1 ) ; } isUsed ( ) { return this . used ; } }