import { scaleLinear , scaleLog } from 'd3-scale' ; param domain export function linear ( domain , clamp = true ) { const scale = scaleLinear ( ) . domain ( domain ) . clamp ( clamp ) ; return ( value ) => scale ( value ) as number ; } param domain export function log ( domain , clamp = true ) { const scale = scaleLog ( ) . domain ( domain ) . clamp ( clamp ) ; return ( value ) => scale ( value ) as number ; } export interface Bin extends Array < number > { x0 ? : number ; x1 ? : number ; } function findForwardBreak ( data , start ) { const value = data [ start - 1 ] ; for ( let i = start ; i < data . length ; i ++ ) { if ( data [ i ] !== value ) { return i - start ; } } return data . length - start ; } function findBackwardBreak ( data , start ) { const value = data [ start - 1 ] ; for ( let i = start - 1 ; i >= 0 ; i -- ) { if ( data [ i ] !== value ) { return start - i - 1 ; } } return start ; } param data param bins param smoothing function quantizeHistogram ( data , bins , smoothing ? ) { const values = data . sort ( ( a , b ) => a - b ) ; let binLength = Math . ceil ( values . length / bins ) ; const binStructure = [ ] ; let start = 0 ; for ( let i = 0 ; i < bins ; i ++ ) { const end = start + binLength ; const forward = findForwardBreak ( values , end ) ; const backward = findBackwardBreak ( values , end ) ; const moveBackward = backward < forward && backward < binLength ; const newEnd = smoothing && moveBackward ? end - backward : end + forward ; const bin = values . slice ( start , newEnd ) ; if ( bin . length > 0 ) { bin . x0 = bin [ 0 ] ; bin . x1 = bin [ bin . length - 1 ] ; binStructure . push ( bin ) ; } start = newEnd ; if ( newEnd !== end ) { binLength = Math . ceil ( ( values . length - newEnd ) / ( bins - ( i + 1 ) ) ) ; } } return binStructure ; } param domain param bins export function quantile ( domain , bins ? ) { const quantized = quantizeHistogram ( domain , bins || 10 , true ) ; const histoLinearScale = scaleLinear ( ) . domain ( [ 0 , bins ! - 1 ] ) . range ( [ 0 , 1 ] ) ; const scale = ( value ) => { const binIndex = quantized . findIndex ( bin => { return value >= bin . x0 ! && value <= bin . x1 ! ; } ) ; return histoLinearScale ( binIndex ) ; } ; scale . bins = quantized . map ( ( { x0 , x1 , length } ) => ( { min : x0 , max : x1 , size : length , } ) ) ; return scale as ( input : number ) => number ; } export interface LearningState { /**
     * Current value of the learning. When elapsed time is 0 (i.e., the learning was just applied),
     * this will be 1.0. As time elapses and the learning fades, this declines toward 0.
     */ value : number ; /**
     * This is a measure of the recall strength needed. If learning exposure happens frequently,
     * this will increase slowly because the recall stays high. As time elapses between exposures,
     * this increases because recall is more difficult. If the learning value reaches zero (i.e.,
     * there is too much time between exposures for any reasonable recall), then this resets.
     */ strength : number ; } param elapsedTime param state returns export function recall ( ) { param elapsedTime return ( elapsedTime , state = { value : 0 , strength : 0 } ) : LearningState => { const s = { ... state } ; if ( elapsedTime === 0 ) { if ( s . strength === 0 ) { s . strength = 1 ; s . value = 1 ; } else { s . strength += 1 - s . value ; } } if ( s . strength > 0 ) { const d = elapsedTime / 1000 ; const exponent = - d / s . strength ; s . value = Math . exp ( exponent ) ; } else { s . value = 0 ; } if ( s . value === 0 ) { s . strength = 1 ; } return s ; } ; }