UNPKG

bigint-gcd

Version:

greater common divisor (gcd) of two BigInt values using Lehmer's GCD algorithm

147 lines (138 loc) 4.29 kB
export function u64gcd(a:u64, b:u64):u64 { if (b !== 0) { do { const r = a % b; a = b; b = r; } while (b !== 0); } return a; } export function u64gcdext(a:u64, b:u64):u64 { let A = i64(1); let B = i64(0); let C = i64(0); let D = i64(1); if (b !== 0) { do { const q = a / b; const b1 = a - q * b; a = b; b = b1; const C1 = i64(A - i64(q * C)); const D1 = i64(B - i64(q * D)); A = C; B = D; C = C1; D = D1; } while (b !== 0); } gA = A; gB = B; return a; } export function helper(x:u64, xlo:u64, y:u64, ylo:u64, lobits:i32):i32 { // computes the transformation matrix, which is the product of all {{0, 1}, {1, -q_i}} matrices, // where q_i are the quotients produced by Euclidean algorithm for any pair of integers (a, b), // where a within [x, x + 1] and b within [y, y + 1] // 2x2-matrix transformation matrix of (x_initial, y_initial) into (x, y): let A = i64(1); let B = i64(0); let C = i64(0); let D = i64(1); let i = 0; let bits = 0; if (u64(y) != u64(-1) && u64(x) != u64(-1)) { // overflow do { // switch from a matrix to pairs of (xmin,xmax) and (ymin,ymax): // any pair of initial integers looks like: (x_initial + alpha, y_initial + beta), where 0 <= alpha < 1 and 0 <= beta < 1 // if we multiply transformation by it we get (x + alpha * A + beta * B, y + alpha * C + beta * D) // as A,B and C,D have different signs, and the signs are changed after every Euclidean step, then: //console.assert(sign(A) !== sign(B) && sign(C) !== sign(D)); //console.assert(sign(A) !== sign(C) && sign(B) !== sign(D)); let xmin = u64(x + B); let xmax = u64(x + A); let ymin = u64(y + C); let ymax = u64(y + D); if ((i & 1) != 0) { const xmin0 = xmin; xmin = xmax; xmax = xmin0; const ymin0 = ymin; ymin = ymax; ymax = ymin0; } do { const q = u64(xmin / ymax); // The quotient for any pair (x,y) is within floor(xmin / ymax) and floor(xmax / ymin) as x > 0 and y > 0 // So we need to check that u64(xmax / ymin) == q if q === u64(xmax / ymin): // 0 <= xmax - q * ymin < ymin if (u64(xmax - u64(q * ymin)) >= u64(ymin)) { // not same quotient break; } // continue Euclidean step: i = i32(i + 1); const ymin1 = u64(xmin - u64(q * ymax)); const ymax1 = u64(xmax - u64(q * ymin)); const y1 = u64(x - u64(q * y)); xmin = ymin; xmax = ymax; x = y; ymin = ymin1; ymax = ymax1; y = y1; //console.debug(q); } while (true); // switch back to the matrix: A = i64(xmax - x); B = i64(xmin - x); C = i64(ymin - y); D = i64(ymax - y); if ((i & 1) != 0) { const A0 = A; A = B; B = A0; const C0 = C; C = D; D = C0; } // add more bits from xlo and ylo: bits = i32(i64.clz(i64(u64((u64(x + A) > u64(x + B) ? u64(x + A) : u64(x + B)))))); // xmax ? bits = i32(lobits) < i32(bits) ? lobits : bits; if (i32(bits) != i32(0)) { const s = i32(lobits - bits); const xlo1 = u64(u64(xlo) >>> u64(s)); const ylo1 = u64(u64(ylo) >>> u64(s)); xlo = u64(xlo - u64(xlo1 << u64(s))); ylo = u64(ylo - u64(ylo1 << u64(s))); x = u64(u64(u64(A * xlo1) + u64(B * ylo1)) + u64(x << u64(bits))); y = u64(u64(u64(C * xlo1) + u64(D * ylo1)) + u64(y << u64(bits))); lobits = s; } } while (i32(bits) != i32(0)); } // AssemblyScript does not support multi-value return. // Performance of it is similar to the used method. Perhaps it would be faster to use the memory. gA = A; gB = B; gC = C; gD = D; return 0; } let gA = i64(0); let gB = i64(0); let gC = i64(0); let gD = i64(0); export function A():i64 { return gA; } export function B():i64 { return gB; } export function C():i64 { return gC; } export function D():i64 { return gD; }