/* Copyright 2018 0KIMS association. This file is part of circom (Zero Knowledge Circuit Compiler). circom 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. circom 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 circom. If not, see . */ /* // Number /////////////// N: a { t: "N", v: bigInt(a) } // Signal /////////////// { t: "S", sIdx: sIdx } // Linear Convination ////////////////// LC: c1*s1 + c2*s2 + c3*s3 { t: "LC", coefs: { s1: bigInt(c1), s2: bigInt(c2), s3: bigInt(c3) } } // Quadratic Expression ////////////////// QEX: a*b + c WHERE a,b,c are LC { t: "QEX" a: { t: "LC", coefs: {...} }, b: { t: "LC", coefs: {...} }, c: { t: "LC", coefs: {...} } } NQ: Non quadratic expression { t: "NQ" } */ /* + N LC QEX NQ N N LC QEX NQ LC LC LC QEX NQ QEX QEX QEX NQ NQ NQ NQ NQ NQ NQ * N LC QEX NQ N N LC QEX NQ LC LC QEX NQ NQ QEX QEX NQ NQ NQ NQ NQ NQ NQ NQ */ const bigInt = require("big-integer"); const utils = require("./utils"); const sONE = 0; class LCAlgebra { constructor (aField) { const self = this; this.field= aField; [ ["lt",2], ["leq",2], ["eq",2], ["neq",2], ["geq",2], ["gt",2], ["idiv",2], ["mod",2], ["band",2], ["bor",2], ["bxor",2], ["bnot",2], ["land",2], ["lor",2], ["lnot",2], ["shl",2], ["shr",2], ].forEach( (op) => { self._genNQOp(op[0], op[1]); }); } _genNQOp(op, nOps) { const self=this; self[op] = function() { const operands = []; for (let i=0; i=0) sIdx = ctx.signals[sIdx].e; } S = S + "[" + sIdx + "]"; } } if (S=="") return "0"; else return S; } else if (a.t == "QEX") { return "( "+ this.toString(a.a, ctx)+" ) * ( "+ this.toString(a.b, ctx)+" ) + " + this.toString(a.c, ctx); } else { return "NQ"; } } evaluate(ctx, n) { if (n.t == "N") { return n.v; } else if (n.t == "SIGNAL") { return getSignalValue(ctx, n.sIdx); } else if (n.t == "LC") { let v= this.field.zero; for (let k in n.coefs) { const s = getSignalValue(ctx, k); if (s === null) return null; v = this.field.add(v, this.field.mul( n.coefs[k], s)); } return v; } else if (n.type == "QEX") { const a = this.evaluate(ctx, n.a); if (a === null) return null; const b = this.evaluate(ctx, n.b); if (b === null) return null; const c = this.evaluate(ctx, n.c); if (c === null) return null; return this.field.add(this.field.mul(a,b), c); } else { return null; } function getSignalValue(ctx, sIdx) { let s = ctx.signals[sIdx]; while (s.e>=0) s = ctx.signals[s.e]; if (utils.isDefined(s.v)) return s.v; return null; } } canonize(ctx, a) { if (a.t == "LC") { const res = this._clone(a); for (let k in a.coefs) { let s = k; while (ctx.signals[s].e>=0) s= ctx.signals[s].e; if (utils.isDefined(ctx.signals[s].v)&&(k != sONE)) { const v = this.field.mul(res.coefs[k], ctx.signals[s].v); if (!utils.isDefined(res.coefs[sONE])) { res.coefs[sONE]=v; } else { res.coefs[sONE]= this.field.add(res.coefs[sONE], v); } delete res.coefs[k]; } else if (s != k) { if (!utils.isDefined(res.coefs[s])) { res.coefs[s]=res.coefs[k]; } else { res.coefs[s]= this.field.add(res.coefs[s], res.coefs[k]); } delete res.coefs[k]; } } for (let k in res.coefs) { if (res.coefs[k].isZero()) delete res.coefs[k]; } return res; } else if (a.t == "QEX") { const res = { t: "QEX", a: this.canonize(ctx, a.a), b: this.canonize(ctx, a.b), c: this.canonize(ctx, a.c) }; return res; } else { return a; } } } module.exports = LCAlgebra;