UNPKG

@toruslabs/ffjavascript

Version:

Finite Field Library in Javascript

149 lines (121 loc) 3.93 kB
'use strict'; /* Copyright 2018 0kims association. This file is part of snarkjs. snarkjs is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. snarkjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with snarkjs. If not, see <https://www.gnu.org/licenses/>. */ /* This library does operations on polynomials with coefficients in a field F. A polynomial P(x) = p0 + p1 * x + p2 * x^2 + ... + pn * x^n is represented by the array [ p0, p1, p2, ... , pn ]. */ class FFT { constructor(G, F, opMulGF) { this.F = F; this.G = G; this.opMulGF = opMulGF; let rem = F.sqrt_t || F.t; let s = F.sqrt_s || F.s; let nqr = F.one; while (F.eq(F.pow(nqr, F.half), F.one)) nqr = F.add(nqr, F.one); this.w = new Array(s + 1); this.wi = new Array(s + 1); this.w[s] = this.F.pow(nqr, rem); this.wi[s] = this.F.inv(this.w[s]); let n = s - 1; while (n >= 0) { this.w[n] = this.F.square(this.w[n + 1]); this.wi[n] = this.F.square(this.wi[n + 1]); n--; } this.roots = []; /* for (let i=0; i<16; i++) { let r = this.F.one; n = 1 << i; const rootsi = new Array(n); for (let j=0; j<n; j++) { rootsi[j] = r; r = this.F.mul(r, this.w[i]); } this.roots.push(rootsi); } */ this._setRoots(Math.min(s, 15)); } _setRoots(n) { for (let i = n; i >= 0 && !this.roots[i]; i--) { let r = this.F.one; const nroots = 1 << i; const rootsi = new Array(nroots); for (let j = 0; j < nroots; j++) { rootsi[j] = r; r = this.F.mul(r, this.w[i]); } this.roots[i] = rootsi; } } fft(p) { if (p.length <= 1) return p; const bits = log2(p.length - 1) + 1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); return res; } ifft(p) { if (p.length <= 1) return p; const bits = log2(p.length - 1) + 1; this._setRoots(bits); const m = 1 << bits; if (p.length != m) { throw new Error("Size must be multiple of 2"); } const res = __fft(this, p, bits, 0, 1); const twoinvm = this.F.inv(this.F.mulScalar(this.F.one, m)); const resn = new Array(m); for (let i = 0; i < m; i++) { resn[i] = this.opMulGF(res[(m - i) % m], twoinvm); } return resn; } } function log2(V) { return ( ((V & 0xffff0000) !== 0 ? ((V &= 0xffff0000), 16) : 0) | ((V & 0xff00ff00) !== 0 ? ((V &= 0xff00ff00), 8) : 0) | ((V & 0xf0f0f0f0) !== 0 ? ((V &= 0xf0f0f0f0), 4) : 0) | ((V & 0xcccccccc) !== 0 ? ((V &= 0xcccccccc), 2) : 0) | ((V & 0xaaaaaaaa) !== 0) ); } function __fft(PF, pall, bits, offset, step) { const n = 1 << bits; if (n == 1) { return [pall[offset]]; } else if (n == 2) { return [PF.G.add(pall[offset], pall[offset + step]), PF.G.sub(pall[offset], pall[offset + step])]; } const ndiv2 = n >> 1; const p1 = __fft(PF, pall, bits - 1, offset, step * 2); const p2 = __fft(PF, pall, bits - 1, offset + step, step * 2); const out = new Array(n); for (let i = 0; i < ndiv2; i++) { out[i] = PF.G.add(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); out[i + ndiv2] = PF.G.sub(p1[i], PF.opMulGF(p2[i], PF.roots[bits][i])); } return out; } module.exports = FFT;