jampary
Version:
Modern arbitrary-precision arithmetic library using floating-point expansions.
238 lines (217 loc) • 6.48 kB
text/typescript
type float = number;
type int = number;
let max = Math.max;
let sqrt = Math.sqrt;
// type float = f64;
// type int = i32;
export type Vec = Array<float>;
const splitter = 134217729.; // = 2^27+1 for 64-bit float
let EE: float; // global variable for storing temp error
/* === Basic EFT bricks === */
// Algorithm 3.2
//@inline
function quickSum(a: float, b: float): float {
let s = a + b;
EE = b - (s - a);
return s;
}
// Algorithm 3.1
//@inline
function twoSum(a: float, b: float): float {
let s = a + b;
let t = s - b;
EE = (a - t) + (b - (s - t));
return s;
}
// Algorithm 3.3 with inlined 3.2
//@inline
function twoProd(a: float, b: float): float {
let t = splitter * a;
let ah = t + (a - t), al = a - ah;
t = splitter * b;
let bh = t + (b - t), bl = b - bh;
t = a * b;
EE = al * bl - (((t - ah * bh) - ah * bl) - al * bh);
return t;
}
/* === Vectorized helpers === */
// Merge two descending sorted arrs of floats into one sorted arr
//@inline
function vecMerge(A: Vec, Al: int, Ar: int, B: Vec, Bl: int, Br: int): Vec {
let len = Ar - Al + Br - Bl;
let R = new Array<float>(len);
let i = Al, j = Bl, k = 0;
while (k < len) {
if (i < Ar && j < Br) {
R[k++] = (Math.abs(A[i]) > Math.abs(B[j])) ? A[i++] : B[j++];
} else {
R[k++] = (i < Ar) ? A[i++] : B[j++];
}
}
return R;
}
// Merge and negate
//@inline
function vecMergeNeg(A: Vec, Al: int, Ar: int, B: Vec, Bl: int, Br: int): Vec {
let len = Ar - Al + Br - Bl;
let R = new Array<float>(len);
let i = Al, j = Bl, k = 0;
while (k < len) {
if (i < Ar && j < Br) {
R[k++] = (Math.abs(A[i]) > Math.abs(B[j])) ? A[i++] : -B[j++];
} else {
R[k++] = (i < Ar) ? A[i++] : -B[j++];
}
}
return R;
}
// Algorithm 3
//@inline
function vecSum(A: Vec): Vec {
let E = new Array<float>(A.length);
let s = A[A.length - 1];
for (let i = A.length - 2; i >= 0; --i) {
s = quickSum(A[i], s);
E[i + 1] = EE;
}
E[0] = s;
return E;
}
// Algorithm 6
function renormalize(A: Vec, outSize: int): Vec {
let E = vecSum(A);
let F = (outSize == E.length) ? E : new Array<float>(outSize);
//Algorithm 7
let e = E[0], j = 0;
for (let i = 1; i < E.length && j < outSize; ++i) {
F[j] = quickSum(e, E[i]);
e = EE;
if (e != 0.) {
++j;
} else {
e = F[j];
}
}
if (e != 0. && j < outSize) F[j] = e;
for (let i = j + 1; i < outSize; ++i) F[i] = 0.;//js-only
for (let i = 0; i < outSize - 1; ++i) {
//Algorithm 8
e = F[i];
for (let j = i; j < outSize - 1; ++j) {
F[j] = quickSum(e, F[j + 1]);
e = EE;
}
F[outSize - 1] = e;
}
return F;
}
/* === Arbitrary-precision operations === */
// Algorithm 4 (rounded)
export function add(A: Vec, B: Vec): Vec {
let n = max(A.length, B.length);
return renormalize(vecMerge(A, 0, A.length, B, 0, B.length), n);
}
// Negated Algorithm 4 (rounded)
export function sub(A: Vec, B: Vec): Vec {
let n = max(A.length, B.length);
return renormalize(vecMergeNeg(A, 0, A.length, B, 0, B.length), n);
}
// Algorithm 5 (rounded)
// 2do: revisit memory consumtion
export function mul(A: Vec, B: Vec): Vec {
let n = A.length, m = B.length, d = max(n, m);
let R = new Array<float>(d + 1);
let P = new Array<float>(d);
let E = new Array<float>(d * d);
let E2 = new Array<float>(d);
let S: Array<float>;
if (n < d) {
let T = A;
A = new Array<float>(d);
for (let i = 0; i < n; ++i) A[i] = T[i];
for (let i = n; i < d; ++i) A[i] = 0.;//js-only
}
if (m < d) {
let T = B;
B = new Array<float>(d);
for (let i = 0; i < m; ++i) B[i] = T[i];
for (let i = m; i < d; ++i) B[i] = 0.;//js-only
}
R[0] = twoProd(A[0], B[0]);
E[0] = EE;
R[d] = 0.;
for (let n = 1; n < d; ++n) {
for (let i = 0; i <= n; ++i) {
P[i] = twoProd(A[i], B[n - i]);
E2[i] = EE;
}
S = vecSum(vecMerge(P, 0, n + 1, E, 0, n * n));
R[n] = S[0];
E = vecMerge(S, 1, n * n + n + 1, E2, 0, n + 1);
}
for (let i = 1; i < d; ++i) R[d] += A[i] * B[d - i];
for (let i = 0; i < d * d; ++i) R[d] += E[i];
return renormalize(R, d);
}
// Algorithm 9 (rounded)
export function div(A: Vec, B: Vec): Vec {
let n = A.length, m = B.length, d = max(n, m);
let T: Array<float>;
let R = new Array<float>(d);
let Q = new Array<float>(d);
for (let i = 0; i < n; ++i) R[i] = A[i];
for (let i = n; i < d; ++i) R[i] = 0.;//js-only
for (let i = m; i < d; ++i) B[i] = 0.;//js-only
if (m < d) {
T = B;
B = new Array<float>(d);
for (let i = 0; i < m; ++i) B[i] = T[i];
for (let i = m; i < d; ++i) B[i] = 0.;//js-only
}
Q[0] = A[0] / B[0];
for (let i = 1; i < d; ++i) {
T = mul([Q[i - 1]], B);
R = renormalize(sub(R, T), d);
Q[i] = R[0] / B[0];
}
return renormalize(Q, d);
}
// // Algorithm 10 (not tested)
// export function div10(A: Vec, B: Vec): Vec {
// let n = A.length, m = B.length, d = max(n, m);
// for (let i = n; i < d; ++i) A[i] = 0.;//js-only
// for (let i = m; i < d; ++i) B[i] = 0.;//js-only
// let X: Array<float> = [1. / B[0]];
// for (let i = 0; i < 4; ++i) {
// X = mul(X, sub([4.], mul(X, B)));
// }
// return mul(A, X);
// }
// // Argorithm 11 (not tested)
// export function rsqrt(A: Vec): Vec {
// let X: Array<float> = [1. / sqrt(A[0])];
// for (let i = 0; i < 4; ++i) {
// X = mul(div(X, [2.]), sub([3.], mul(X, mul(X, A))));
// }
// return X;
// }
export function mandelbrot(maxIteration: int, width: float, height: float, i: float, j: float,
x0: float, y0: float, dx: float, dy: float): int {
let iteration: int = 0;
let x: Array<float> = [0.,0.,0.,0.]; let y: Array<float> = [0.,0.,0.,0.];
let xx: Array<float> = [0.,0.,0.,0.]; let xy: Array<float> = [0.,0.,0.,0.]; let yy: Array<float> = [0.,0.,0.,0.];
let tx: Array<float> = [x0,0.,0.,0.]; let ty: Array<float> = [y0,0.,0.,0.];
let tdx: Array<float> = [dx,0.,0.,0.]; let tdy: Array<float> = [dy,0.,0.,0.];
let I: Array<float> = [2.*i,0.,0.,0.]; let J: Array<float> = [2.*j,0.,0.,0.];
let W: Array<float> = [width,0.,0.,0.]; let H: Array<float> = [height,0.,0.,0.];
let cx: Array<float> = add(sub(tx, tdx), div(mul(tdx, I), W));
let cy: Array<float> = sub(add(ty, tdy), div(mul(tdy, J), H));
while (iteration++ < maxIteration && add(xx, yy)[0] < 4.) {
x = add(sub(xx, yy), cx);
y = add(add(xy, xy), cy);
xx = mul(x, x);
yy = mul(y, y);
xy = mul(x, y);
}
return iteration;
}