import { UnsafeIntegerError } from './errors' ; import { Money } from './money' ; export const PRECISION_I = 20 ; export const PRECISION = BigInt ( PRECISION_I ) ; export const PRECISION_M = 10n ** PRECISION ; export enum Round { HALF_TO_EVEN = 1 , BANKERS = 1 , HALF_AWAY_FROM_0 = 2 , HALF_TOWARDS_0 = 3 , TOWARDS_0 = 11 , TRUNCATE = 11 } /**
 * This helper function takes a string, number or anything that can
 * be used in the constructor of a Money object, and returns a bigint
 * with adjusted precision.
 */ export function moneyValueToBigInt ( input , round ) { if ( input instanceof Money ) { return input . toSource ( ) ; } switch ( typeof input ) { case 'string' : { const parts = input . match ( /^(-)?([0-9]*)?(\.([0-9]*))?$/ ) ; if ( ! parts ) { throw new TypeError ( 'Input string must follow the pattern (-)##.## or -##' ) ; } const signPart = parts [ 1 ] as ( '-' | undefined ) ; const wholePart = parts [ 2 ] ; const fracPart = parts [ 4 ] ; let output ; if ( wholePart === undefined ) { output = 0n ; } else { output = BigInt ( wholePart ) * PRECISION_M ; } if ( fracPart !== undefined ) { const precisionDifference = ( PRECISION - BigInt ( fracPart . length ) ) ; if ( precisionDifference >= 0 ) { output += BigInt ( fracPart ) * 10n ** precisionDifference ; } else { output += divide ( BigInt ( fracPart ) , 10n ** ( - precisionDifference ) , round ) ; } } if ( signPart === '-' ) { output *= - 1n ; } return output ; } case 'bigint' : return input * PRECISION_M ; case 'number' : if ( ! Number . isSafeInteger ( input ) ) { throw new UnsafeIntegerError ( 'The number ' + input + ' is not a "safe" integer. It must be converted before passing it' ) ; } return BigInt ( input ) * PRECISION_M ; default : throw new TypeError ( 'value must be a safe integer, bigint or string' ) ; } } /**
 * This function takes a bigint that was multiplied by PRECISON_M, and returns
 * a human readable string value with a specified precision.
 *
 * Precision is the number of decimals that are returned.
 */ export function bigintToFixed ( value , precision , round ) { if ( precision === 0 ) { return divide ( value , PRECISION_M , round ) . toString ( ) ; } let wholePart = ( value / PRECISION_M ) ; const negative = value < 0 ; let remainder = ( value % PRECISION_M ) ; if ( precision > PRECISION ) { remainder *= 10n ** ( BigInt ( precision ) - PRECISION ) ; } else { remainder = divide ( remainder , 10n ** ( PRECISION - BigInt ( precision ) ) , round ) ; } if ( remainder < 0 ) { remainder *= - 1n ; } let remainderStr = remainder . toString ( ) . padStart ( precision , '0' ) ; if ( remainderStr . length > precision ) { wholePart += negative ? - 1n : 1n ; remainder = 0n ; remainderStr = '0' . repeat ( precision ) ; } let wholePartStr = wholePart . toString ( ) ; if ( wholePartStr === '0' && negative ) { wholePartStr = '-0' ; } return wholePartStr + '.' + remainderStr ; } /**
 * This function takes 2 bigints and divides them.
 *
 * By default ecmascript will round to 0. For example,
 * 5n / 2n yields 2n.
 *
 * This function rounds to the nearest even number, also
 * known as 'bankers rounding'.
 */ export function divide ( a , b , round ) { const aAbs = a > 0 ? a : - a ; const bAbs = b > 0 ? b : - b ; let result = aAbs / bAbs ; const rem = aAbs % bAbs ; if ( rem * 2n > bAbs ) { switch ( round ) { case Round . TRUNCATE : break ; default : result ++ ; break ; } } else if ( rem * 2n === bAbs ) { switch ( round ) { case Round . HALF_TO_EVEN : if ( result % 2n === 1n ) { result ++ ; } break ; case Round . HALF_AWAY_FROM_0 : result ++ ; break ; case Round . TRUNCATE : case Round . HALF_TOWARDS_0 : break ; } } if ( a > 0 !== b > 0 ) { return - result ; } else { return result ; } }