/** * Binary Calculator (BC) Arbitrary Precision Mathematics Lib v0.10 (LGPL) * Copy of libbcmath included in PHP5 src * supports bcadd, bcsub, bcmul, bcdiv, bccomp, bcscale, and new function bcround(val, precision) * See PHP Manual for parameters.. functions work identical to the PHP5 funcions * Feel free to use how-ever you want, just email any bug-fixes/improvements to the sourceforge project: * https://sourceforge.net/projects/bcmath-js/ * * Ported from the PHP5 bcmath extension source code, * which uses the libbcmath package... * Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc. * Copyright (C) 2000 Philip A. Nelson * The Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA. * e-mail: philnelson@acm.org * us-mail: Philip A. Nelson * Computer Science Department, 9062 * Western Washington University * Bellingham, WA 98226-9062 */ interface BCNum { n_sign: string | null; n_len: number | null; n_scale: number | null; n_value: number[] | null; toString(): string; setScale(scale: number): BCNum; } type BCNumValue = string | number; const libbcmath = { PLUS: "+" as const, MINUS: "-" as const, BASE: 10, scale: 0, bc_num: function (this: BCNum): void { this.n_sign = null; this.n_len = null; this.n_scale = null; this.n_value = null; this.toString = function (): string { let b: string, a: string; a = this.n_value!.join(""); b = (this.n_sign === libbcmath.PLUS ? "" : this.n_sign) + a.substr(0, this.n_len!); if (this.n_scale! > 0) { b += "." + a.substr(this.n_len!, this.n_scale!); } return b; }; this.setScale = function (a: number): BCNum { while (this.n_scale! < a) { this.n_value!.push(0); this.n_scale!++; } while (this.n_scale! > a) { this.n_value!.pop(); this.n_scale!--; } return this; }; } as any as { new (): BCNum }, bc_new_num: function (b: number, c: number): BCNum { const a = new libbcmath.bc_num(); a.n_sign = libbcmath.PLUS; a.n_len = b; a.n_scale = c; a.n_value = libbcmath.safe_emalloc(1, b + c, 0); libbcmath.memset(a.n_value, 0, 0, b + c); return a; }, safe_emalloc: function (c: number, b: number, a: number): number[] { return Array(c * b + a); }, bc_init_num: function (): BCNum { return libbcmath.bc_new_num(1, 0); }, _bc_rm_leading_zeros: function (a: BCNum): void { while (a.n_value![0] === 0 && a.n_len! > 1) { a.n_value!.shift(); a.n_len!--; } }, php_str2num: function (b: string): BCNum { const a = b.indexOf("."); if (a === -1) { return libbcmath.bc_str2num(b, 0); } else { return libbcmath.bc_str2num(b, b.length - a); } }, CH_VAL: function (a: string): number { return parseInt(a, 10); }, BCD_CHAR: function (a: number): string { return a + "0"; }, isdigit: function (a: string): boolean { return !isNaN(parseInt(a, 10)); }, bc_str2num: function (h: string, c: number): BCNum { const g = h.split(""); let a = 0; let b = 0; let e = 0; let i = false; if (g[a] === "+" || g[a] === "-") { a++; } while (g[a] === "0") { a++; } while (g[a] && !isNaN(parseInt(g[a], 10))) { a++; b++; } if (g[a] === ".") { a++; } while (g[a] && !isNaN(parseInt(g[a], 10))) { a++; e++; } if (g[a] || b + e === 0) { return libbcmath.bc_init_num(); } e = libbcmath.MIN(e, c); if (b === 0) { i = true; b = 1; } const f = libbcmath.bc_new_num(b, e); a = 0; if (g[a] === "-") { f.n_sign = libbcmath.MINUS; a++; } else { f.n_sign = libbcmath.PLUS; if (g[a] === "+") { a++; } } while (g[a] === "0") { a++; } let d = 0; if (i) { f.n_value![d++] = 0; b = 0; } for (; b > 0; b--) { f.n_value![d++] = libbcmath.CH_VAL(g[a++]); } if (e > 0) { a++; for (; e > 0; e--) { f.n_value![d++] = libbcmath.CH_VAL(g[a++]); } } return f; }, cint: function (b?: number): number { if (typeof b === "undefined") { b = 0; } const a = parseInt(b.toString(), 10); if (isNaN(a)) { return 0; } return a; }, MIN: function (d: number, c: number): number { return d > c ? c : d; }, MAX: function (d: number, c: number): number { return d > c ? d : c; }, ODD: function (b: number): number { return b & 1; }, memset: function (d: number[], e: number, c: number, a: number): void { for (let b = 0; b < a; b++) { d[e + b] = c; } }, memcpy: function ( b: number[], f: number, e: number[], d: number, a: number ): boolean { for (let c = 0; c < a; c++) { b[f + c] = e[d + c]; } return true; }, bc_is_zero: function (a: BCNum): boolean { let b = a.n_len! + a.n_scale!; let c = 0; while (b > 0 && a.n_value![c++] === 0) { b--; } return b === 0; }, bc_out_of_memory: function (): never { throw new Error("(BC) Out of memory"); }, bc_add: function (f: BCNum, d: BCNum, c: number): BCNum { let e: BCNum; if (f.n_sign === d.n_sign) { e = libbcmath._bc_do_add(f, d, c); e.n_sign = f.n_sign; } else { const b = libbcmath._bc_do_compare(f, d, false, false); switch (b) { case -1: e = libbcmath._bc_do_sub(d, f, c); e.n_sign = d.n_sign; break; case 0: const a = libbcmath.MAX(c, libbcmath.MAX(f.n_scale!, d.n_scale!)); e = libbcmath.bc_new_num(1, a); libbcmath.memset(e.n_value!, 0, 0, a + 1); break; case 1: e = libbcmath._bc_do_sub(f, d, c); e.n_sign = f.n_sign; break; default: e = libbcmath.bc_init_num(); } } return e; }, bc_compare: function (b: BCNum, a: BCNum): number { return libbcmath._bc_do_compare(b, a, true, false); }, _bc_do_compare: function ( e: BCNum, d: BCNum, c: boolean, b: boolean ): number { if (c && e.n_sign !== d.n_sign) { if (e.n_sign === libbcmath.PLUS) { return 1; } else { return -1; } } if (e.n_len !== d.n_len) { if (e.n_len! > d.n_len!) { if (!c || e.n_sign === libbcmath.PLUS) { return 1; } else { return -1; } } else { if (!c || e.n_sign === libbcmath.PLUS) { return -1; } else { return 1; } } } let f = e.n_len! + Math.min(e.n_scale!, d.n_scale!); let g = 0; let a = 0; while (f > 0 && e.n_value![g] === d.n_value![a]) { g++; a++; f--; } if (b && f === 1 && e.n_scale === d.n_scale) { return 0; } if (f !== 0) { if (e.n_value![g] > d.n_value![a]) { if (!c || e.n_sign === libbcmath.PLUS) { return 1; } else { return -1; } } else { if (!c || e.n_sign === libbcmath.PLUS) { return -1; } else { return 1; } } } if (e.n_scale !== d.n_scale) { if (e.n_scale! > d.n_scale!) { for (f = e.n_scale! - d.n_scale!; f > 0; f--) { if (e.n_value![g++] !== 0) { if (!c || e.n_sign === libbcmath.PLUS) { return 1; } else { return -1; } } } } else { for (f = d.n_scale! - e.n_scale!; f > 0; f--) { if (d.n_value![a++] !== 0) { if (!c || e.n_sign === libbcmath.PLUS) { return -1; } else { return 1; } } } } } return 0; }, _one_mult: function ( d: number[], e: number, i: number, f: number, j: number[], c: number ): void { if (f === 0) { libbcmath.memset(j, 0, 0, i); } else { if (f === 1) { libbcmath.memcpy(j, c, d, e, i); } else { let b = e + i - 1; let a = c + i - 1; let h = 0; while (i-- > 0) { const g = d[b--] * f + h; j[a--] = g % libbcmath.BASE; h = Math.floor(g / libbcmath.BASE); } if (h !== 0) { j[a] = h; } } } }, bc_divide: function (l: BCNum, k: BCNum, z: number): BCNum | -1 { if (libbcmath.bc_is_zero(k)) { return -1; } if (libbcmath.bc_is_zero(l)) { return libbcmath.bc_new_num(1, z); } let w: BCNum | undefined; if (k.n_scale === 0) { if (k.n_len === 1 && k.n_value![0] === 1) { w = libbcmath.bc_new_num(l.n_len!, z); w.n_sign = l.n_sign === k.n_sign ? libbcmath.PLUS : libbcmath.MINUS; libbcmath.memset(w.n_value!, l.n_len!, 0, z); libbcmath.memcpy( w.n_value!, 0, l.n_value!, 0, l.n_len! + libbcmath.MIN(l.n_scale!, z) ); return w; } } let s = k.n_scale!; let h = k.n_len! + s - 1; while (s > 0 && k.n_value![h--] === 0) { s--; } const j = l.n_len! + s; const u = l.n_scale! - s; let a: number; if (u < z) { a = z - u; } else { a = 0; } const c = libbcmath.safe_emalloc(1, l.n_len! + l.n_scale!, a + 2); if (c === null) { libbcmath.bc_out_of_memory(); } libbcmath.memset(c, 0, 0, l.n_len! + l.n_scale! + a + 2); libbcmath.memcpy(c, 1, l.n_value!, 0, l.n_len! + l.n_scale!); let i = k.n_len! + s; const b = libbcmath.safe_emalloc(1, i, 1); if (b === null) { libbcmath.bc_out_of_memory(); } libbcmath.memcpy(b, 0, k.n_value!, 0, i); b[i] = 0; h = 0; while (b[h] === 0) { h++; i--; } let q: number; let n: boolean; if (i > j + z) { q = z + 1; n = true; } else { n = false; if (i > j) { q = z + 1; } else { q = j - i + z + 1; } } w = libbcmath.bc_new_num(q - z, z); libbcmath.memset(w.n_value!, 0, 0, q); const e = libbcmath.safe_emalloc(1, i, 1); if (e === null) { libbcmath.bc_out_of_memory(); } if (!n) { const f = Math.floor(10 / (k.n_value![h] + 1)); if (f !== 1) { libbcmath._one_mult(c, 0, j + u + a + 1, f, c, 0); libbcmath._one_mult(k.n_value!, h, i, f, k.n_value!, h); } let r = 0; let x: number; if (i > j) { x = i - j; } else { x = 0; } while (r <= j + z - i) { let m: number; if (k.n_value![h] === c[r]) { m = 9; } else { m = Math.floor((c[r] * 10 + c[r + 1]) / k.n_value![h]); } if ( k.n_value![h + 1] * m > (c[r] * 10 + c[r + 1] - k.n_value![h] * m) * 10 + c[r + 2] ) { m--; if ( k.n_value![h + 1] * m > (c[r] * 10 + c[r + 1] - k.n_value![h] * m) * 10 + c[r + 2] ) { m--; } } let t = 0; if (m !== 0) { e[0] = 0; libbcmath._one_mult(k.n_value!, h, i, m, e, 1); let p = r + i; let o = i; for (let g = 0; g < i + 1; g++) { let A: number; if (o < 0) { A = c[p] - 0 - t; } else { A = c[p] - e[o--] - t; } if (A < 0) { A += 10; t = 1; } else { t = 0; } c[p--] = A; } } if (t === 1) { m--; let p = r + i; let o = i - 1; let v = 0; for (let g = 0; g < i; g++) { let A: number; if (o < 0) { A = c[p] + 0 + v; } else { A = c[p] + k.n_value![o--] + v; } if (A > 9) { A -= 10; v = 1; } else { v = 0; } c[p--] = A; } if (v === 1) { c[p] = (c[p] + 1) % 10; } } w.n_value![x++] = m; r++; } } w.n_sign = l.n_sign === k.n_sign ? libbcmath.PLUS : libbcmath.MINUS; if (libbcmath.bc_is_zero(w)) { w.n_sign = libbcmath.PLUS; } libbcmath._bc_rm_leading_zeros(w); return w; }, _bc_do_add: function (h: BCNum, g: BCNum, i: number): BCNum { const c = libbcmath.MAX(h.n_scale!, g.n_scale!); const b = libbcmath.MAX(h.n_len!, g.n_len!) + 1; const f = libbcmath.bc_new_num(b, libbcmath.MAX(c, i)); let l = h.n_scale!; let a = g.n_scale!; let k = h.n_len! + l - 1; let e = g.n_len! + a - 1; let j = c + b - 1; if (l !== a) { if (l > a) { while (l > a) { f.n_value![j--] = h.n_value![k--]; l--; } } else { while (a > l) { f.n_value![j--] = g.n_value![e--]; a--; } } } l += h.n_len!; a += g.n_len!; let m = 0; while (l > 0 && a > 0) { let d = h.n_value![k--] + g.n_value![e--] + m; if (d >= libbcmath.BASE) { m = 1; d -= libbcmath.BASE; } else { m = 0; } f.n_value![j] = d; j--; l--; a--; } if (l === 0) { while (a-- > 0) { let d = g.n_value![e--] + m; if (d >= libbcmath.BASE) { m = 1; d -= libbcmath.BASE; } else { m = 0; } f.n_value![j--] = d; } } else { while (l-- > 0) { let d = h.n_value![k--] + m; if (d >= libbcmath.BASE) { m = 1; d -= libbcmath.BASE; } else { m = 0; } f.n_value![j--] = d; } } if (m === 1) { f.n_value![j] += 1; } libbcmath._bc_rm_leading_zeros(f); return f; }, _bc_do_sub: function (h: BCNum, g: BCNum, i: number): BCNum { const a = libbcmath.MAX(h.n_len!, g.n_len!); const m = libbcmath.MAX(h.n_scale!, g.n_scale!); const f = libbcmath.MIN(h.n_len!, g.n_len!); const d = libbcmath.MIN(h.n_scale!, g.n_scale!); const l = libbcmath.bc_new_num(a, libbcmath.MAX(m, i)); let k = h.n_len! + h.n_scale! - 1; let c = g.n_len! + g.n_scale! - 1; let n = a + m - 1; let j = 0; if (h.n_scale !== d) { for (let e = h.n_scale! - d; e > 0; e--) { l.n_value![n--] = h.n_value![k--]; } } else { for (let e = g.n_scale! - d; e > 0; e--) { let b = 0 - g.n_value![c--] - j; if (b < 0) { b += libbcmath.BASE; j = 1; } else { j = 0; } l.n_value![n--] = b; } } for (let e = 0; e < f + d; e++) { let b = h.n_value![k--] - g.n_value![c--] - j; if (b < 0) { b += libbcmath.BASE; j = 1; } else { j = 0; } l.n_value![n--] = b; } if (a !== f) { for (let e = a - f; e > 0; e--) { let b = h.n_value![k--] - j; if (b < 0) { b += libbcmath.BASE; j = 1; } else { j = 0; } l.n_value![n--] = b; } } libbcmath._bc_rm_leading_zeros(l); return l; }, MUL_BASE_DIGITS: 80, MUL_SMALL_DIGITS: 0, bc_multiply: function (f: BCNum, d: BCNum, h: number): BCNum { const b = f.n_len! + f.n_scale!; const a = d.n_len! + d.n_scale!; const g = f.n_scale! + d.n_scale!; const e = libbcmath.MIN( g, libbcmath.MAX(h, libbcmath.MAX(f.n_scale!, d.n_scale!)) ); const c = libbcmath._bc_rec_mul(f, b, d, a, g); c.n_sign = f.n_sign === d.n_sign ? libbcmath.PLUS : libbcmath.MINUS; c.n_len = a + b + 1 - g; c.n_scale = e; libbcmath._bc_rm_leading_zeros(c); if (libbcmath.bc_is_zero(c)) { c.n_sign = libbcmath.PLUS; } return c; }, new_sub_num: function (b: number, d: number, c: number[]): BCNum { const a = new libbcmath.bc_num(); a.n_sign = libbcmath.PLUS; a.n_len = b; a.n_scale = d; a.n_value = c; return a; }, _bc_simp_mul: function ( i: BCNum, b: number, h: BCNum, m: number, a: number ): BCNum { const d = b + m + 1; const j = libbcmath.bc_new_num(d, 0); const n = b - 1; const l = m - 1; let f = d - 1; let g = 0; for (let e = 0; e < d - 1; e++) { const k = n - libbcmath.MAX(0, e - m + 1); const c = l - libbcmath.MIN(e, m - 1); let kIdx = k; let cIdx = c; while (kIdx >= 0 && cIdx <= l) { g += i.n_value![kIdx--] * h.n_value![cIdx++]; } j.n_value![f--] = Math.floor(g % libbcmath.BASE); g = Math.floor(g / libbcmath.BASE); } j.n_value![f] = g; return j; }, _bc_shift_addsub: function ( b: BCNum, g: BCNum, a: number, d: boolean ): boolean { let e = g.n_len!; if (g.n_value![0] === 0) { e--; } if (!(b.n_len! + b.n_scale! >= a + e)) { throw new Error("len + scale < shift + count"); } let c = b.n_len! + b.n_scale! - a - 1; let h = g.n_len! - 1; let f = 0; if (d) { while (e--) { b.n_value![c] -= g.n_value![h--] + f; if (b.n_value![c] < 0) { f = 1; b.n_value![c--] += libbcmath.BASE; } else { f = 0; c--; } } while (f) { b.n_value![c] -= f; if (b.n_value![c] < 0) { b.n_value![c--] += libbcmath.BASE; } else { f = 0; } } } else { while (e--) { b.n_value![c] += g.n_value![h--] + f; if (b.n_value![c] > libbcmath.BASE - 1) { f = 1; b.n_value![c--] -= libbcmath.BASE; } else { f = 0; c--; } } while (f) { b.n_value![c] += f; if (b.n_value![c] > libbcmath.BASE - 1) { b.n_value![c--] -= libbcmath.BASE; } else { f = 0; } } } return true; }, _bc_rec_mul: function ( m: BCNum, i: number, l: BCNum, j: number, c: number ): BCNum { if ( i + j < libbcmath.MUL_BASE_DIGITS || i < libbcmath.MUL_SMALL_DIGITS || j < libbcmath.MUL_SMALL_DIGITS ) { return libbcmath._bc_simp_mul(m, i, l, j, c); } const o = Math.floor((libbcmath.MAX(i, j) + 1) / 2); let r: BCNum, s: BCNum; if (i < o) { r = libbcmath.bc_init_num(); s = libbcmath.new_sub_num(i, 0, m.n_value!); } else { r = libbcmath.new_sub_num(i - o, 0, m.n_value!); s = libbcmath.new_sub_num(o, 0, m.n_value!.slice(i - o)); } let g: BCNum, h: BCNum; if (j < o) { g = libbcmath.bc_init_num(); h = libbcmath.new_sub_num(j, 0, l.n_value!); } else { g = libbcmath.new_sub_num(j - o, 0, l.n_value!); h = libbcmath.new_sub_num(o, 0, l.n_value!.slice(j - o)); } libbcmath._bc_rm_leading_zeros(r); libbcmath._bc_rm_leading_zeros(s); const f = s.n_len!; libbcmath._bc_rm_leading_zeros(g); libbcmath._bc_rm_leading_zeros(h); const p = h.n_len!; const e = libbcmath.bc_is_zero(r) || libbcmath.bc_is_zero(g); const y = libbcmath.bc_sub(r, s, 0); const q = y.n_len!; const x = libbcmath.bc_sub(h, g, 0); const t = x.n_len!; let d: BCNum; if (e) { d = libbcmath.bc_init_num(); } else { d = libbcmath._bc_rec_mul(r, r.n_len!, g, g.n_len!, 0); } let b: BCNum; if (libbcmath.bc_is_zero(y) || libbcmath.bc_is_zero(x)) { b = libbcmath.bc_init_num(); } else { b = libbcmath._bc_rec_mul(y, q, x, t, 0); } let a: BCNum; if (libbcmath.bc_is_zero(s) || libbcmath.bc_is_zero(h)) { a = libbcmath.bc_init_num(); } else { a = libbcmath._bc_rec_mul(s, s.n_len!, h, h.n_len!, 0); } const w = i + j + 1; const k = libbcmath.bc_new_num(w, 0); if (!e) { libbcmath._bc_shift_addsub(k, d, 2 * o, false); libbcmath._bc_shift_addsub(k, d, o, false); } libbcmath._bc_shift_addsub(k, a, o, false); libbcmath._bc_shift_addsub(k, a, 0, false); libbcmath._bc_shift_addsub(k, b, o, y.n_sign !== x.n_sign); return k; }, bc_sub: function (e: BCNum, d: BCNum, c: number): BCNum { let f: BCNum; if (e.n_sign !== d.n_sign) { f = libbcmath._bc_do_add(e, d, c); f.n_sign = e.n_sign; } else { const b = libbcmath._bc_do_compare(e, d, false, false); switch (b) { case -1: f = libbcmath._bc_do_sub(d, e, c); f.n_sign = d.n_sign === libbcmath.PLUS ? libbcmath.MINUS : libbcmath.PLUS; break; case 0: const a = libbcmath.MAX(c, libbcmath.MAX(e.n_scale!, d.n_scale!)); f = libbcmath.bc_new_num(1, a); libbcmath.memset(f.n_value!, 0, 0, a + 1); break; case 1: f = libbcmath._bc_do_sub(e, d, c); f.n_sign = e.n_sign; break; default: f = libbcmath.bc_init_num(); } } return f; }, }; libbcmath.MUL_SMALL_DIGITS = libbcmath.MUL_BASE_DIGITS / 4; export function bcadd(b: BCNumValue, d: BCNumValue, f?: number): string { if (typeof f === "undefined") { f = libbcmath.scale; } f = f < 0 ? 0 : f; let e = libbcmath.php_str2num(b.toString()); let c = libbcmath.php_str2num(d.toString()); if (e.n_scale! > c.n_scale!) { c.setScale(e.n_scale!); } if (c.n_scale! > e.n_scale!) { e.setScale(c.n_scale!); } let a = libbcmath.bc_add(e, c, f); if (a.n_scale! > f) { a.n_scale = f; } return a.toString(); } export function bcsub(b: BCNumValue, d: BCNumValue, f?: number): string { if (typeof f === "undefined") { f = libbcmath.scale; } f = f < 0 ? 0 : f; let e = libbcmath.php_str2num(b.toString()); let c = libbcmath.php_str2num(d.toString()); if (e.n_scale! > c.n_scale!) { c.setScale(e.n_scale!); } if (c.n_scale! > e.n_scale!) { e.setScale(c.n_scale!); } let a = libbcmath.bc_sub(e, c, f); if (a.n_scale! > f) { a.n_scale = f; } return a.toString(); } export function bccomp(a: BCNumValue, c: BCNumValue, e?: number): number { if (typeof e === "undefined") { e = libbcmath.scale; } e = e < 0 ? 0 : e; const d = libbcmath.bc_str2num(a.toString(), e); const b = libbcmath.bc_str2num(c.toString(), e); return libbcmath.bc_compare(d, b); } export function bcscale(a: number): boolean { const parsedScale = parseInt(a.toString(), 10); if (isNaN(parsedScale)) { return false; } if (parsedScale < 0) { return false; } libbcmath.scale = parsedScale; return true; } export function bcdiv(b: BCNumValue, d: BCNumValue, f?: number): string { if (typeof f === "undefined") { f = libbcmath.scale; } f = f < 0 ? 0 : f; let e = libbcmath.php_str2num(b.toString()); let c = libbcmath.php_str2num(d.toString()); if (e.n_scale! > c.n_scale!) { c.setScale(e.n_scale!); } if (c.n_scale! > e.n_scale!) { e.setScale(c.n_scale!); } const a = libbcmath.bc_divide(e, c, f); if (a === -1) { throw new Error("(BC) Division by zero"); } if (a.n_scale! > f) { a.n_scale = f; } return a.toString(); } export function bcmul(b: BCNumValue, d: BCNumValue, f?: number): string { if (typeof f === "undefined") { f = libbcmath.scale; } f = f < 0 ? 0 : f; let e = libbcmath.php_str2num(b.toString()); let c = libbcmath.php_str2num(d.toString()); if (e.n_scale! > c.n_scale!) { c.setScale(e.n_scale!); } if (c.n_scale! > e.n_scale!) { e.setScale(c.n_scale!); } let a = libbcmath.bc_multiply(e, c, f); if (a.n_scale! > f) { a.n_scale = f; } return a.toString(); } export function bcround(d: BCNumValue, b: number): string { let a = "0." + Array(b + 1).join("0") + "5"; if (d.toString().substring(0, 1) === "-") { a = "-" + a; } const c = bcadd(d, a, b); return c; }