import bigInteger from "big-integer" ; import { Amino , Type } from "@chainapsis/ts-amino" ; import { Int } from "./int" ; const { Method , DefineStruct } = Amino ; @ DefineStruct ( ) export class Dec { public static readonly precision : bigInteger . BigInteger = bigInteger ( 18 ) ; private static readonly precisionMultipliers : { [ key ] : bigInteger . BigInteger | undefined ; } = { } ; private static calcPrecisionMultiplier ( prec ) { if ( prec . lt ( bigInteger ( 0 ) ) ) { throw new Error ( "Invalid prec" ) ; } if ( prec . gt ( Dec . precision ) ) { throw new Error ( "Too much precision" ) ; } if ( Dec . precisionMultipliers [ prec . toString ( ) ] ) { return Dec . precisionMultipliers [ prec . toString ( ) ] ! ; } const zerosToAdd = Dec . precision . minus ( prec ) ; const multiplier = bigInteger ( 10 ) . pow ( zerosToAdd ) ; Dec . precisionMultipliers [ prec . toString ( ) ] = multiplier ; return multiplier ; } private int : bigInteger . BigInteger ; param int param prec constructor ( int , prec = 0 ) { if ( typeof int === "string" ) { if ( int . length === 0 ) { throw new Error ( "empty string" ) ; } if ( ! /^(-?\d+\.\d+)$|^(-?\d+)$/ . test ( int ) ) { throw new Error ( `invalid decimal: ${ int }` ) ; } if ( int . indexOf ( "." ) >= 0 ) { prec = int . length - int . indexOf ( "." ) - 1 ; int = int . replace ( "." , "" ) ; } this . int = bigInteger ( int ) ; } else if ( typeof int === "number" ) { this . int = bigInteger ( int ) ; } else if ( int instanceof Int ) { this . int = bigInteger ( int . toString ( ) ) ; } else { this . int = bigInteger ( int ) ; } this . int = this . int . multiply ( Dec . calcPrecisionMultiplier ( bigInteger ( prec ) ) ) ; } public isZero ( ) { return this . int . eq ( bigInteger ( 0 ) ) ; } public isNegative ( ) { return this . int . isNegative ( ) ; } public isPositive ( ) { return this . int . isPositive ( ) ; } public equals ( d2 ) { return this . int . eq ( d2 . int ) ; } /**
     * Alias for the greater method.
     */ public gt ( d2 ) { return this . int . gt ( d2 . int ) ; } /**
     * Alias for the greaterOrEquals method.
     */ public gte ( d2 ) { return this . int . geq ( d2 . int ) ; } /**
     * Alias for the lesser method.
     */ public lt ( d2 ) { return this . int . lt ( d2 . int ) ; } /**
     * Alias for the lesserOrEquals method.
     */ public lte ( d2 ) { return this . int . leq ( d2 . int ) ; } /**
     * reverse the decimal sign.
     */ public neg ( ) { return new Dec ( this . int . negate ( ) , Dec . precision . toJSNumber ( ) ) ; } /**
     * Returns the absolute value of a decimals.
     */ public abs ( ) { return new Dec ( this . int . abs ( ) , Dec . precision . toJSNumber ( ) ) ; } public add ( d2 ) { return new Dec ( this . int . add ( d2 . int ) , Dec . precision . toJSNumber ( ) ) ; } public sub ( d2 ) { return new Dec ( this . int . subtract ( d2 . int ) , Dec . precision . toJSNumber ( ) ) ; } public mul ( d2 ) { return new Dec ( this . mulRaw ( d2 ) . chopPrecisionAndRound ( ) , Dec . precision . toJSNumber ( ) ) ; } public mulTruncate ( d2 ) { return new Dec ( this . mulRaw ( d2 ) . chopPrecisionAndTruncate ( ) , Dec . precision . toJSNumber ( ) ) ; } private mulRaw ( d2 ) { return new Dec ( this . int . multiply ( d2 . int ) , Dec . precision . toJSNumber ( ) ) ; } public quo ( d2 ) { return new Dec ( this . quoRaw ( d2 ) . chopPrecisionAndRound ( ) , Dec . precision . toJSNumber ( ) ) ; } public quoTruncate ( d2 ) { return new Dec ( this . quoRaw ( d2 ) . chopPrecisionAndTruncate ( ) , Dec . precision . toJSNumber ( ) ) ; } public quoRoundUp ( d2 ) { return new Dec ( this . quoRaw ( d2 ) . chopPrecisionAndRoundUp ( ) , Dec . precision . toJSNumber ( ) ) ; } private quoRaw ( d2 ) { const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; const mul = this . int . multiply ( precision ) . multiply ( precision ) ; return new Dec ( mul . divide ( d2 . int ) , Dec . precision . toJSNumber ( ) ) ; } public isInteger ( ) { const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; return this . int . remainder ( precision ) . equals ( bigInteger ( 0 ) ) ; } /**
     * Remove a Precision amount of rightmost digits and perform bankers rounding
     * on the remainder (gaussian rounding) on the digits which have been removed.
     */ private chopPrecisionAndRound ( ) { if ( this . isNegative ( ) ) { const absoulteDec = this . abs ( ) ; const choped = absoulteDec . chopPrecisionAndRound ( ) ; return choped . negate ( ) ; } const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; const fivePrecision = precision . divide ( bigInteger ( 2 ) ) ; const { quotient , remainder } = this . int . divmod ( precision ) ; if ( remainder . equals ( bigInteger ( 0 ) ) ) { return quotient ; } if ( remainder . lt ( fivePrecision ) ) { return quotient ; } else if ( remainder . gt ( fivePrecision ) ) { return quotient . add ( bigInteger ( 1 ) ) ; } else { if ( quotient . divide ( bigInteger ( 2 ) ) . equals ( bigInteger ( 0 ) ) ) { return quotient ; } else { return quotient . add ( bigInteger ( 1 ) ) ; } } } private chopPrecisionAndRoundUp ( ) { if ( this . isNegative ( ) ) { const absoulteDec = this . abs ( ) ; const choped = absoulteDec . chopPrecisionAndTruncate ( ) ; return choped . negate ( ) ; } const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; const { quotient , remainder } = this . int . divmod ( precision ) ; if ( remainder . equals ( bigInteger ( 0 ) ) ) { return quotient ; } return quotient . add ( bigInteger ( 1 ) ) ; } /**
     * Similar to chopPrecisionAndRound, but always rounds down
     */ private chopPrecisionAndTruncate ( ) { const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; return this . int . divide ( precision ) ; } public toString ( prec = Dec . precision . toJSNumber ( ) ) { const precision = Dec . calcPrecisionMultiplier ( bigInteger ( 0 ) ) ; const int = this . int . abs ( ) ; const { quotient : interger , remainder : fraction } = int . divmod ( precision ) ; let fractionStr = fraction . toString ( 10 ) ; for ( let i = 0 , l = fractionStr . length ; i < Dec . precision . toJSNumber ( ) - l ; i ++ ) { fractionStr = "0" + fractionStr ; } fractionStr = fractionStr . substring ( 0 , prec ) ; return ( ( this . isNegative ( ) ? "-" : "" ) + interger . toString ( 10 ) + "." + fractionStr ) ; } public round ( ) { return new Int ( this . chopPrecisionAndRound ( ) ) ; } public roundUp ( ) { return new Int ( this . chopPrecisionAndRoundUp ( ) ) ; } public truncate ( ) { return new Int ( this . chopPrecisionAndTruncate ( ) ) ; } @ Method . AminoMarshaler ( { type : Type . String } ) public marshalAmino ( ) { return this . int . toString ( 10 ) ; } public marshalJSON ( ) { return `"${ this . int . toString ( 10 ) }"` ; } }