UNPKG

@toruslabs/ffjavascript

Version:

Finite Field Library in Javascript

114 lines (106 loc) 3.75 kB
'use strict'; var _defineProperty = require('@babel/runtime/helpers/defineProperty'); /* 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/>. */ class FFT { constructor(G, F, opMulGF) { var _F$sqrt_s; _defineProperty(this, "F", void 0); _defineProperty(this, "G", void 0); _defineProperty(this, "opMulGF", void 0); _defineProperty(this, "w", void 0); _defineProperty(this, "wi", void 0); _defineProperty(this, "roots", void 0); this.F = F; this.G = G; this.opMulGF = opMulGF; const rem = F.sqrt_t || F.t; const s = (_F$sqrt_s = F.sqrt_s) !== null && _F$sqrt_s !== void 0 ? _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 = []; 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 ? 1 : 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;