-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.ts
129 lines (99 loc) · 3.06 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
const allDigits = /^(-|\+)?\d+$/
const withDecimal = /^(-|\+)?(\d+)\.(\d+)$/
export function validate(str: any) {
return typeof str === `string` && (allDigits.test(str) || withDecimal.test(str))
}
function validateAndThrow(str: string) {
if (!validate(str)) {
throw new Error(`Invalid input ` + str)
}
}
export function getPrecision(str: string) {
if (allDigits.test(str)) {
return 0
} else {
return str.split(`.`)[1].length
}
}
export function add(a: string, b: string) {
validateAndThrow(a)
validateAndThrow(b)
const normalized = normalizeToSamePrecision(a, b)
const sum = (BigInt(normalized.a) + BigInt(normalized.b)).toString()
return normalized.precision > 0 ? addDecimal(sum, normalized.precision) : sum
}
export function subtract(a: string, b: string) {
validateAndThrow(a)
validateAndThrow(b)
const normalized = normalizeToSamePrecision(a, b)
const result = (BigInt(normalized.a) - BigInt(normalized.b)).toString()
return normalized.precision > 0 ? addDecimal(result, normalized.precision) : result
}
export function multiply(a: string, b: string) {
validateAndThrow(a)
validateAndThrow(b)
const normalized = normalizeToLargeEnoughPrecisionToMultiply(a, b)
const result = (BigInt(normalized.a) * BigInt(normalized.b)).toString()
return normalized.precision > 0 ? addDecimal(result, normalized.precision) : result
}
export function modulo(dividend: string, divisor: string) {
validateAndThrow(dividend)
validateAndThrow(divisor)
const normalized = normalizeToSamePrecision(dividend, divisor)
const result = (BigInt(normalized.a) % BigInt(normalized.b)).toString()
return normalized.precision > 0 ? addDecimal(result, normalized.precision) : result
}
function normalizeToSamePrecision(a: string, b: string) {
const precisionA = getPrecision(a)
const precisionB = getPrecision(b)
const differenceInPrecisions = diff(precisionA, precisionB)
const precision = Math.max(precisionA, precisionB)
if (precisionA > precisionB) {
b = padWithZeroes(b, differenceInPrecisions)
} else if (precisionB > precisionA) {
a = padWithZeroes(a, differenceInPrecisions)
}
return {
a: stripDecimal(a),
b: stripDecimal(b),
precision,
}
}
function normalizeToLargeEnoughPrecisionToMultiply(a: string, b: string) {
const precisionA = getPrecision(a)
const precisionB = getPrecision(b)
const precision = precisionA + precisionB
return {
a: stripDecimal(a),
b: stripDecimal(b),
precision,
}
}
function diff(a: number, b: number) {
return a > b ? (a - b) : (b - a)
}
function padWithZeroes(str: string, newZeroes: number) {
while (newZeroes > 0) {
str += `0`
newZeroes--
}
return str
}
function stripDecimal(str: string) {
return str.replace(`.`, ``)
}
function addDecimal(str: string, position: number) {
const isNegative = str[0] === `-`
if (isNegative) {
str = str.substring(1)
}
while (str.length < position + 1) {
str = `0` + str
}
const beforeTheDecimal = str.length - position
let newNumber = str.substring(0, beforeTheDecimal) + `.` + str.substring(beforeTheDecimal)
if (isNegative) {
newNumber = `-` + newNumber
}
return newNumber
}