You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

531 lines
14 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. Copyright 2018 0kims association.
  3. This file is part of snarkjs.
  4. snarkjs is a free software: you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License as published by the
  6. Free Software Foundation, either version 3 of the License, or (at your option)
  7. any later version.
  8. snarkjs is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. more details.
  12. You should have received a copy of the GNU General Public License along with
  13. snarkjs. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. /*
  16. This library does operations on polynomials with coefficients in a field F.
  17. A polynomial P(x) = p0 + p1 * x + p2 * x^2 + ... + pn * x^n is represented
  18. by the array [ p0, p1, p2, ... , pn ].
  19. */
  20. const bigInt = require("./bigint.js");
  21. const assert = require("assert");
  22. class PolField {
  23. constructor (F) {
  24. this.F = F;
  25. const q = this.F.q;
  26. let rem = q.sub(bigInt(1));
  27. let s = 0;
  28. while (!rem.isOdd()) {
  29. s ++;
  30. rem = rem.shr(1);
  31. }
  32. const five = this.F.add(this.F.add(this.F.two, this.F.two), this.F.one);
  33. this.w = new Array(s+1);
  34. this.wi = new Array(s+1);
  35. this.w[s] = this.F.exp(five, rem);
  36. this.wi[s] = this.F.inverse(this.w[s]);
  37. let n=s-1;
  38. while (n>=0) {
  39. this.w[n] = this.F.square(this.w[n+1]);
  40. this.wi[n] = this.F.square(this.wi[n+1]);
  41. n--;
  42. }
  43. this.roots = [];
  44. /* for (let i=0; i<16; i++) {
  45. let r = this.F.one;
  46. n = 1 << i;
  47. const rootsi = new Array(n);
  48. for (let j=0; j<n; j++) {
  49. rootsi[j] = r;
  50. r = this.F.mul(r, this.w[i]);
  51. }
  52. this.roots.push(rootsi);
  53. }
  54. */
  55. this._setRoots(15);
  56. }
  57. _setRoots(n) {
  58. for (let i=n; (i>=0) && (!this.roots[i]); i--) {
  59. let r = this.F.one;
  60. const nroots = 1 << i;
  61. const rootsi = new Array(nroots);
  62. for (let j=0; j<nroots; j++) {
  63. rootsi[j] = r;
  64. r = this.F.mul(r, this.w[i]);
  65. }
  66. this.roots[i] = rootsi;
  67. }
  68. }
  69. add(a, b) {
  70. const m = Math.max(a.length, b.length);
  71. const res = new Array(m);
  72. for (let i=0; i<m; i++) {
  73. res[i] = this.F.add(a[i] || this.F.zero, b[i] || this.F.zero);
  74. }
  75. return this.reduce(res);
  76. }
  77. double(a) {
  78. return this.add(a,a);
  79. }
  80. sub(a, b) {
  81. const m = Math.max(a.length, b.length);
  82. const res = new Array(m);
  83. for (let i=0; i<m; i++) {
  84. res[i] = this.F.sub(a[i] || this.F.zero, b[i] || this.F.zero);
  85. }
  86. return this.reduce(res);
  87. }
  88. mulScalar(p, b) {
  89. if (this.F.isZero(b)) return [];
  90. if (this.F.equals(b, this.F.one)) return p;
  91. const res = new Array(p.length);
  92. for (let i=0; i<p.length; i++) {
  93. res[i] = this.F.mul(p[i], b);
  94. }
  95. return res;
  96. }
  97. mul(a, b) {
  98. if (a.length == 0) return [];
  99. if (b.length == 0) return [];
  100. if (a.length == 1) return this.mulScalar(b, a[0]);
  101. if (b.length == 1) return this.mulScalar(a, b[0]);
  102. if (b.length > a.length) {
  103. [b, a] = [a, b];
  104. }
  105. if ((b.length <= 2) || (b.length < log2(a.length))) {
  106. return this.mulNormal(a,b);
  107. } else {
  108. return this.mulFFT(a,b);
  109. }
  110. }
  111. mulNormal(a, b) {
  112. let res = [];
  113. b = this.affine(b);
  114. for (let i=0; i<b.length; i++) {
  115. res = this.add(res, this.scaleX(this.mulScalar(a, b[i]), i) );
  116. }
  117. return res;
  118. }
  119. mulFFT(a,b) {
  120. const longestN = Math.max(a.length, b.length);
  121. const bitsResult = log2(longestN-1)+2;
  122. this._setRoots(bitsResult);
  123. const m = 1 << bitsResult;
  124. const ea = this.extend(a,m);
  125. const eb = this.extend(b,m);
  126. const ta = __fft(this, ea, bitsResult, 0, 1, false);
  127. const tb = __fft(this, eb, bitsResult, 0, 1, false);
  128. const tres = new Array(m);
  129. for (let i=0; i<m; i++) {
  130. tres[i] = this.F.mul(ta[i], tb[i]);
  131. }
  132. const res = __fft(this, tres, bitsResult, 0, 1, true);
  133. const twoinvm = this.F.inverse( this.F.mulScalar(this.F.one, m) );
  134. const resn = new Array(m);
  135. for (let i=0; i<m; i++) {
  136. resn[i] = this.F.mul(res[(m-i)%m], twoinvm);
  137. }
  138. return this.reduce(this.affine(resn));
  139. }
  140. square(a) {
  141. return this.mul(a,a);
  142. }
  143. scaleX(p, n) {
  144. if (n==0) {
  145. return p;
  146. } else if (n>0) {
  147. const z = new Array(n).fill(this.F.zero);
  148. return z.concat(p);
  149. } else {
  150. if (-n >= p.length) return [];
  151. return p.slice(-n);
  152. }
  153. }
  154. eval2(p, x) {
  155. let v = this.F.zero;
  156. let ix = this.F.one;
  157. for (let i=0; i<p.length; i++) {
  158. v = this.F.add(v, this.F.mul(p[i], ix));
  159. ix = this.F.mul(ix, x);
  160. }
  161. return v;
  162. }
  163. eval(p,x) {
  164. const F = this.F;
  165. if (p.length == 0) return F.zero;
  166. const m = this._next2Power(p.length);
  167. const ep = this.extend(p, m);
  168. return _eval(ep, x, 0, 1, m);
  169. function _eval(p, x, offset, step, n) {
  170. if (n==1) return p[offset];
  171. const newX = F.square(x);
  172. const res= F.add(
  173. _eval(p, newX, offset, step << 1, n >> 1),
  174. F.mul(
  175. x,
  176. _eval(p, newX, offset+step , step << 1, n >> 1)));
  177. return res;
  178. }
  179. }
  180. lagrange(points) {
  181. let roots = [this.F.one];
  182. for (let i=0; i<points.length; i++) {
  183. roots = this.mul(roots, [this.F.neg(points[i][0]), this.F.one]);
  184. }
  185. let sum = [];
  186. for (let i=0; i<points.length; i++) {
  187. let mpol = this.ruffini(roots, points[i][0]);
  188. const factor =
  189. this.F.mul(
  190. this.F.inverse(this.eval(mpol, points[i][0])),
  191. points[i][1]);
  192. mpol = this.mulScalar(mpol, factor);
  193. sum = this.add(sum, mpol);
  194. }
  195. return sum;
  196. }
  197. fft(p) {
  198. if (p.length <= 1) return p;
  199. const bits = log2(p.length-1)+1;
  200. this._setRoots(bits);
  201. const m = 1 << bits;
  202. const ep = this.extend(p, m);
  203. const res = __fft(this, ep, bits, 0, 1);
  204. return res;
  205. }
  206. ifft(p) {
  207. if (p.length <= 1) return p;
  208. const bits = log2(p.length-1)+1;
  209. this._setRoots(bits);
  210. const m = 1 << bits;
  211. const ep = this.extend(p, m);
  212. const res = __fft(this, ep, bits, 0, 1);
  213. const twoinvm = this.F.inverse( this.F.mulScalar(this.F.one, m) );
  214. const resn = new Array(m);
  215. for (let i=0; i<m; i++) {
  216. resn[i] = this.F.mul(res[(m-i)%m], twoinvm);
  217. }
  218. return resn;
  219. }
  220. _fft(pall, bits, offset, step) {
  221. const n = 1 << bits;
  222. if (n==1) {
  223. return [ pall[offset] ];
  224. }
  225. const ndiv2 = n >> 1;
  226. const p1 = this._fft(pall, bits-1, offset, step*2);
  227. const p2 = this._fft(pall, bits-1, offset+step, step*2);
  228. const out = new Array(n);
  229. let m= this.F.one;
  230. for (let i=0; i<ndiv2; i++) {
  231. out[i] = this.F.add(p1[i], this.F.mul(m, p2[i]));
  232. out[i+ndiv2] = this.F.sub(p1[i], this.F.mul(m, p2[i]));
  233. m = this.F.mul(m, this.w[bits]);
  234. }
  235. return out;
  236. }
  237. extend(p, e) {
  238. if (e == p.length) return p;
  239. const z = new Array(e-p.length).fill(this.F.zero);
  240. return p.concat(z);
  241. }
  242. reduce(p) {
  243. if (p.length == 0) return p;
  244. if (! this.F.isZero(p[p.length-1]) ) return p;
  245. let i=p.length-1;
  246. while( i>0 && this.F.isZero(p[i]) ) i--;
  247. return p.slice(0, i+1);
  248. }
  249. affine(p) {
  250. for (let i=0; i<p.length; i++) {
  251. p[i] = this.F.affine(p[i]);
  252. }
  253. return p;
  254. }
  255. equals(a, b) {
  256. const pa = this.reduce(this.affine(a));
  257. const pb = this.reduce(this.affine(b));
  258. if (pa.length != pb.length) return false;
  259. for (let i=0; i<pb.length; i++) {
  260. if (!this.F.equals(pa[i], pb[i])) return false;
  261. }
  262. return true;
  263. }
  264. ruffini(p, r) {
  265. const res = new Array(p.length-1);
  266. res[res.length-1] = p[p.length-1];
  267. for (let i = res.length-2; i>=0; i--) {
  268. res[i] = this.F.add(this.F.mul(res[i+1], r), p[i+1]);
  269. }
  270. return res;
  271. }
  272. _next2Power(v) {
  273. v--;
  274. v |= v >> 1;
  275. v |= v >> 2;
  276. v |= v >> 4;
  277. v |= v >> 8;
  278. v |= v >> 16;
  279. v++;
  280. return v;
  281. }
  282. toString(p) {
  283. const ap = this.affine(p);
  284. let S = "";
  285. for (let i=ap.length-1; i>=0; i--) {
  286. if (!this.F.isZero(p[i])) {
  287. if (S!="") S += " + ";
  288. S = S + p[i].toString(10);
  289. if (i>0) {
  290. S = S + "x";
  291. if (i>1) {
  292. S = S + "^" +i;
  293. }
  294. }
  295. }
  296. }
  297. return S;
  298. }
  299. _reciprocal(p, bits) {
  300. const k = 1 << bits;
  301. if (k==1) {
  302. return [ this.F.inverse(p[0]) ];
  303. }
  304. const np = this.scaleX(p, -k/2);
  305. const q = this._reciprocal(np, bits-1);
  306. const a = this.scaleX(this.double(q), 3*k/2-2);
  307. const b = this.mul( this.square(q), p);
  308. return this.scaleX(this.sub(a,b), -(k-2));
  309. }
  310. // divides x^m / v
  311. _div2(m, v) {
  312. const kbits = log2(v.length-1)+1;
  313. const k = 1 << kbits;
  314. const scaleV = k - v.length;
  315. // rec = x^(k - 2) / v* x^scaleV =>
  316. // rec = x^(k-2-scaleV)/ v
  317. //
  318. // res = x^m/v = x^(m + (2*k-2 - scaleV) - (2*k-2 - scaleV)) /v =>
  319. // res = rec * x^(m - (2*k-2 - scaleV)) =>
  320. // res = rec * x^(m - 2*k + 2 + scaleV)
  321. const rec = this._reciprocal(this.scaleX(v, scaleV), kbits);
  322. const res = this.scaleX(rec, m - 2*k + 2 + scaleV);
  323. return res;
  324. }
  325. div(_u, _v) {
  326. if (_u.length < _v.length) return [];
  327. const kbits = log2(_v.length-1)+1;
  328. const k = 1 << kbits;
  329. const u = this.scaleX(_u, k-_v.length);
  330. const v = this.scaleX(_v, k-_v.length);
  331. const n = v.length-1;
  332. let m = u.length-1;
  333. const s = this._reciprocal(v, kbits);
  334. let t;
  335. if (m>2*n) {
  336. t = this.sub(this.scaleX([this.F.one], 2*n), this.mul(s, v));
  337. }
  338. let q = [];
  339. let rem = u;
  340. let us, ut;
  341. let finish = false;
  342. while (!finish) {
  343. us = this.mul(rem, s);
  344. q = this.add(q, this.scaleX(us, -2*n));
  345. if ( m > 2*n ) {
  346. ut = this.mul(rem, t);
  347. rem = this.scaleX(ut, -2*n);
  348. m = rem.length-1;
  349. } else {
  350. finish = true;
  351. }
  352. }
  353. return q;
  354. }
  355. // returns the ith nth-root of one
  356. oneRoot(n, i) {
  357. let nbits = log2(n-1)+1;
  358. let res = this.F.one;
  359. let r = i;
  360. assert(i<n);
  361. assert(1<<nbits === n);
  362. while (r>0) {
  363. if (r & 1 == 1) {
  364. res = this.F.mul(res, this.w[nbits]);
  365. }
  366. r = r >> 1;
  367. nbits --;
  368. }
  369. return res;
  370. }
  371. computeVanishingPolinomial(bits, t) {
  372. const m = 1 << bits;
  373. return this.F.sub(this.F.exp(t, bigInt(m)), this.F.one);
  374. }
  375. evaluateLagrangePolynomials(bits, t) {
  376. const m= 1 << bits;
  377. const tm = this.F.exp(t, bigInt(m));
  378. const u= new Array(m).fill(this.F.zero);
  379. this._setRoots(bits);
  380. const omega = this.w[bits];
  381. if (this.F.equals(tm, this.F.one)) {
  382. for (let i = 0; i < m; i++) {
  383. if (this.F.equals(this.roots[bits][0],t)) { // i.e., t equals omega^i
  384. u[i] = this.F.one;
  385. return u;
  386. }
  387. }
  388. }
  389. const z = this.F.sub(tm, this.F.one);
  390. // let l = this.F.mul(z, this.F.exp(this.F.twoinv, m));
  391. let l = this.F.mul(z, this.F.inverse(bigInt(m)));
  392. for (let i = 0; i < m; i++) {
  393. u[i] = this.F.mul(l, this.F.inverse(this.F.sub(t,this.roots[bits][i])));
  394. l = this.F.mul(l, omega);
  395. }
  396. return u;
  397. }
  398. log2(V) {
  399. return log2(V);
  400. }
  401. }
  402. function log2( V )
  403. {
  404. 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 ) );
  405. }
  406. function __fft(PF, pall, bits, offset, step) {
  407. const n = 1 << bits;
  408. if (n==1) {
  409. return [ pall[offset] ];
  410. } else if (n==2) {
  411. return [
  412. PF.F.add(pall[offset], pall[offset + step]),
  413. PF.F.sub(pall[offset], pall[offset + step])];
  414. }
  415. const ndiv2 = n >> 1;
  416. const p1 = __fft(PF, pall, bits-1, offset, step*2);
  417. const p2 = __fft(PF, pall, bits-1, offset+step, step*2);
  418. const out = new Array(n);
  419. for (let i=0; i<ndiv2; i++) {
  420. out[i] = PF.F.add(p1[i], PF.F.mul(PF.roots[bits][i], p2[i]));
  421. out[i+ndiv2] = PF.F.sub(p1[i], PF.F.mul(PF.roots[bits][i], p2[i]));
  422. }
  423. return out;
  424. }
  425. module.exports = PolField;