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.

572 lines
16 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. /*
  2. Copyright 2018 0KIMS association.
  3. This file is part of circom (Zero Knowledge Circuit Compiler).
  4. circom is a free software: you can redistribute it and/or modify it
  5. under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. circom is distributed in the hope that it will be useful, but WITHOUT
  9. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  10. or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
  11. License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with circom. If not, see <https://www.gnu.org/licenses/>.
  14. */
  15. /*
  16. // Number
  17. ///////////////
  18. N: a
  19. {
  20. t: "N",
  21. v: bigInt(a)
  22. }
  23. // Signal
  24. ///////////////
  25. {
  26. t: "S",
  27. sIdx: sIdx
  28. }
  29. // Linear Convination
  30. //////////////////
  31. LC: c1*s1 + c2*s2 + c3*s3
  32. {
  33. t: "LC",
  34. coefs: {
  35. s1: bigInt(c1),
  36. s2: bigInt(c2),
  37. s3: bigInt(c3)
  38. }
  39. }
  40. // Quadratic Expression
  41. //////////////////
  42. QEX: a*b + c WHERE a,b,c are LC
  43. {
  44. t: "QEX"
  45. a: { t: "LC", coefs: {...} },
  46. b: { t: "LC", coefs: {...} },
  47. c: { t: "LC", coefs: {...} }
  48. }
  49. NQ: Non quadratic expression
  50. {
  51. t: "NQ"
  52. }
  53. */
  54. /*
  55. + N LC QEX NQ
  56. N N LC QEX NQ
  57. LC LC LC QEX NQ
  58. QEX QEX QEX NQ NQ
  59. NQ NQ NQ NQ NQ
  60. * N LC QEX NQ
  61. N N LC QEX NQ
  62. LC LC QEX NQ NQ
  63. QEX QEX NQ NQ NQ
  64. NQ NQ NQ NQ NQ
  65. */
  66. const bigInt = require("big-integer");
  67. const utils = require("./utils");
  68. const sONE = 0;
  69. class LCAlgebra {
  70. constructor (aField) {
  71. const self = this;
  72. this.field= aField;
  73. [
  74. ["lt",2],
  75. ["leq",2],
  76. ["eq",2],
  77. ["neq",2],
  78. ["geq",2],
  79. ["gt",2],
  80. ["idiv",2],
  81. ["mod",2],
  82. ["band",2],
  83. ["bor",2],
  84. ["bxor",2],
  85. ["bnot",2],
  86. ["land",2],
  87. ["lor",2],
  88. ["lnot",2],
  89. ["shl",2],
  90. ["shr",2],
  91. ].forEach( (op) => {
  92. self._genNQOp(op[0], op[1]);
  93. });
  94. }
  95. _genNQOp(op, nOps) {
  96. const self=this;
  97. self[op] = function() {
  98. const operands = [];
  99. for (let i=0; i<nOps; i++) {
  100. if (typeof(arguments[i]) !== "object") throw new Error("Invalid operand type");
  101. if (arguments[i].t !== "N") return {t: "NQ"};
  102. operands.push(arguments[i].v);
  103. }
  104. return {
  105. t: "N",
  106. v: self.field[op](...operands)
  107. };
  108. };
  109. }
  110. _signal2lc(a) {
  111. if (a.t == "S") {
  112. const lc = {
  113. t: "LC",
  114. coefs: {}
  115. };
  116. lc.coefs[a.sIdx] = bigInt(1);
  117. return lc;
  118. } else {
  119. return a;
  120. }
  121. }
  122. _clone(a) {
  123. const res = {};
  124. res.t = a.t;
  125. if (a.t == "N") {
  126. res.v = a.v;
  127. } else if (a.t == "S") {
  128. res.sIdx = a.sIdx;
  129. } else if (a.t == "LC") {
  130. res.coefs = {};
  131. for (let k in a.coefs) {
  132. res.coefs[k] = a.coefs[k];
  133. }
  134. } else if (a.t == "QEX") {
  135. res.a = this._clone(a.a);
  136. res.b = this._clone(a.b);
  137. res.c = this._clone(a.c);
  138. }
  139. return res;
  140. }
  141. add(_a,_b) {
  142. const self = this;
  143. const a = self._signal2lc(_a);
  144. const b = self._signal2lc(_b);
  145. if (a.t == "NQ") return a;
  146. if (b.t == "NQ") return b;
  147. if (a.t == "N") {
  148. if (b.t == "N") {
  149. return add_N_N(a,b);
  150. } else if (b.t=="LC") {
  151. return add_LC_N(b,a);
  152. } else if (b.t=="QEX") {
  153. return add_QEX_N(b,a);
  154. } else {
  155. return { type: "NQ" };
  156. }
  157. } else if (a.t=="LC") {
  158. if (b.t == "N") {
  159. return add_LC_N(a,b);
  160. } else if (b.t=="LC") {
  161. return add_LC_LC(a,b);
  162. } else if (b.t=="QEX") {
  163. return add_QEX_LC(b,a);
  164. } else {
  165. return { t: "NQ" };
  166. }
  167. } else if (a.t=="QEX") {
  168. if (b.t == "N") {
  169. return add_QEX_N(a,b);
  170. } else if (b.t=="LC") {
  171. return add_QEX_LC(a,b);
  172. } else if (b.t=="QEX") {
  173. return { t: "NQ" };
  174. } else {
  175. return { t: "NQ" };
  176. }
  177. } else {
  178. return { t: "NQ" };
  179. }
  180. function add_N_N(a,b) {
  181. return {
  182. t: "N",
  183. v: self.field.add(a.v, b.v)
  184. };
  185. }
  186. function add_LC_N(a,b) {
  187. let res = self._clone(a);
  188. if (b.v.isZero()) return res;
  189. if (!utils.isDefined(res.coefs[sONE])) {
  190. res.coefs[sONE]= b.v;
  191. } else {
  192. res.coefs[sONE]= self.field.add(res.coefs[sONE], b.v);
  193. }
  194. return res;
  195. }
  196. function add_LC_LC(a,b) {
  197. let res = self._clone(a);
  198. for (let k in b.coefs) {
  199. if (!utils.isDefined(res.coefs[k])) {
  200. res.coefs[k]=b.coefs[k];
  201. } else {
  202. res.coefs[k]= self.field.add(res.coefs[k], b.coefs[k]);
  203. }
  204. }
  205. return res;
  206. }
  207. function add_QEX_N(a,b) {
  208. let res = self._clone(a);
  209. res.c = add_LC_N(res.c, b);
  210. return res;
  211. }
  212. function add_QEX_LC(a,b) {
  213. let res = self._clone(a);
  214. res.c = add_LC_LC(res.c, b);
  215. return res;
  216. }
  217. }
  218. mul(_a,_b) {
  219. const self = this;
  220. const a = self._signal2lc(_a);
  221. const b = self._signal2lc(_b);
  222. if (a.t == "NQ") return a;
  223. if (b.t == "NQ") return b;
  224. if (a.t == "N") {
  225. if (b.t == "N") {
  226. return mul_N_N(a,b);
  227. } else if (b.t=="LC") {
  228. return mul_LC_N(b,a);
  229. } else if (b.t=="QEX") {
  230. return mul_QEX_N(b,a);
  231. } else {
  232. return { t: "NQ"};
  233. }
  234. } else if (a.t=="LC") {
  235. if (b.t == "N") {
  236. return mul_LC_N(a,b);
  237. } else if (b.t=="LC") {
  238. return mul_LC_LC(a,b);
  239. } else if (b.t=="QEX") {
  240. return { t: "NQ" };
  241. } else {
  242. return { t: "NQ" };
  243. }
  244. } else if (a.t=="QEX") {
  245. if (b.t == "N") {
  246. return mul_QEX_N(a,b);
  247. } else if (b.t=="LC") {
  248. return { t: "NQ" };
  249. } else if (b.t=="QEX") {
  250. return { t: "NQ" };
  251. } else {
  252. return { t: "NQ" };
  253. }
  254. } else {
  255. return { t: "NQ" };
  256. }
  257. function mul_N_N(a,b) {
  258. return {
  259. t: "N",
  260. v: self.field.mul(a.v, b.v)
  261. };
  262. }
  263. function mul_LC_N(a,b) {
  264. let res = self._clone(a);
  265. for (let k in res.coefs) {
  266. res.coefs[k] = self.field.mul(res.coefs[k], b.v);
  267. }
  268. return res;
  269. }
  270. function mul_LC_LC(a,b) {
  271. return {
  272. t: "QEX",
  273. a: self._clone(a),
  274. b: self._clone(b),
  275. c: { t: "LC", coefs: {}}
  276. };
  277. }
  278. function mul_QEX_N(a,b) {
  279. return {
  280. t: "QEX",
  281. a: mul_LC_N(a.a, b),
  282. b: self._clone(a.b),
  283. c: mul_LC_N(a.c, b)
  284. };
  285. }
  286. }
  287. neg(_a) {
  288. const a = this._signal2lc(_a);
  289. let res = this._clone(a);
  290. if (res.t == "N") {
  291. res.v = this.field.neg(a.v);
  292. } else if (res.t == "LC") {
  293. for (let k in res.coefs) {
  294. res.coefs[k] = this.field.neg(res.coefs[k]);
  295. }
  296. } else if (res.t == "QEX") {
  297. res.a = this.neg(res.a);
  298. res.c = this.neg(res.c);
  299. } else {
  300. res = {t: "NQ"};
  301. }
  302. return res;
  303. }
  304. sub(a, b) {
  305. return this.add(a, this.neg(b));
  306. }
  307. div(a, b) {
  308. if (b.t == "N") {
  309. if (b.v.isZero()) throw new Error("Division by zero");
  310. const inv = {
  311. t: "N",
  312. v: this.field.inv(b.v)
  313. };
  314. return this.mul(a, inv);
  315. } else {
  316. return {t: "NQ"};
  317. }
  318. }
  319. pow(a, b) {
  320. if (b.t == "N") {
  321. if (b.v.isZero()) {
  322. if (this.isZero(a)) {
  323. throw new Error("Zero to the Zero");
  324. }
  325. return {
  326. t: "N",
  327. v: this.field.one
  328. };
  329. } else if (b.v.eq(this.field.one)) {
  330. return a;
  331. } else if (b.v.eq(bigInt(2))) {
  332. return this.mul(a,a);
  333. } else {
  334. if (a.t=="N") {
  335. return {
  336. t: "N",
  337. v: this.field.pow(a.v, b.v)
  338. };
  339. } else {
  340. return {t: "NQ"};
  341. }
  342. }
  343. } else {
  344. return {t: "NQ"};
  345. }
  346. }
  347. substitute(where, signal, equivalence) {
  348. if (equivalence.t != "LC") throw new Error("Equivalence must be a Linear Combination");
  349. if (where.t == "LC") {
  350. if (!utils.isDefined(where.coefs[signal]) || where.coefs[signal].isZero()) return where;
  351. const res=this._clone(where);
  352. const coef = res.coefs[signal];
  353. for (let k in equivalence.coefs) {
  354. if (k != signal) {
  355. const v = this.field.mul( coef, equivalence.coefs[k] );
  356. if (!utils.isDefined(res.coefs[k])) {
  357. res.coefs[k]=v;
  358. } else {
  359. res.coefs[k]= this.field.add(res.coefs[k],v);
  360. }
  361. if (res.coefs[k].isZero()) delete res.coefs[k];
  362. }
  363. }
  364. delete res.coefs[signal];
  365. return res;
  366. } else if (where.t == "QEX") {
  367. const res = {
  368. t: "QEX",
  369. a: this.substitute(where.a, signal, equivalence),
  370. b: this.substitute(where.b, signal, equivalence),
  371. c: this.substitute(where.c, signal, equivalence)
  372. };
  373. return res;
  374. } else {
  375. return where;
  376. }
  377. }
  378. toQEX(a) {
  379. if (a.t == "N") {
  380. const res = {
  381. t: "QEX",
  382. a: {t: "LC", coefs: {}},
  383. b: {t: "LC", coefs: {}},
  384. c: {t: "LC", coefs: {}}
  385. };
  386. res.c[sONE] = a.v;
  387. return res;
  388. } else if (a.t == "LC") {
  389. return {
  390. t: "QEX",
  391. a: {t: "LC", coefs: {}},
  392. b: {t: "LC", coefs: {}},
  393. c: this._clone(a)
  394. };
  395. } else if (a.t == "QEX") {
  396. return this._clone(a);
  397. } else {
  398. throw new Error(`Type ${a.t} can not be converted to QEX`);
  399. }
  400. }
  401. isZero(a) {
  402. if (a.t == "N") {
  403. return a.v.isZero();
  404. } else if (a.t == "LC") {
  405. for (let k in a.coefs) {
  406. if (!a.coefs[k].isZero()) return false;
  407. }
  408. return true;
  409. } else if (a.t == "QEX") {
  410. return (this.isZero(a.a) || this.isZero(a.b)) && this.isZero(a.c);
  411. } else {
  412. return false;
  413. }
  414. }
  415. toString(a, ctx) {
  416. if (a.t == "N") {
  417. return a.v.toString();
  418. } else if (a.t == "LC") {
  419. let S="";
  420. for (let k in a.coefs) {
  421. if (!a.coefs[k].isZero()) {
  422. let c;
  423. if (a.coefs[k].greater(this.field.p.divide(2))) {
  424. S = S + "-";
  425. c = this.field.p.minus(a.coefs[k]);
  426. } else {
  427. if (S!="") S=S+" + ";
  428. c = a.coefs[k];
  429. }
  430. if (!c.equals(bigInt.one)) {
  431. S = S + c.toString() + "*";
  432. }
  433. let sIdx = k;
  434. if (ctx) {
  435. while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
  436. }
  437. S = S + "[" + sIdx + "]";
  438. }
  439. }
  440. if (S=="") return "0"; else return S;
  441. } else if (a.t == "QEX") {
  442. return "( "+
  443. this.toString(a.a, ctx)+" ) * ( "+
  444. this.toString(a.b, ctx)+" ) + " +
  445. this.toString(a.c, ctx);
  446. } else {
  447. return "NQ";
  448. }
  449. }
  450. evaluate(ctx, n) {
  451. if (n.t == "N") {
  452. return n.v;
  453. } else if (n.t == "SIGNAL") {
  454. return getSignalValue(ctx, n.sIdx);
  455. } else if (n.t == "LC") {
  456. let v= this.field.zero;
  457. for (let k in n.coefs) {
  458. const s = getSignalValue(ctx, k);
  459. if (s === null) return null;
  460. v = this.field.add(v, this.field.mul( n.coefs[k], s));
  461. }
  462. return v;
  463. } else if (n.type == "QEX") {
  464. const a = this.evaluate(ctx, n.a);
  465. if (a === null) return null;
  466. const b = this.evaluate(ctx, n.b);
  467. if (b === null) return null;
  468. const c = this.evaluate(ctx, n.c);
  469. if (c === null) return null;
  470. return this.field.add(this.field.mul(a,b), c);
  471. } else {
  472. return null;
  473. }
  474. function getSignalValue(ctx, sIdx) {
  475. let s = ctx.signals[sIdx];
  476. while (s.e>=0) s = ctx.signals[s.e];
  477. if (utils.isDefined(s.v)) return s.v;
  478. return null;
  479. }
  480. }
  481. canonize(ctx, a) {
  482. if (a.t == "LC") {
  483. const res = this._clone(a);
  484. for (let k in a.coefs) {
  485. let s = k;
  486. while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
  487. if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
  488. const v = this.field.mul(res.coefs[k], ctx.signals[s].v);
  489. if (!utils.isDefined(res.coefs[sONE])) {
  490. res.coefs[sONE]=v;
  491. } else {
  492. res.coefs[sONE]= this.field.add(res.coefs[sONE], v);
  493. }
  494. delete res.coefs[k];
  495. } else if (s != k) {
  496. if (!utils.isDefined(res.coefs[s])) {
  497. res.coefs[s]=res.coefs[k];
  498. } else {
  499. res.coefs[s]= this.field.add(res.coefs[s], res.coefs[k]);
  500. }
  501. delete res.coefs[k];
  502. }
  503. }
  504. for (let k in res.coefs) {
  505. if (res.coefs[k].isZero()) delete res.coefs[k];
  506. }
  507. return res;
  508. } else if (a.t == "QEX") {
  509. const res = {
  510. t: "QEX",
  511. a: this.canonize(ctx, a.a),
  512. b: this.canonize(ctx, a.b),
  513. c: this.canonize(ctx, a.c)
  514. };
  515. return res;
  516. } else {
  517. return a;
  518. }
  519. }
  520. }
  521. module.exports = LCAlgebra;