export type Params = { fn : ( ... inputs : any [ ] ) => number ; fnParams : any [ ] ; percentTolerance ? : number ; customToleranceFn ? : ( arg0 : number ) => boolean ; maxIterations : number ; maxStep : number ; goal : number ; independentVariableIdx : number ; } ; export const IsNanError = TypeError ( 'resulted in NaN' ) ; export const FailedToConvergeError = Error ( 'failed to converge' ) ; export const InvalidInputsError = Error ( 'invalid inputs' ) ; const goalSeek = ( { fn , fnParams , percentTolerance , customToleranceFn , maxIterations , maxStep , goal , independentVariableIdx , } ) : number => { if ( typeof customToleranceFn !== 'function' ) { if ( ! percentTolerance ) { throw InvalidInputsError ; } } let g ; let y ; let y1 ; let oldGuess ; let newGuess ; let res ; const absoluteTolerance = ( ( percentTolerance || 0 ) / 100 ) * goal ; for ( let i = 0 ; i < maxIterations ; i ++ ) { res = fn . apply ( null , fnParams ) ; y = res - goal ; if ( isNaN ( y ) ) throw IsNanError ; if ( typeof customToleranceFn !== 'function' ) { if ( Math . abs ( y ) <= Math . abs ( absoluteTolerance ) ) return fnParams [ independentVariableIdx ] ; } else { if ( customToleranceFn ( res ) ) return fnParams [ independentVariableIdx ] ; } oldGuess = fnParams [ independentVariableIdx ] ; newGuess = oldGuess + y ; if ( Math . abs ( newGuess - oldGuess ) > maxStep ) { if ( newGuess > oldGuess ) { newGuess = oldGuess + maxStep ; } else { newGuess = oldGuess - maxStep ; } } fnParams [ independentVariableIdx ] = newGuess ; y1 = fn . apply ( null , fnParams ) - goal ; if ( isNaN ( y1 ) ) throw IsNanError ; g = ( y1 - y ) / y ; if ( g === 0 ) g = 0.0001 ; newGuess = oldGuess - y / g ; if ( maxStep && Math . abs ( newGuess - oldGuess ) > maxStep ) { if ( newGuess > oldGuess ) { newGuess = oldGuess + maxStep ; } else { newGuess = oldGuess - maxStep ; } } fnParams [ independentVariableIdx ] = newGuess ; } throw FailedToConvergeError ; } ; export default goalSeek ;