Last active
May 2, 2018 00:49
-
-
Save Ariex/ecf1fef24f0a473cf6ff8abb0feb0a76 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var BigNumber = (()=>{ | |
// scale numbers represented in array format up/down by 10 to eliminate decimals | |
function alignArrayNumbers(arr1, decimalPos1, arr2, decimalPos2) { | |
var diff = Math.abs(decimalPos1 - decimalPos2); | |
var a1 = arr1.slice() | |
, a2 = arr2.slice(); | |
if (decimalPos1 > decimalPos2) { | |
a2 = [...a2, ...Array(decimalPos1-decimalPos2).fill(0)]; | |
} else if (decimalPos1 < decimalPos2) { | |
a1 = [...a1, ...Array(decimalPos2-decimalPos1).fill(0)]; | |
} | |
return [a1, a2]; | |
} | |
// remove 0 at left side of array | |
function trimLeadingZeros(arr) { | |
var a = arr.slice(); | |
while (a[0] === 0 && a.length > 1) { | |
a.shift(); | |
} | |
return a; | |
} | |
// assume arr1 and arr2 are all represent an unsigned number in array form | |
function arrayCompare(arr1, arr2) { | |
var [a1,a2] = [arr1.slice(), arr2.slice()]; | |
while (a1[0] === 0) { | |
a1.splice(0, 1); | |
} | |
while (a2[0] === 0) { | |
a2.splice(0, 1); | |
} | |
if (a1.length > a2.length) { | |
return 1; | |
} else if (a1.length < a2.length) { | |
return -1; | |
} | |
if (a1.length === a2.length) { | |
for (var i = 0; i < a1.length; i++) { | |
if (a1[i] > a2[i]) { | |
return 1; | |
} else if (a1[i] < a2[i]) { | |
return -1; | |
} | |
} | |
} | |
return 0; | |
} | |
function arrayAdd(arr1, arr2) { | |
if (arr1.length > arr2.length) { | |
arr2 = [...Array(arr1.length-arr2.length).fill(0), ...arr2]; | |
} else if (arr1.length < arr2.length) { | |
arr1 = [...Array(arr2.length-arr1.length).fill(0), ...arr1]; | |
} | |
var res = Array(arr1.length + 1).fill(0); | |
for(var i = arr1.length-1;i>-1;i--){ | |
var r = arr1[i] + arr2[i] + res[i+1]; | |
if (r >= 10) { | |
r = r - 10; | |
res[i] += 1; | |
} | |
res[i+1] = r; | |
} | |
return res; | |
} | |
function arraySubtract(arr1, arr2) { | |
var res = Array(arr1.length).fill(0); | |
res.isNegative = false; | |
if (arrayCompare(arr1, arr2) < 0) { | |
[arr1,arr2] = [arr2, arr1]; | |
res.isNegative = true; | |
} | |
if (arr1.length > arr2.length) { | |
arr2 = [...Array(arr1.length-arr2.length).fill(0), ...arr2]; | |
} else if (arr1.length < arr2.length) { | |
arr1 = [...Array(arr2.length-arr1.length).fill(0), ...arr1]; | |
} | |
for (var i = arr1.length - 1; i > -1; i--) { | |
var r = arr1[i] - arr2[i]; | |
if (r < 0) { | |
r += 10; | |
arr1[i - 1] -= 1; | |
} | |
res[i] = r; | |
} | |
return res; | |
} | |
function arrayMultiply(arr1, arr2) { | |
var res = []; | |
for (var i = 0; i < arr2.length; i++) { | |
var n = arr2[arr2.length - i - 1]; | |
var r = Array(arr1.length + 1).fill(0); | |
for (var j = arr1.length - 1; j > -1; j--) { | |
var v = n * arr1[j] + r[j]; | |
if (v >= 10) { | |
var t = Math.floor(v / 10); | |
v = v - t * 10; | |
r[j] += t;// what if r[j] is 10 now? | |
} | |
r[j + 1] += v; | |
} | |
r = [...r, ...Array(i).fill(0)]; | |
res.push(r); | |
} | |
return res.reduce((s,a)=>arrayAdd(s, a), [0]); | |
} | |
// perform divide on int only, no decimal supported | |
function arrayIntDivide(arr1, arr2) { | |
if (arr2.length === 1 && arr2[0] === 0) { | |
throw "divide by 0"; | |
} | |
for (var i = 0; i < 10; i++) { | |
switch (arrayCompare(arr1, arrayMultiply(arr2, [i]))) { | |
case 0: | |
return i; | |
case -1: | |
return i - 1; | |
} | |
} | |
return 9; | |
} | |
// perform divide on any number, this will scale number represented in array in the same size to make them integer, then apply int divide, then restore the decimal | |
function arrayDivide(arr1, arr2, decimalPos=10) { | |
var res = []; | |
res.decimalPos = decimalPos; | |
var a1 = [...arr1, ...Array(decimalPos).fill(0)]; | |
var startAt = arr2.length; | |
var divider = a1.slice(0, startAt); | |
while (startAt <= a1.length) { | |
var r = arrayIntDivide(divider, arr2); | |
var reminder = arraySubtract(divider, arrayMultiply(arr2, [r])); | |
divider = [...reminder, a1[startAt++]]; | |
res.push(r); | |
} | |
return res; | |
} | |
// function outputNum(arr) { | |
// var res = arr.join("").replace(/^0*/g, ""); | |
// if (res.length < 1) { | |
// res = "0"; | |
// } | |
// if (typeof arr.decimalPos === "number") { | |
// if (res.length > arr.decimalPos) { | |
// res = res.slice(0, -arr.decimalPos) + "." + res.slice(-arr.decimalPos); | |
// } else { | |
// res = "0." + res.padStart(arr.decimalPos, "0"); | |
// } | |
// } | |
// return (arr.isPositive === void (0) || arr.isPositive ? "" : "-") + res; | |
// } | |
// function test(num1, num2, opt) { | |
// var nums1 = num1.toString().split("").map(n=>parseInt(n)); | |
// var nums2 = num2.toString().split("").map(n=>parseInt(n)); | |
// var res = []; | |
// switch (opt) { | |
// case "+": | |
// res[0] = outputNum(arrayAdd(nums1, nums2)); | |
// res[1] = parseFloat(num1) + parseFloat(num2); | |
// res[2] = res[0] === res[1].toString(); | |
// break; | |
// case "-": | |
// res[0] = outputNum(arraySubtract(nums1, nums2)); | |
// res[1] = parseFloat(num1) - parseFloat(num2); | |
// res[2] = res[0] === res[1].toString(); | |
// break; | |
// case "*": | |
// res[0] = outputNum(arrayMultiply(nums1, nums2)); | |
// res[1] = parseFloat(num1) * parseFloat(num2); | |
// res[2] = res[0] === res[1].toString(); | |
// break; | |
// case "/": | |
// res[0] = outputNum(arrayDivide(nums1, nums2, 106)); | |
// res[1] = (parseFloat(num1) / parseFloat(num2)).toFixed(17); | |
// res[2] = res[0] === res[1].toString(); | |
// break; | |
// } | |
// return res; | |
// } | |
// console.group("Plus"); | |
// console.log(test(0, 5432, "+")); | |
// console.log(test(0, 0, "+")); | |
// console.log(test(12345, 5432, "+")); | |
// console.log(test(12345678909, 5432, "+")); | |
// console.groupEnd("Plus"); | |
// console.group("Minus"); | |
// console.log(test(0, 5432, "-")); | |
// console.log(test(0, 0, "-")); | |
// console.log(test(12345, 5432, "-")); | |
// console.log(test(12345678909, 5432, "-")); | |
// console.log(test(5432, 12345678909, "-")); | |
// console.groupEnd("Minus"); | |
// console.group("multiply"); | |
// console.log(test(11, 12, "*")); | |
// console.log(test(0, 12, "*")); | |
// console.log(test(11, 0, "*")); | |
// console.log(test(113141, 12, "*")); | |
// console.log(test("11", "1312311231231232", "*")); | |
// console.groupEnd("multiply"); | |
// console.group("divide"); | |
// console.log(test(11, 12, "/")); | |
// console.log(test(0, 12, "/")); | |
// console.log(test(11, 3, "/")); | |
// console.log(test(113141, 12, "/")); | |
// console.log(test("1230", "321", "/")); | |
// console.log(test("11", "1312311231231232", "/")); | |
// console.groupEnd("divide"); | |
return class BigNumber { | |
/** | |
* Creates an instance of BigNumber. | |
* @param {any} num a number represented as number or string or array or BigNumber | |
* @param {any} decimalPos if num is an array, this will indicate where is the decimal | |
* @param {any} isNegative if num is an array, this will indicate if it is a negative number | |
*/ | |
constructor(num, decimalPos, isNegative) { | |
if (num instanceof BigNumber) { | |
this.decimalPos = num.decimalPos; | |
this.isNegative = num.isNegative; | |
this.num = trimLeadingZeros(num.num); | |
return; | |
} | |
if (Array.isArray(num)) { | |
this.num = trimLeadingZeros(num); | |
this.decimalPos = decimalPos || 0; | |
this.isNegative = isNegative === void (0) ? true : isNegative; | |
return; | |
} | |
var str = num.toString(); | |
// assume there is no format issue | |
this.isNegative = str.slice(0, 1) === "-"; | |
str = str.replace("-", ""); | |
this.decimalPos = str.indexOf(".") > -1 ? str.length - str.indexOf(".") - 1 : 0; | |
this.num = str.replace(".", "").split("").map(s=>parseInt(s)); | |
} | |
toString() { | |
// trim leading zeros | |
var res = this.num.join("").replace(/^0*/g, ""); | |
// display 0 correctly | |
if (res.length < 1) { | |
return "0"; | |
} | |
// display leading 0 for decimals less than 1 | |
if (this.decimalPos >= res.length) { | |
res = "0" + res.padStart(this.decimalPos, "0"); | |
} | |
var decimal = this.decimalPos < 1 ? "" : res.slice(-this.decimalPos); | |
res = `${this.isNegative ? "-" : ""}${res.slice(0, res.length - decimal.length)}${decimal.length > 0 ? "." : ""}${decimal}`; | |
if(res.indexOf(".") === -1){ | |
return res; | |
} | |
// remove trailing zeros | |
res = res.replace(/0*$/g, ""); | |
if(res.slice(-1)==="."){ | |
res = res.slice(0, -1); | |
} | |
return res; | |
} | |
add(num) { | |
if (!(num instanceof BigNumber)) { | |
throw "Only support operations on BigNumber instance"; | |
} | |
if (this.isNegative !== num.isNegative) { | |
// -3 + 2 | |
if (this.isNegative) { | |
return num.minus(new BigNumber(this.num,this.decimalPos,false)); | |
} else { | |
// 3 + -2 | |
return this.minus(new BigNumber(num.num,num.decimalPos,false)); | |
} | |
} | |
// 3 + 2 or -3 + -2 => -(3 + 2) | |
var isNegative = this.isNegative; | |
var [n1,n2] = alignArrayNumbers(this.num, this.decimalPos, num.num, num.decimalPos); | |
var res = arrayAdd(n1, n2); | |
return new BigNumber(res,Math.max(this.decimalPos, num.decimalPos),isNegative); | |
} | |
minus(num) { | |
if (!(num instanceof BigNumber)) { | |
throw "Only support operations on BigNumber instance"; | |
} | |
if (this.isNegative !== num.isNegative) { | |
// -3 - 2 => - (3+2) | |
if (this.isNegative) { | |
var res = num.add(new BigNumber(this.num,this.decimalPos,false)); | |
res.isNegative = true; | |
return res; | |
} else { | |
// 3 - -2 => 3+2 | |
var res = this.add(new BigNumber(num.num,num.decimalPos,false)); | |
return res; | |
} | |
} | |
// now we dealing with -3 - -2 | |
if (this.isNegative) { | |
var n1 = new BigNumber(this); | |
n1.isNegative = false; | |
var n2 = new BigNumber(num); | |
n2.isNegative = false; | |
return n2.minus(n1); | |
} | |
// now we dealing with 3 - 2 | |
var [n1,n2] = alignArrayNumbers(this.num, this.decimalPos, num.num, num.decimalPos); | |
var res = arraySubtract(n1, n2); | |
return new BigNumber(res,Math.max(this.decimalPos, num.decimalPos),res.isNegative); | |
} | |
multiply(num) { | |
if (!(num instanceof BigNumber)) { | |
throw "Only support operations on BigNumber instance"; | |
} | |
var [n1,n2] = [this.num, num.num]; | |
var res = trimLeadingZeros(arrayMultiply(n1, n2)); | |
return new BigNumber(res,this.decimalPos + num.decimalPos,this.isNegative !== num.isNegative); | |
} | |
divide(num) { | |
if (!(num instanceof BigNumber)) { | |
throw "Only support operations on BigNumber instance"; | |
} | |
var [n1,n2] = [this.num, num.num]; | |
var res = arrayDivide(n1, n2, 10 - Math.abs(this.decimalPos - num.decimalPos)); | |
return new BigNumber(trimLeadingZeros(res),Math.abs(this.decimalPos - num.decimalPos) + res.decimalPos,this.isNegative !== num.isNegative); | |
} | |
} | |
} | |
)(); | |
console.group("add"); | |
console.assert(new BigNumber(0.1).add(new BigNumber(0.2)).toString() === "0.3"); | |
console.assert(new BigNumber(-0.1).add(new BigNumber(-0.2)).toString() === "-0.3"); | |
console.assert(new BigNumber(0).add(new BigNumber(0.2)).toString() === "0.2"); | |
console.assert(new BigNumber(0).add(new BigNumber(0)).toString() === "0"); | |
console.assert(new BigNumber("1234567890987654321").add(new BigNumber("1234567890987654321")).toString() === "2469135781975308642"); | |
console.assert(new BigNumber("-1234567890987654321").add(new BigNumber("1234567890987654321")).toString() === "0"); | |
console.assert(new BigNumber("-1234567890987654321").add(new BigNumber("-1234567890987654321")).toString() === "-2469135781975308642"); | |
console.assert(new BigNumber("9999").add(new BigNumber("1")).toString() === "10000"); | |
console.groupEnd("add"); | |
console.group("minus"); | |
console.assert(new BigNumber(0.1).minus(new BigNumber(0.2)).toString() === "-0.1"); | |
console.assert(new BigNumber(0).minus(new BigNumber(0.2)).toString() === "-0.2"); | |
console.assert(new BigNumber(0).minus(new BigNumber(0)).toString() === "0"); | |
console.assert(new BigNumber("1234567890987654321").minus(new BigNumber("1234567890987654321")).toString() === "0"); | |
console.assert(new BigNumber("-1234567890987654321").minus(new BigNumber("1234567890987654321")).toString() === "-2469135781975308642"); | |
console.assert(new BigNumber("-1234567890987654321").minus(new BigNumber("-1234567890987654321")).toString() === "0"); | |
console.assert(new BigNumber("10000").minus(new BigNumber("1")).toString() === "9999"); | |
console.groupEnd("minus"); | |
console.group("multiply"); | |
console.assert(new BigNumber(0.1).multiply(new BigNumber(0.2)).toString() === "0.02"); | |
console.assert(new BigNumber(0).multiply(new BigNumber(0.2)).toString() === "0"); | |
console.assert(new BigNumber(0).multiply(new BigNumber(0)).toString() === "0"); | |
console.assert(new BigNumber("1234567890987654321").multiply(new BigNumber("1234567890987654321")).toString() === "1524157877457704723228166437789971041"); | |
console.assert(new BigNumber("-1234567890987654321").multiply(new BigNumber("1234567890987654321")).toString() === "-1524157877457704723228166437789971041"); | |
console.assert(new BigNumber("-1234567890987654321").multiply(new BigNumber("-1234567890987654321")).toString() === "1524157877457704723228166437789971041"); | |
console.groupEnd("multiply"); | |
console.group("divide"); | |
console.assert(new BigNumber(0.1).divide(new BigNumber(0.2)).toString() === "0.5"); | |
console.assert(new BigNumber(0).divide(new BigNumber(0.2)).toString() === "0"); | |
try{ | |
console.assert(new BigNumber(0).divide(new BigNumber(0)).toString() === "0"); | |
}catch(e){ | |
console.assert(e === "divide by 0"); | |
} | |
console.assert(new BigNumber("1234567890987654321").divide(new BigNumber("1234567890987654321")).toString() === "1"); | |
console.assert(new BigNumber("-1234567890987654321").divide(new BigNumber("1234567890987654321")).toString() === "-1"); | |
console.assert(new BigNumber("-1234567890987654321").divide(new BigNumber("-1234567890987654321")).toString() === "1"); | |
console.groupEnd("divide"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment