import * as _ from "lodash" ; export class SplitCalculator { public scale : number [ ] ; public data : number [ ] ; public maxScaleGapThreshold : number ; public start : number ; constructor ( scale , data , maxScaleGapThreshold ? ) { this . scale = scale ; this . data = data ; this . maxScaleGapThreshold = maxScaleGapThreshold ; this . start = performance . now ( ) ; this . normalize ( ) ; } public normalize ( ) { const normalizedScale = [ ] ; const interpolatedData = [ ] ; _ . forEach ( this . scale , ( scaleValue , index , scale ) => { interpolatedData . push ( _ . isNumber ( this . data [ index ] ) ? this . data [ index ] : 0 ) ; normalizedScale . push ( scaleValue ) ; const nextScaleValue = scale [ index + 1 ] ; if ( _ . isNumber ( nextScaleValue ) ) { const nextScaleDiff = nextScaleValue - scaleValue ; if ( nextScaleDiff < 0 ) { throw new Error ( "Scale should have gaps >= 0" ) ; } if ( _ . isNumber ( this . maxScaleGapThreshold ) && nextScaleDiff > this . maxScaleGapThreshold ) { throw new Error ( "Scale has a too importants gap. Cannot normalize scale" ) ; } if ( nextScaleDiff > 1 ) { const linearFunction = this . getLinearFunction ( this . data [ index + 1 ] , this . data [ index ] , nextScaleValue , scaleValue ) ; let missingScaleValue = scaleValue + 1 ; while ( missingScaleValue < nextScaleValue ) { interpolatedData . push ( linearFunction ( missingScaleValue ) ) ; normalizedScale . push ( missingScaleValue ) ; missingScaleValue ++ ; } } } } ) ; this . scale = normalizedScale ; this . data = interpolatedData ; } public getBestSplit ( scaleRange , showProcessTime ? ) { if ( scaleRange > this . scale . length ) { throw new Error ( "Requested scaleRange of " + scaleRange + " is greater than scale range length of " + this . scale . length + "." ) ; } let maxSumFound ; let currentMaxSum ; if ( scaleRange > 1 ) { let index = 0 ; currentMaxSum = _ . sum ( this . data . slice ( index , scaleRange ) ) ; maxSumFound = currentMaxSum ; while ( this . scale [ scaleRange + index ] ) { currentMaxSum = currentMaxSum + this . data [ scaleRange + index ] - this . data [ index ] ; if ( maxSumFound < currentMaxSum ) { maxSumFound = currentMaxSum ; } index ++ ; } } else { maxSumFound = _ . max ( this . data ) ; } const bestSplit = ( maxSumFound / scaleRange ) ; if ( showProcessTime ) { const processTime = performance . now ( ) - this . start ; console . debug ( "Processed split of range " + scaleRange + " in " + _ . floor ( processTime , 4 ) + " ms." ) ; } return bestSplit ; } public getBestSplitRanges ( ranges , showProcessTime ? ) { const results = [ ] ; _ . forEach ( ranges , ( range ) => { results . push ( { range : range , result : this . getBestSplit ( range ) } ) ; } ) ; if ( showProcessTime ) { const processTime = performance . now ( ) - this . start ; console . debug ( "Processed split in " + _ . floor ( processTime , 4 ) + " ms." ) ; } return results ; } public getLinearFunction ( y1 , y0 , x1 , x0 ) { const a = ( y1 - y0 ) / ( x1 - x0 ) ; const b = y0 - a * x0 ; return ( value ) => { return _ . floor ( a * value + b , 4 ) ; } ; } }