import BigNumber from 'bignumber.js' ; constant type { ( 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ) } description export type ROUND_MODE = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 ; function genFnFromBigNumber < T extends keyof BigNumber , U extends boolean > ( fn , noRight ) { if ( noRight ) { return function ( ) : number { return this . inner [ fn ] ( ) ; } as any ; } return function ( right ) { const temp = right . clone ( ) ; this . alignPrecision ( temp ) ; return this . inner [ fn ] ( temp . _getInner ( ) ) ; } as any ; } const BN = BigNumber . clone ( ) ; let GLOBAL_PRECISION = 18 ; export class FixedPointNumber { private precision ! : number ; private inner : BigNumber ; constructor ( origin , precision = GLOBAL_PRECISION ) { if ( typeof origin !== 'number' && typeof origin !== 'string' ) throw new Error ( 'FixedPointNumber constructor should use number or string' ) ; this . precision = precision ; this . inner = new BN ( origin ) . shiftedBy ( this . precision ) ; } public static setGlobalPrecision ( precision = 18 ) { GLOBAL_PRECISION = precision ; } name description public static fromRational ( numerator , denominator , precision = 18 ) { const _numerator = numerator instanceof FixedPointNumber ? numerator . _getInner ( ) : new BN ( numerator ) ; const _denominator = denominator instanceof FixedPointNumber ? denominator . _getInner ( ) : new BN ( denominator ) ; const inner = _numerator . times ( 10 ** precision ) . div ( _denominator ) ; const temp = new FixedPointNumber ( 0 , precision ) ; temp . _setInner ( inner ) ; return temp ; } name description public static fromInner ( origin , precision = 18 ) { const inner = new BN ( origin ) ; const temp = new FixedPointNumber ( 0 , precision ) ; temp . _setInner ( inner ) ; return temp ; } name description public static _fromBN ( origin , precision = 18 ) { const temp = new FixedPointNumber ( 0 , precision ) ; temp . _setInner ( origin ) ; return temp ; } public _setInner ( origin ) { this . inner = origin ; } public _getInner ( ) { return this . inner ; } private setMode ( dp = 0 , rm = 3 ) { BN . config ( { DECIMAL_PLACES : dp , ROUNDING_MODE : rm as ROUND_MODE , EXPONENTIAL_AT : [ - 100 , 100 ] } ) ; } name public toNumber ( dp = 8 , rm = 3 ) { this . setMode ( ) ; return this . inner . shiftedBy ( - this . precision ) . dp ( dp , rm as any ) . toNumber ( ) ; } name public toString ( dp = 0 , rm = 3 ) { this . setMode ( dp , rm ) ; return this . inner . shiftedBy ( - this . precision ) . toString ( ) ; } name public toChainData ( ) { return this . inner . toFixed ( 0 , 3 ) ; } name public trunc ( ) { this . setMode ( ) ; const DIV = 10 ** this . precision ; const inner = this . _getInner ( ) . abs ( ) . div ( DIV ) . dp ( 0 , 3 ) . times ( DIV ) ; if ( this . isNegative ( ) ) { return FixedPointNumber . _fromBN ( inner . negated ( ) ) ; } else { return FixedPointNumber . _fromBN ( inner ) ; } } name public frac ( ) { const integer = this . trunc ( ) ; const fractional = this . minus ( integer ) ; if ( integer . isZero ( ) ) { return fractional ; } else { return fractional . abs ( ) ; } } private alignPrecision ( b ) { if ( this . precision !== b . precision ) { b . setPrecision ( this . precision ) ; } } name description public setPrecision ( precision ) { const oldPrecision = this . precision ; this . precision = precision ; this . _setInner ( this . inner . shiftedBy ( this . precision - oldPrecision ) ) ; } name description public forceSetPrecision ( precision ) { this . precision = precision ; } name description public getPrecision ( ) { return this . precision ; } name description public abs ( ) { return FixedPointNumber . _fromBN ( this . inner . abs ( ) , this . precision ) ; } name description public plus ( right ) { const temp = right . clone ( ) ; this . alignPrecision ( temp ) ; this . setMode ( ) ; return FixedPointNumber . _fromBN ( this . inner . plus ( temp . inner ) , this . precision ) ; } name description public minus ( right ) { const temp = right . clone ( ) ; this . alignPrecision ( temp ) ; this . setMode ( ) ; return FixedPointNumber . _fromBN ( this . inner . minus ( temp . inner ) , this . precision ) ; } name description public times ( right ) { const temp = right . clone ( ) ; this . alignPrecision ( temp ) ; this . setMode ( ) ; return FixedPointNumber . _fromBN ( this . inner . times ( temp . inner ) . shiftedBy ( - this . precision ) , this . precision ) ; } name description public div ( right ) { const temp = right . clone ( ) ; this . alignPrecision ( temp ) ; this . setMode ( ) ; return FixedPointNumber . _fromBN ( this . inner . shiftedBy ( this . precision ) . div ( temp . inner ) , this . precision ) ; } name public reciprocal ( ) { return FixedPointNumber . ONE . div ( this ) ; } name public isGreaterThan = genFnFromBigNumber ( 'isGreaterThan' , false ) . bind ( this ) ; name public isGreaterThanOrEqualTo = genFnFromBigNumber ( 'isGreaterThanOrEqualTo' , false ) . bind ( this ) ; name public isLessThan = genFnFromBigNumber ( 'isLessThan' , false ) . bind ( this ) ; name public isLessOrEqualTo = genFnFromBigNumber ( 'isLessThanOrEqualTo' , false ) . bind ( this ) ; name public isEqualTo = genFnFromBigNumber ( 'isEqualTo' , false ) . bind ( this ) ; name public isZero = genFnFromBigNumber ( 'isZero' , true ) . bind ( this ) ; name public isNaN = genFnFromBigNumber ( 'isNaN' , true ) . bind ( this ) ; name public isFinaite = genFnFromBigNumber ( 'isFinite' , true ) . bind ( this ) ; name public isNegative = genFnFromBigNumber ( 'isNegative' , true ) . bind ( this ) ; name public isPositive = genFnFromBigNumber ( 'isPositive' , true ) . bind ( this ) ; name public clone ( ) { return FixedPointNumber . fromInner ( this . inner . toString ( ) , this . precision ) ; } name public min ( ... targets ) { const temp = targets . map ( ( item ) => item . clone ( ) ) ; temp . forEach ( ( item ) => this . alignPrecision ( item ) ) ; return FixedPointNumber . _fromBN ( BigNumber . min . apply ( null , [ this . inner , ... temp . map ( ( i ) => i . _getInner ( ) ) ] ) , this . precision ) ; } name public max ( ... targets ) { const temp = targets . map ( ( item ) => item . clone ( ) ) ; temp . forEach ( ( item ) => this . alignPrecision ( item ) ) ; return FixedPointNumber . _fromBN ( BigNumber . max . apply ( null , [ this . inner , ... temp . map ( ( i ) => i . _getInner ( ) ) ] ) , this . precision ) ; } public add = this . plus ; public sub = this . minus ; public mul = this . times ; public lt = this . isLessThan ; public gt = this . isGreaterThan ; public lte = this . isLessOrEqualTo ; public gte = this . isGreaterThanOrEqualTo ; public eq = this . isEqualTo ; static ZERO = new FixedPointNumber ( 0 ) ; static ONE = new FixedPointNumber ( 1 ) ; static TWO = new FixedPointNumber ( 2 ) ; static THREE = new FixedPointNumber ( 3 ) ; static FOUR = new FixedPointNumber ( 3 ) ; static FIVE = new FixedPointNumber ( 5 ) ; static SIX = new FixedPointNumber ( 6 ) ; static TEN = new FixedPointNumber ( 10 ) ; }