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.

571 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
4 years ago
4 years ago
6 years ago
4 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 utils = require("./utils");
  67. const sONE = 0;
  68. class LCAlgebra {
  69. constructor (aField) {
  70. const self = this;
  71. this.F= aField;
  72. [
  73. ["idiv",2],
  74. ["mod",2],
  75. ["band",2],
  76. ["bor",2],
  77. ["bxor",2],
  78. ["bnot",2],
  79. ["land",2],
  80. ["lor",2],
  81. ["lnot",2],
  82. ["shl",2],
  83. ["shr",2],
  84. ["lt",2, true],
  85. ["leq",2, true],
  86. ["eq",2, true],
  87. ["neq",2, true],
  88. ["geq",2, true],
  89. ["gt",2, true]
  90. ].forEach( (op) => {
  91. self._genNQOp(op[0], op[1], op[2]);
  92. });
  93. }
  94. _genNQOp(op, nOps, adjustBool) {
  95. const self=this;
  96. self[op] = function() {
  97. const operands = [];
  98. for (let i=0; i<nOps; i++) {
  99. if (typeof(arguments[i]) !== "object") throw new Error("Invalid operand type");
  100. if (arguments[i].t !== "N") return {t: "NQ"};
  101. operands.push(arguments[i].v);
  102. }
  103. return {
  104. t: "N",
  105. v: adjustBool ? ( self.F[op](...operands) ? self.F.one: self.F.zero) : self.F[op](...operands)
  106. };
  107. };
  108. }
  109. _signal2lc(a) {
  110. if (a.t == "S") {
  111. const lc = {
  112. t: "LC",
  113. coefs: {}
  114. };
  115. lc.coefs[a.sIdx] = self.F.one;
  116. return lc;
  117. } else {
  118. return a;
  119. }
  120. }
  121. _clone(a) {
  122. const res = {};
  123. res.t = a.t;
  124. if (a.t == "N") {
  125. res.v = a.v;
  126. } else if (a.t == "S") {
  127. res.sIdx = a.sIdx;
  128. } else if (a.t == "LC") {
  129. res.coefs = {};
  130. for (let k in a.coefs) {
  131. res.coefs[k] = a.coefs[k];
  132. }
  133. } else if (a.t == "QEX") {
  134. res.a = this._clone(a.a);
  135. res.b = this._clone(a.b);
  136. res.c = this._clone(a.c);
  137. }
  138. return res;
  139. }
  140. add(_a,_b) {
  141. const self = this;
  142. const a = self._signal2lc(_a);
  143. const b = self._signal2lc(_b);
  144. if (a.t == "NQ") return a;
  145. if (b.t == "NQ") return b;
  146. if (a.t == "N") {
  147. if (b.t == "N") {
  148. return add_N_N(a,b);
  149. } else if (b.t=="LC") {
  150. return add_LC_N(b,a);
  151. } else if (b.t=="QEX") {
  152. return add_QEX_N(b,a);
  153. } else {
  154. return { type: "NQ" };
  155. }
  156. } else if (a.t=="LC") {
  157. if (b.t == "N") {
  158. return add_LC_N(a,b);
  159. } else if (b.t=="LC") {
  160. return add_LC_LC(a,b);
  161. } else if (b.t=="QEX") {
  162. return add_QEX_LC(b,a);
  163. } else {
  164. return { t: "NQ" };
  165. }
  166. } else if (a.t=="QEX") {
  167. if (b.t == "N") {
  168. return add_QEX_N(a,b);
  169. } else if (b.t=="LC") {
  170. return add_QEX_LC(a,b);
  171. } else if (b.t=="QEX") {
  172. return { t: "NQ" };
  173. } else {
  174. return { t: "NQ" };
  175. }
  176. } else {
  177. return { t: "NQ" };
  178. }
  179. function add_N_N(a,b) {
  180. return {
  181. t: "N",
  182. v: self.F.add(a.v, b.v)
  183. };
  184. }
  185. function add_LC_N(a,b) {
  186. let res = self._clone(a);
  187. if (self.F.isZero(b.v)) return res;
  188. if (!utils.isDefined(res.coefs[sONE])) {
  189. res.coefs[sONE]= b.v;
  190. } else {
  191. res.coefs[sONE]= self.F.add(res.coefs[sONE], b.v);
  192. }
  193. return res;
  194. }
  195. function add_LC_LC(a,b) {
  196. let res = self._clone(a);
  197. for (let k in b.coefs) {
  198. if (!utils.isDefined(res.coefs[k])) {
  199. res.coefs[k]=b.coefs[k];
  200. } else {
  201. res.coefs[k]= self.F.add(res.coefs[k], b.coefs[k]);
  202. }
  203. }
  204. return res;
  205. }
  206. function add_QEX_N(a,b) {
  207. let res = self._clone(a);
  208. res.c = add_LC_N(res.c, b);
  209. return res;
  210. }
  211. function add_QEX_LC(a,b) {
  212. let res = self._clone(a);
  213. res.c = add_LC_LC(res.c, b);
  214. return res;
  215. }
  216. }
  217. mul(_a,_b) {
  218. const self = this;
  219. const a = self._signal2lc(_a);
  220. const b = self._signal2lc(_b);
  221. if (a.t == "NQ") return a;
  222. if (b.t == "NQ") return b;
  223. if (a.t == "N") {
  224. if (b.t == "N") {
  225. return mul_N_N(a,b);
  226. } else if (b.t=="LC") {
  227. return mul_LC_N(b,a);
  228. } else if (b.t=="QEX") {
  229. return mul_QEX_N(b,a);
  230. } else {
  231. return { t: "NQ"};
  232. }
  233. } else if (a.t=="LC") {
  234. if (b.t == "N") {
  235. return mul_LC_N(a,b);
  236. } else if (b.t=="LC") {
  237. return mul_LC_LC(a,b);
  238. } else if (b.t=="QEX") {
  239. return { t: "NQ" };
  240. } else {
  241. return { t: "NQ" };
  242. }
  243. } else if (a.t=="QEX") {
  244. if (b.t == "N") {
  245. return mul_QEX_N(a,b);
  246. } else if (b.t=="LC") {
  247. return { t: "NQ" };
  248. } else if (b.t=="QEX") {
  249. return { t: "NQ" };
  250. } else {
  251. return { t: "NQ" };
  252. }
  253. } else {
  254. return { t: "NQ" };
  255. }
  256. function mul_N_N(a,b) {
  257. return {
  258. t: "N",
  259. v: self.F.mul(a.v, b.v)
  260. };
  261. }
  262. function mul_LC_N(a,b) {
  263. let res = self._clone(a);
  264. for (let k in res.coefs) {
  265. res.coefs[k] = self.F.mul(res.coefs[k], b.v);
  266. }
  267. return res;
  268. }
  269. function mul_LC_LC(a,b) {
  270. return {
  271. t: "QEX",
  272. a: self._clone(a),
  273. b: self._clone(b),
  274. c: { t: "LC", coefs: {}}
  275. };
  276. }
  277. function mul_QEX_N(a,b) {
  278. return {
  279. t: "QEX",
  280. a: mul_LC_N(a.a, b),
  281. b: self._clone(a.b),
  282. c: mul_LC_N(a.c, b)
  283. };
  284. }
  285. }
  286. neg(_a) {
  287. const a = this._signal2lc(_a);
  288. let res = this._clone(a);
  289. if (res.t == "N") {
  290. res.v = this.F.neg(a.v);
  291. } else if (res.t == "LC") {
  292. for (let k in res.coefs) {
  293. res.coefs[k] = this.F.neg(res.coefs[k]);
  294. }
  295. } else if (res.t == "QEX") {
  296. res.a = this.neg(res.a);
  297. res.c = this.neg(res.c);
  298. } else {
  299. res = {t: "NQ"};
  300. }
  301. return res;
  302. }
  303. sub(a, b) {
  304. return this.add(a, this.neg(b));
  305. }
  306. div(a, b) {
  307. if (b.t == "N") {
  308. if (this.F.isZero(b.v)) throw new Error("Division by zero");
  309. const inv = {
  310. t: "N",
  311. v: this.F.inv(b.v)
  312. };
  313. return this.mul(a, inv);
  314. } else {
  315. return {t: "NQ"};
  316. }
  317. }
  318. pow(a, b) {
  319. if (b.t == "N") {
  320. if (this.F.isZero(b.v)) {
  321. if (this.isZero(a)) {
  322. throw new Error("Zero to the Zero");
  323. }
  324. return {
  325. t: "N",
  326. v: this.F.one
  327. };
  328. } else if (this.F.eq(b.v, this.F.one)) {
  329. return a;
  330. } else if (this.F.eq(b.v, this.F.two)) {
  331. return this.mul(a,a);
  332. } else {
  333. if (a.t=="N") {
  334. return {
  335. t: "N",
  336. v: this.F.pow(a.v, b.v)
  337. };
  338. } else {
  339. return {t: "NQ"};
  340. }
  341. }
  342. } else {
  343. return {t: "NQ"};
  344. }
  345. }
  346. substitute(where, signal, equivalence) {
  347. if (equivalence.t != "LC") throw new Error("Equivalence must be a Linear Combination");
  348. if (where.t == "LC") {
  349. if (!utils.isDefined(where.coefs[signal]) || this.F.isZero(where.coefs[signal])) return where;
  350. const res=this._clone(where);
  351. const coef = res.coefs[signal];
  352. for (let k in equivalence.coefs) {
  353. if (k != signal) {
  354. const v = this.F.mul( coef, equivalence.coefs[k] );
  355. if (!utils.isDefined(res.coefs[k])) {
  356. res.coefs[k]=v;
  357. } else {
  358. res.coefs[k]= this.F.add(res.coefs[k],v);
  359. }
  360. if (this.F.isZero(res.coefs[k])) delete res.coefs[k];
  361. }
  362. }
  363. delete res.coefs[signal];
  364. return res;
  365. } else if (where.t == "QEX") {
  366. const res = {
  367. t: "QEX",
  368. a: this.substitute(where.a, signal, equivalence),
  369. b: this.substitute(where.b, signal, equivalence),
  370. c: this.substitute(where.c, signal, equivalence)
  371. };
  372. return res;
  373. } else {
  374. return where;
  375. }
  376. }
  377. toQEX(a) {
  378. if (a.t == "N") {
  379. const res = {
  380. t: "QEX",
  381. a: {t: "LC", coefs: {}},
  382. b: {t: "LC", coefs: {}},
  383. c: {t: "LC", coefs: {}}
  384. };
  385. res.c[sONE] = a.v;
  386. return res;
  387. } else if (a.t == "LC") {
  388. return {
  389. t: "QEX",
  390. a: {t: "LC", coefs: {}},
  391. b: {t: "LC", coefs: {}},
  392. c: this._clone(a)
  393. };
  394. } else if (a.t == "QEX") {
  395. return this._clone(a);
  396. } else {
  397. throw new Error(`Type ${a.t} can not be converted to QEX`);
  398. }
  399. }
  400. isZero(a) {
  401. if (a.t == "N") {
  402. return this.F.isZero(a.v);
  403. } else if (a.t == "LC") {
  404. for (let k in a.coefs) {
  405. if (!this.F.isZero(a.coefs[k])) return false;
  406. }
  407. return true;
  408. } else if (a.t == "QEX") {
  409. return (this.isZero(a.a) || this.isZero(a.b)) && this.isZero(a.c);
  410. } else {
  411. return false;
  412. }
  413. }
  414. toString(a, ctx) {
  415. if (a.t == "N") {
  416. return a.v.toString();
  417. } else if (a.t == "LC") {
  418. let S="";
  419. for (let k in a.coefs) {
  420. if (!this.F.isZero(a.coefs[k])) {
  421. let c;
  422. if (a.coefs[k].greater(this.F.p.divide(2))) {
  423. S = S + "-";
  424. c = this.F.p.minus(a.coefs[k]);
  425. } else {
  426. if (S!="") S=S+" + ";
  427. c = a.coefs[k];
  428. }
  429. if (!c.equals(this.F.one)) {
  430. S = S + c.toString() + "*";
  431. }
  432. let sIdx = k;
  433. if (ctx) {
  434. while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
  435. }
  436. S = S + "[" + sIdx + "]";
  437. }
  438. }
  439. if (S=="") return "0"; else return S;
  440. } else if (a.t == "QEX") {
  441. return "( "+
  442. this.toString(a.a, ctx)+" ) * ( "+
  443. this.toString(a.b, ctx)+" ) + " +
  444. this.toString(a.c, ctx);
  445. } else {
  446. return "NQ";
  447. }
  448. }
  449. evaluate(ctx, n) {
  450. if (n.t == "N") {
  451. return n.v;
  452. } else if (n.t == "SIGNAL") {
  453. return getSignalValue(ctx, n.sIdx);
  454. } else if (n.t == "LC") {
  455. let v= this.F.zero;
  456. for (let k in n.coefs) {
  457. const s = getSignalValue(ctx, k);
  458. if (s === null) return null;
  459. v = this.F.add(v, this.F.mul( n.coefs[k], s));
  460. }
  461. return v;
  462. } else if (n.type == "QEX") {
  463. const a = this.evaluate(ctx, n.a);
  464. if (a === null) return null;
  465. const b = this.evaluate(ctx, n.b);
  466. if (b === null) return null;
  467. const c = this.evaluate(ctx, n.c);
  468. if (c === null) return null;
  469. return this.F.add(this.F.mul(a,b), c);
  470. } else {
  471. return null;
  472. }
  473. function getSignalValue(ctx, sIdx) {
  474. let s = ctx.signals[sIdx];
  475. while (s.e>=0) s = ctx.signals[s.e];
  476. if (utils.isDefined(s.v)) return s.v;
  477. return null;
  478. }
  479. }
  480. canonize(ctx, a) {
  481. if (a.t == "LC") {
  482. const res = this._clone(a);
  483. for (let k in a.coefs) {
  484. let s = k;
  485. while (ctx.signals[s].e>=0) s= ctx.signals[s].e;
  486. if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) {
  487. const v = this.F.mul(res.coefs[k], ctx.signals[s].v);
  488. if (!utils.isDefined(res.coefs[sONE])) {
  489. res.coefs[sONE]=v;
  490. } else {
  491. res.coefs[sONE]= this.F.add(res.coefs[sONE], v);
  492. }
  493. delete res.coefs[k];
  494. } else if (s != k) {
  495. if (!utils.isDefined(res.coefs[s])) {
  496. res.coefs[s]=res.coefs[k];
  497. } else {
  498. res.coefs[s]= this.F.add(res.coefs[s], res.coefs[k]);
  499. }
  500. delete res.coefs[k];
  501. }
  502. }
  503. for (let k in res.coefs) {
  504. if (this.F.isZero(res.coefs[k])) delete res.coefs[k];
  505. }
  506. return res;
  507. } else if (a.t == "QEX") {
  508. const res = {
  509. t: "QEX",
  510. a: this.canonize(ctx, a.a),
  511. b: this.canonize(ctx, a.b),
  512. c: this.canonize(ctx, a.c)
  513. };
  514. return res;
  515. } else {
  516. return a;
  517. }
  518. }
  519. }
  520. module.exports = LCAlgebra;