export function clamp ( val , min , max , cycle = false ) { if ( val < min ) { return cycle ? max : min ; } if ( val > max ) { return cycle ? min : max ; } return val ; } export function clamp01 ( val ) { return clamp ( val , 0 , 1 , false ) ; } export function arrayCompareG < T > ( arr , cond ) { if ( ! Array . isArray ( arr ) || arr . length <= 0 ) { return null ; } let result = arr [ 0 ] ; for ( let i = 1 ; i < arr . length ; i ++ ) { const current = arr [ i ] ; if ( cond ( current , result ) ) { result = current ; } } return result ; } export function arrayCompare ( arr , absolute , cond ) { if ( ! Array . isArray ( arr ) || arr . length <= 0 ) { return null ; } let max = arr [ 0 ] ; for ( let i = 1 ; i < arr . length ; i ++ ) { const e = absolute ? Math . abs ( arr [ i ] ) : arr [ i ] ; if ( cond ( e , max ) ) { max = e ; } } return max ; } export function arrayMax ( arr , absolute = false ) { return arrayCompare ( arr , absolute , ( e , max ) => e > max ) ; } export function arrayMin ( arr , absolute = false ) { return arrayCompare ( arr , absolute , ( e , min ) => e < min ) ; } export function arrayAverage ( arr , absolute = false ) { if ( ! Array . isArray ( arr ) || arr . length <= 0 ) { return 0 ; } let sum = 0 ; for ( let i = 0 ; i < arr . length ; i ++ ) { const e = absolute ? Math . abs ( arr [ i ] ) : arr [ i ] ; sum += e ; } return sum / arr . length ; } export function arrayCount < T > ( arr , condition ) { if ( ! arr || ! arr . length ) { return 0 ; } let count = 0 ; arr . forEach ( a => { if ( condition ( a ) ) { ++ count ; } } ) ; return count ; } export function normalize ( arr ) { if ( arr . length === 0 ) { return arr ; } const min = arrayMin ( arr ) ; const max = arrayMax ( arr ) ; const dist = max - min ; if ( Math . abs ( dist ) < 0.000001 ) { return arr . map ( _ => 1 ) ; } return arr . map ( x => ( x - min ) / dist ) ; } export function roundNumber ( val , signs = 2 ) { const k = 10 ** signs ; return Math . round ( ( val + Number . EPSILON ) * k ) / k ; } export function roundHalf ( num ) { return Math . round ( num * 2 ) / 2 ; } export function random ( min = 0 , max = 1 , trunc = true ) { const r = Math . random ( ) ; const res = min + r * ( max - min ) ; return trunc ? Math . trunc ( res ) : res ; } export function arraySwap < T > ( arr , i1 , i2 ) { const x = arr [ i1 ] ; arr [ i1 ] = arr [ i2 ] ; arr [ i2 ] = x ; } export function shuffle < T > ( arr , slice ) ; export function shuffle < T > ( arr ) ; export function shuffle < T > ( arr , slice ) ; export function shuffle < T > ( arr , slice = true ) { const res = ( slice ? arr ?. slice ( ) : arr ) || [ ] ; for ( let i = 0 ; i < res . length - 1 ; ++ i ) { const nextIndex = random ( i + 1 , res . length - 1 ) ; arraySwap ( res , i , nextIndex ) ; } return res ; } export function groupBy < T , K extends string | number > ( arr , grouper ) { const result = { } ; arr . forEach ( item => { const k = grouper ( item ) ; let g = result [ k ] ; if ( ! g ) { g = [ ] ; result [ k ] = g ; } g . push ( item ) ; } ) ; return result ; } export function groupOneBy < T , K extends string | number > ( arr , grouper ) { const result = { } ; arr . forEach ( item => { const k = grouper ( item ) ; result [ k ] = item ; } ) ; return result ; } export function arraySplit < T > ( arr , predicate ) { const trueArr = [ ] ; const falseArr = [ ] ; arr . forEach ( a => { ( predicate ( a ) ? trueArr : falseArr ) . push ( a ) ; } ) ; return [ trueArr , falseArr ] ; } export function findIndexLeast ( num , arr , sort = false ) { if ( sort ) { arr . sort ( ) ; } return arr . findIndex ( i => i > num ) ; } export function getNumberSuffix ( num ) { const lastDigit = ( num || 0 ) % 10 ; switch ( lastDigit ) { case 1 : return 'st' ; case 2 : return 'nd' ; case 3 : return 'rd' ; default : return 'th' ; } }