import _ from 'lodash' ; export class PointInfo { val : string ; start : number ; ms = 0 ; constructor ( val , start ) { this . val = val ; this . start = start ; } } export class LegendValue { val : string ; ms = 0 ; count = 0 ; per = 0 ; constructor ( val ) { this . val = val ; } } export class DistinctPoints { changes : PointInfo [ ] = [ ] ; legendInfo : LegendValue [ ] = [ ] ; last : PointInfo | null = null ; asc = false ; transitionCount = 0 ; distinctValuesCount = 0 ; elapsed = 0 ; constructor ( public name ) { } add ( ts , val ) { if ( this . last == null ) { this . last = { val : val , start : ts , ms : 0 , } ; this . changes . push ( this . last ) ; } else if ( ts === this . last . start ) { console . log ( 'skip point with duplicate timestamp' , ts , val ) ; return ; } else { if ( this . changes . length === 1 ) { this . asc = ts > this . last . start ; } if ( ts > this . last . start !== this . asc ) { console . log ( 'skip out of order point' , ts , val ) ; return ; } if ( val === this . last . val ) { if ( ! this . asc ) { this . last . start = ts ; } } else { this . last = { val : val , start : ts , ms : 0 , } ; this . changes . push ( this . last ) ; } } } finish ( ctrl ) { if ( this . changes . length < 1 ) { console . log ( 'no points found!' ) ; return ; } if ( ! this . asc ) { this . last = this . changes [ 0 ] ; _ . reverse ( this . changes ) ; } if ( ! this . last ) { return ; } if ( this . last . start < ctrl . range . to ) { const until = ctrl . range . to + 1 ; this . changes . push ( { val : this . last . val , start : until , ms : 0 , } ) ; } this . transitionCount = 0 ; const distinct = new Map < string , LegendValue > ( ) ; let last = this . changes [ 0 ] ; for ( let i = 1 ; i < this . changes . length ; i ++ ) { const pt = this . changes [ i ] ; let s = last . start ; let e = pt . start ; if ( s < ctrl . range . from ) { s = ctrl . range . from ; } else if ( s < ctrl . range . to ) { this . transitionCount ++ ; } if ( e > ctrl . range . to ) { e = ctrl . range . to ; } last . ms = e - s ; if ( last . ms > 0 ) { if ( distinct . has ( last . val ) ) { const v = distinct . get ( last . val ) ! ; v . ms += last . ms ; v . count ++ ; } else { distinct . set ( last . val , { val : last . val , ms : last . ms , count : 1 , per : 0 } ) ; } } last = pt ; } const elapsed = ctrl . range . to - ctrl . range . from ; this . elapsed = elapsed ; distinct . forEach ( ( value , key ) => { value . per = value . ms / elapsed ; this . legendInfo . push ( value ) ; } ) ; this . distinctValuesCount = _ . size ( this . legendInfo ) ; if ( ! ctrl . isTimeline ) { this . legendInfo = _ . orderBy ( this . legendInfo , [ 'ms' ] , [ 'desc' ] ) ; } } static combineLegend ( data , ctrl ) { if ( data . length === 1 ) { return data [ 0 ] ; } const merged = new DistinctPoints ( 'merged' ) ; let elapsed = 0 ; const distinct = new Map < string , LegendValue > ( ) ; _ . forEach ( data , ( m ) => { merged . transitionCount += m . transitionCount ; elapsed += m . elapsed ; _ . forEach ( m . legendInfo , ( leg ) => { if ( distinct . has ( leg . val ) ) { const v = distinct . get ( leg . val ) ! ; v . ms += leg . ms ; v . count += leg . count ; } else { distinct . set ( leg . val , { val : leg . val , ms : leg . ms , count : leg . count , per : 0 } ) ; } } ) ; } ) ; merged . elapsed = elapsed ; distinct . forEach ( ( value , key ) => { value . per = value . ms / elapsed ; merged . legendInfo . push ( value ) ; } ) ; merged . distinctValuesCount = _ . size ( merged . legendInfo ) ; return merged ; } }