diff --git a/src/c_gen.js b/src/c_gen.js
index a886b3b..0da92df 100644
--- a/src/c_gen.js
+++ b/src/c_gen.js
@@ -87,7 +87,7 @@ function instantiateConstant(ctx, value) {
function createRefs(ctx, ast) {
const scopeLabels = [];
iterateAST(ast, (ast, level) => {
- while ((scopeLabels.length>0)&&(!scopeLabels[scopeLabels.length-1].startsWith(level))) {
+ while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
ctx.scopes.pop();
scopeLabels.pop();
}
@@ -329,7 +329,7 @@ function genDeclareVariable(ctx, ast) {
}
sizes = utils.accSizes(sizes);
} else {
- sizes = null; // If not sizes, the sized are defined in the first assignement.
+ sizes = [1,0];
}
if ((!v.sizes)&&(sizes)) {
diff --git a/src/compiler.js b/src/compiler.js
index 84f9a3b..b1e64f1 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -17,26 +17,18 @@
along with circom. If not, see .
*/
-const fs = require("fs");
-const path = require("path");
const bigInt = require("big-integer");
const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
const sONE = 0;
-const assert = require("assert");
const buildC = require("./c_build");
-const exec = require("./exec");
-const lc = require("./lcalgebra");
+const constructionPhase = require("./construction_phase");
const Ctx = require("./ctx");
-const ZqField = require("./zqfield");
+const ZqField = require("fflib").ZqField;
const utils = require("./utils");
const buildR1cs = require("./r1csfile").buildR1cs;
module.exports = compile;
-const parser = require("../parser/jaz.js").parser;
-
-const timeout = ms => new Promise(res => setTimeout(res, ms));
-
async function compile(srcFile, options) {
options.p = options.p || __P__;
if (!options) {
@@ -45,26 +37,15 @@ async function compile(srcFile, options) {
if (typeof options.reduceConstraints === "undefined") {
options.reduceConstraints = true;
}
- const fullFileName = srcFile;
- const fullFilePath = path.dirname(fullFileName);
-
- const src = fs.readFileSync(fullFileName, "utf8");
- const ast = parser.parse(src);
-
- assert(ast.type == "BLOCK");
-
const ctx = new Ctx();
ctx.field = new ZqField(options.p);
+ ctx.verbose= options.verbose || false;
ctx.mainComponent = options.mainComponent || "main";
- ctx.filePath= fullFilePath;
- ctx.fileName= fullFileName;
- ctx.includedFiles = {};
- ctx.includedFiles[fullFileName] = src.split("\n");
- ctx.verbose= options.verbose || false;
+ constructionPhase(ctx, srcFile);
- exec(ctx, ast);
+ console.log("NConstraints Before: "+ctx.constraints.length);
if (ctx.error) {
throw(ctx.error);
@@ -91,6 +72,8 @@ async function compile(srcFile, options) {
}
}
+ console.log("NConstraints After: "+ctx.constraints.length);
+
generateWitnessNames(ctx);
if (ctx.error) {
@@ -235,8 +218,8 @@ function reduceConstants(ctx) {
const newConstraints = [];
for (let i=0; i.
+*/
+
+/*
+
+ The exec functions, return an object of the form:
+
+ {
+ t: "C" or "V" or "S"
+ s: Accumulated Sizes
+ v: [] only when "V" Array of values where each value is:
+ {
+ t: "N", "S", "LC", "QEX", "NQ"
+ v: When "N" its the bigInt of the number
+ coefs: { sIdx: vCoef } when "LC" the values of the coefs
+ a,b,c: a LC object of a quadratic expression.
+ }
+ refId: In case of a variable, the reference of a variable.
+ sIdx: When signal, the sIdx
+ cIdx: When component, the cIdx
+ }
+
+
+
+
+
+ */
+
+const path = require("path");
+const fs = require("fs");
+const assert = require("assert");
+
+const iterateAST = require("./iterateast");
+const utils = require("./utils");
+
+const bigInt = require("big-integer");
+
+const LCAlgebra = require("./lcalgebra");
+const parser = require("../parser/jaz.js").parser;
+
+/* TODO: Add lines information
+
+function setLines(dst, first, last) {
+ last = last || first;
+ dst.first_line = first.first_line;
+ dst.first_column = first.first_column;
+ dst.last_line = last.last_line;
+ dst.last_column = last.last_column;
+}
+*/
+
+module.exports = constructionPhase;
+
+const NQVAL = {t: "V", s:[1,0], v: [{t:"NQ"}]};
+
+function constructionPhase(ctx, srcFile) {
+
+ const fullFileName = srcFile;
+ const fullFilePath = path.dirname(fullFileName);
+
+ const src = fs.readFileSync(fullFileName, "utf8");
+ ctx.ast = parser.parse(src);
+
+ assert(ctx.ast.type == "BLOCK");
+
+ ctx.lc = new LCAlgebra(ctx.field);
+ ctx.filePath= fullFilePath;
+ ctx.fileName= fullFileName;
+ ctx.includedFiles = {};
+ ctx.includedFiles[fullFileName] = src.split("\n");
+
+ ctx.refs = [];
+ createRefs(ctx, ctx.ast);
+ exec(ctx, ctx.ast);
+
+}
+
+function exec(ctx, ast) {
+ if (!ast) {
+ return ctx.throwError(ast, "Null AST");
+ }
+ if (ast.type == "NUMBER") {
+ return execNumber(ctx,ast);
+ } else if (ast.type == "VARIABLE") {
+ return execVariable(ctx, ast);
+ } else if (ast.type == "PIN") {
+ return execPin(ctx, ast);
+ } else if (ast.type == "OP") {
+ if (ast.op == "=") {
+ return execAssignement(ctx, ast);
+ } else if (ast.op == "<--") {
+ return execAssignement(ctx, ast);
+ } else if (ast.op == "<==") {
+ return execSignalAssignConstrain(ctx, ast);
+ } else if (ast.op == "===") {
+ return execConstrain(ctx, ast);
+ } else if (ast.op == "+=") {
+ return execOpOp(ctx, ast, "add", "LEFT");
+ } else if (ast.op == "*=") {
+ return execOpOp(ctx, ast, "mul", "LEFT");
+ } else if (ast.op == "+") {
+ return execOp(ctx, ast, "add", 2);
+ } else if (ast.op == "-") {
+ return execOp(ctx, ast, "sub", 2);
+ } else if (ast.op == "UMINUS") {
+ return execOp(ctx, ast, "neg", 1);
+ } else if (ast.op == "*") {
+ return execOp(ctx, ast, "mul", 2);
+ } else if (ast.op == "%") {
+ return execOp(ctx, ast, "mod", 2);
+ } else if (ast.op == "PLUSPLUSRIGHT") {
+ return execOpOp(ctx, ast, "add", "RIGHT");
+ } else if (ast.op == "PLUSPLUSLEFT") {
+ return execOpOp(ctx, ast, "add", "LEFT");
+ } else if (ast.op == "MINUSMINUSRIGHT") {
+ return execOpOp(ctx, ast, "sub", "RIGHT");
+ } else if (ast.op == "MINUSMINUSLEFT") {
+ return execOpOp(ctx, ast, "sub", "LEFT");
+ } else if (ast.op == "/") {
+ return execOp(ctx, ast, "div", 2);
+ } else if (ast.op == "\\") {
+ return execOp(ctx, ast, "idiv", 2);
+ } else if (ast.op == "**") {
+ return execOp(ctx, ast, "pow", 2);
+ } else if (ast.op == "&") {
+ return execOp(ctx, ast, "band", 2);
+ } else if (ast.op == "|") {
+ return execOp(ctx, ast, "bor", 2);
+ } else if (ast.op == "^") {
+ return execOp(ctx, ast, "bxor", 2);
+ } else if (ast.op == "~") {
+ return execOp(ctx, ast, "bnot", 1);
+ } else if (ast.op == "&&") {
+ return execOp(ctx, ast, "land", 2);
+ } else if (ast.op == "||") {
+ return execOp(ctx, ast, "lor", 2);
+ } else if (ast.op == "!") {
+ return execOp(ctx, ast, "lnot", 1);
+ } else if (ast.op == "<<") {
+ return execOp(ctx, ast, "shl", 2);
+ } else if (ast.op == ">>") {
+ return execOp(ctx, ast, "shr", 2);
+ } else if (ast.op == "<") {
+ return execOp(ctx, ast, "lt", 2);
+ } else if (ast.op == ">") {
+ return execOp(ctx, ast, "gt", 2);
+ } else if (ast.op == "<=") {
+ return execOp(ctx, ast, "leq", 2);
+ } else if (ast.op == ">=") {
+ return execOp(ctx, ast, "geq", 2);
+ } else if (ast.op == "==") {
+ return execOp(ctx, ast, "eq", 2);
+ } else if (ast.op == "!=") {
+ return execOp(ctx, ast, "neq", 2);
+ } else if (ast.op == "?") {
+ return execTerCon(ctx, ast);
+ } else {
+ ctx.throwError(ast, "Invalid operation: " + ast.op);
+ }
+ } else if (ast.type == "DECLARE") {
+ if (ast.declareType == "COMPONENT") {
+ return execDeclareComponent(ctx, ast);
+ } else if ((ast.declareType == "SIGNALIN")||
+ (ast.declareType == "SIGNALOUT")||
+ (ast.declareType == "SIGNAL")) {
+ return execDeclareSignal(ctx, ast);
+ } else if (ast.declareType == "VARIABLE") {
+ return execDeclareVariable(ctx, ast);
+ } else {
+ ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
+ }
+ } else if (ast.type == "FUNCTIONCALL") {
+ return execFunctionCall(ctx, ast);
+ } else if (ast.type == "BLOCK") {
+ return execBlock(ctx, ast);
+ } else if (ast.type == "COMPUTE") {
+ return execCompute(ctx, ast);
+ } else if (ast.type == "FOR") {
+ return execLoop(ctx, ast);
+ } else if (ast.type == "WHILE") {
+ return execLoop(ctx, ast);
+ } else if (ast.type == "IF") {
+ return execIf(ctx, ast);
+ } else if (ast.type == "RETURN") {
+ return execReturn(ctx, ast);
+ } else if (ast.type == "TEMPLATEDEF") {
+ return execTemplateDef(ctx, ast);
+ } else if (ast.type == "FUNCTIONDEF") {
+ return execFunctionDef(ctx, ast);
+ } else if (ast.type == "INCLUDE") {
+ return execInclude(ctx, ast);
+ } else if (ast.type == "ARRAY") {
+ return execArray(ctx, ast);
+ } else {
+ ctx.throwError(ast, "Invalid AST node type: " + ast.type);
+ }
+}
+
+function execNumber(ctx, ast) {
+ return {
+ t: "V",
+ s:[1,0],
+ v: [{
+ t: "N",
+ v: bigInt(ast.value)
+ }]
+ };
+}
+
+
+function execTemplateDef(ctx, ast) {
+ ctx.templates[ast.name] = {
+ block: ast.block,
+ params: ast.params,
+ fileName: ctx.fileName,
+ filePath: ctx.filePath,
+ };
+}
+
+function execFunctionDef(ctx, ast) {
+ ctx.functions[ast.name] = {
+ block: ast.block,
+ params: ast.params,
+ fileName: ctx.fileName,
+ filePath: ctx.filePath
+ };
+}
+
+function execDeclareComponent(ctx, ast) {
+ if (ast.name.type != "VARIABLE") return ctx.throwError( ast, "Invalid component name");
+
+ const sizes=[];
+ for (let i=0; i< ast.name.selectors.length; i++) {
+ const sizeRef = exec(ctx, ast.name.selectors[i]);
+ if (ctx.error) return;
+
+ const size = val(ctx, sizeRef);
+ if (size.t != "N") return ctx.throwError( ast.name.selectors[i], "expected a number");
+
+ sizes.push( size.v.toJSNumber() );
+ }
+
+ let cIdx = ctx.addComponent(ast.name.name, sizes);
+ if (!Array.isArray(cIdx)) cIdx = [cIdx, cIdx+1];
+
+
+ ctx.refs[ast.refId].s = utils.accSizes(sizes);
+ ctx.refs[ast.refId].cIdx = cIdx[0];
+
+ return ctx.refs[ast.refId];
+}
+
+
+function execDeclareSignal(ctx, ast) {
+
+ if (ast.name.type != "VARIABLE") return ctx.throwError(ast, "Invalid component name");
+
+ const sizes=[];
+ for (let i=0; i< ast.name.selectors.length; i++) {
+ const size = exec(ctx, ast.name.selectors[i]);
+ if (ctx.error) return;
+ if (size.s[0] != 1) return ctx.throwError(ast, "Size cannot be an array");
+ if (size.v[0].t != "N") return ctx.throwError(ast, "Size must be declared in construction time");
+ sizes.push( size.v[0].v.toJSNumber() );
+ }
+
+ let sIdx = ctx.addSignal(ast.name.name, sizes);
+ if (!Array.isArray(sIdx)) sIdx = [sIdx, sIdx+1];
+ for (let i=sIdx[0]; i", "==>"].indexOf(ast.op) < 0)) return ctx.throwError(ast, "Cannot assign to a signal with `=` use <-- or <== ops");
+ if ((left.t == "V")&&( ["<--", "<==", "-->", "==>"].indexOf(ast.op) >= 0)) return ctx.throwError(ast, `Cannot assign to a var with ${ast.op}. use = op`);
+
+ const right = exec(ctx, ast.values[1]);
+ if (ctx.error) return;
+
+ if (!utils.sameSizes(left.s.slice(leftSels.length),right.s)) return ctx.throwError(ast, "Sizes in assignment must be the same");
+
+ let o = 0;
+ for (let i=0; i=0) sDest=ctx.signals[sDest.e];
+
+ if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
+
+ if (v !== null) {
+ sDest.v = v;
+ }
+ }
+
+ function joinSignals(dIdx, sIdx) {
+ let sDest=ctx.signals[dIdx];
+ let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
+ while (sDest.e>=0) {
+ sDest=ctx.signals[sDest.e];
+ isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
+ }
+
+ if (utils.isDefined(sDest.v)) return ctx.throwError(ast, "Signals cannot be assigned twice");
+
+ let sSrc = ctx.signals[sIdx];
+ let isIn = (sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN);
+ while (sSrc.e>=0) {
+ sSrc=ctx.signals[sSrc.e];
+ isIn = isIn || ((sSrc.o & ctx.MAIN)&&(sSrc.o & ctx.IN));
+ }
+
+ // Skip if an out is assigned directly to an input.
+ if ((!isIn)||(!isOut)) {
+ sDest.e = sIdx;
+ } else {
+ if (utils.isDefined(sSrc.v)) sDest.v = sSrc.v;
+ }
+ }
+}
+
+
+
+function execInstantiateComponet(ctx, vr, fn, sels) {
+
+ if (fn.type != "FUNCTIONCALL") return ctx.throwError(fn, "Right type of instantiate component must be a function call");
+
+ const templateName = fn.name;
+
+ const template = ctx.templates[templateName];
+ if (!template) return ctx.throwError("Invalid Template");
+
+ const paramValues = [];
+ for (let i=0; i< fn.params.length; i++) {
+ const v = exec(ctx, fn.params[i]);
+ if (ctx.error) return;
+ for (let j=0; j v.s.length-2) return ctx.throwError(ast, "Too many selectors");
+ for (let i=0; i= v.s[i] / v.s[i+1]) return ctx.throwError(ast, "Out of Range");
+ if (sels[i] < 0 ) return ctx.throwError(ast, "selector negative");
+ o += sels[i] * v.s[i+1];
+ s = v.s[i+1];
+ }
+
+ if (v.t == "V") {
+ return {
+ t: "V",
+ s: v.s.slice(sels.length),
+ v: v.v.slice(o, o+s)
+ };
+ } else if (v.t == "S") {
+ return {
+ t: "S",
+ s: v.s.slice(sels.length),
+ sIdx: o + v.sIdx
+ };
+ } else if (v.t == "C") {
+ return {
+ t: "C",
+ s: v.s.slice(sels.length),
+ cIdx: o
+ };
+ } else {
+ assert(false);
+ }
+}
+
+
+
+
+function execPin(ctx, ast) {
+ const selsC = [];
+ for (let i=0; i< ast.component.selectors.length; i++) {
+ const sel = exec(ctx, ast.component.selectors[i]);
+ if (ctx.error) return;
+
+ if (sel.s[0] != 1) return ctx.throwError(ast, "Component selector cannot be an array");
+ if (sel.v[0].t != "N") return NQVAL;
+ selsC.push(sel.v[0].v.toJSNumber());
+ }
+
+ const cIdx = ctx.getComponentIdx(ast.component.name, selsC);
+ if (cIdx<0) return ctx.throwError(ast.component, "Component does not exists: "+ast.component.name);
+
+ const selsP = [];
+ for (let i=0; i< ast.pin.selectors.length; i++) {
+ const sel = exec(ctx, ast.pin.selectors[i]);
+ if (ctx.error) return;
+ if (sel.s[0] != 1) return ctx.throwError(ast, "Signal selector cannot be an array");
+ if (sel.v[0].t != "N") return NQVAL;
+ selsP.push(sel.v[0].v.toJSNumber());
+ }
+ const sIdx = ctx.components[cIdx].names.getSignalIdx(ast.pin.name, selsP);
+
+ if (sIdx<0) ctx.throwError(ast, "Signal not defined:" + buildFullName() );
+ return {
+ t: "S",
+ sIdx: sIdx,
+ s: utils.accSizes(ctx.components[cIdx].names.o[ast.pin.name].sizes).slice(selsP.length)
+ };
+
+ function buildFullName() {
+ return ast.component.name + sels2str(selsC) + "." + ast.pin.name + sels2str(selsP);
+ }
+
+ function sels2str(sels) {
+ let S = "";
+ for (let i=0; i< sels.length; i++) {
+ S += "[" + sels[i] + "]";
+ }
+ return S;
+ }
+}
+
+function execLoop(ctx, ast) {
+
+ if (ast.init) {
+ exec(ctx, ast.init);
+ if (ctx.error) return;
+ }
+
+ let v = exec(ctx, ast.condition);
+ if (ctx.error) return;
+ if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
+ if (v.v[0].t != "N") {
+ iterateAST(ast, (ast) => {
+ if (ast.type == "OP") {
+ if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
+ return ctx.throwError(ast.condition, "Constraint inside a calculating block");
+ }
+ }
+ });
+
+ // Assert no constraints
+ return;
+ }
+
+ while ((!v.v[0].v.isZero())&&(!ctx.returnValue)) {
+ exec(ctx, ast.body);
+ if (ctx.error) return;
+
+ if (ast.step) {
+ exec(ctx, ast.step);
+ if (ctx.error) return;
+ }
+
+ v = exec(ctx, ast.condition);
+ if (ctx.error) return;
+ if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition in loop cannot be an array");
+ if (v.v[0].t != "N") return ctx.throwError(ast.condition, "Condition result not a number");
+ }
+}
+
+function execCompute(ctx, ast) {
+ iterateAST(ast, (ast) => {
+ if (ast.type == "OP") {
+ if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
+ return ctx.throwError(ast.condition, "Constraint inside a calculating block");
+ }
+ }
+ });
+}
+
+function execIf(ctx, ast) {
+ let v = exec(ctx, ast.condition);
+ if (ctx.error) return;
+ if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
+ if (v.v[0].t != "N") {
+ iterateAST(ast, (ast) => {
+ if (ast.type == "OP") {
+ if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
+ return ctx.throwError(ast.condition, "Constraint inside a calculating block");
+ }
+ }
+ });
+
+ // Assert no constraints
+ return;
+ }
+
+ if (!v.v[0].v.isZero()) {
+ exec(ctx, ast.then);
+ } else {
+ if (ast.else) {
+ exec(ctx, ast.else);
+ }
+ }
+}
+
+
+function execTerCon(ctx, ast) {
+ let v = exec(ctx, ast.values[0]);
+ if (ctx.error) return;
+ if (v.s[0] != 1) return ctx.throwError(ast.condition, "Condition cannot be an array");
+ if (v.v[0].t != "N") {
+ iterateAST(ast, (ast) => {
+ if (ast.type == "OP") {
+ if (["==>", "<==", "==="].indexOf(ast.op) >= 0) {
+ return ctx.throwError(ast.condition, "Constraint inside a calculating block");
+ }
+ }
+ });
+
+ // Assert no constraints
+ return NQVAL;
+ }
+
+ if (!v.v[0].v.isZero()) {
+ return exec(ctx, ast.values[1]);
+ } else {
+ return exec(ctx, ast.values[2]);
+ }
+}
+
+function execOp(ctx, ast, op, nOps) {
+ const operands = [];
+ for (let i=0; i= 0) sIdx = ctx.signals[sIdx].e;
+ res.coefs[sIdx] = ctx.field.one;
+ return res;
+ } else {
+ ctx.throwError(ast, "Invalid type: " + a.t);
+ }
+}
+
+function execConstrain(ctx, ast) {
+ ast.fileName = ctx.fileName;
+ ast.filePath = ctx.filePath;
+ const a = exec(ctx, ast.values[0]);
+ if (ctx.error) return;
+ const aV = val(ctx, a, ast.values[0]);
+ const b = exec(ctx, ast.values[1]);
+ if (ctx.error) return;
+ const bV = val(ctx, b, ast.values[1]);
+
+ const res = ctx.lc.sub(aV,bV);
+ if (res.type == "NQ") return ctx.throwError(ast, "Non Quadratic constraint");
+
+ if (!ctx.lc.isZero(res)) {
+ ctx.constraints.push(ctx.lc.toQEX(res));
+ if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
+ }
+
+ return a;
+}
+
+function execSignalAssignConstrain(ctx, ast) {
+ const v = execAssignement(ctx,ast);
+ if (ctx.error) return;
+ execConstrain(ctx, ast);
+ if (ctx.error) return;
+ return v;
+}
+
+function execInclude(ctx, ast) {
+ if (ast.block) {
+
+ const oldFilePath = ctx.filePath;
+ const oldFileName = ctx.fileName;
+
+ ctx.filePath = ast.filePath;
+ ctx.fileName = ast.fileName;
+
+ exec(ctx, ast.block); // Process only one.
+
+ ctx.filePath = oldFilePath;
+ ctx.fileName = oldFileName;
+ }
+}
+
+function execArray(ctx, ast) {
+ const res = {t: "V", v:[]};
+
+
+ let subSize = null;
+
+ for (let i=0; i {
+ while ((scopeLabels.length>0)&&(!level.startsWith(scopeLabels[scopeLabels.length-1]))) {
+ scopes.pop();
+ scopeLabels.pop();
+ }
+ if (ast.type == "DECLARE") {
+ if (ast.declareType == "COMPONENT") {
+ ast.refId = define(ast.name.name, {t: "C"});
+ } else if ((ast.declareType == "SIGNALIN")||
+ (ast.declareType == "SIGNALOUT")||
+ (ast.declareType == "SIGNAL")) {
+ ast.refId = define(ast.name.name, {t: "S"});
+ } else if (ast.declareType == "VARIABLE") {
+ ast.refId = define(ast.name.name, {t: "V", s: null});
+ } else {
+ return ctx.throwError(ast, "Invalid declaration: " + ast.declareType);
+ }
+ } else if (["BLOCK", "FOR", "IF", "WHILE", "COMPUTE"].indexOf(ast.type) >= 0) {
+ scopeLabels.push(level);
+ scopes.push({});
+ } else if (ast.type == "TEMPLATEDEF") {
+ ast.refId = define(ast.name, {t: "T"});
+ } else if (ast.type == "FUNCTIONDEF") {
+ ast.refId = define(ast.name, {t: "F"});
+ } else if (ast.type == "INCLUDE") {
+ const incFileName = path.resolve(ctx.filePath, ast.file);
+ const incFilePath = path.dirname(incFileName);
+
+ ctx.includedFiles = ctx.includedFiles || [];
+ if (ctx.includedFiles[incFileName]) {
+ ctx.bloc = null; // Include already included...
+ return;
+ }
+
+ const src = fs.readFileSync(incFileName, "utf8");
+
+ if (!src) return ctx.throwError(ast, "Include file not found: "+incFileName);
+
+ ctx.includedFiles[incFileName] = src.split("\n");
+
+ ast.block = parser.parse(src);
+ ast.filePath = incFilePath;
+ ast.fileName = incFileName;
+
+ const oldFilePath = ctx.filePath;
+ const oldFileName = ctx.fileName;
+ ctx.filePath = incFilePath;
+ ctx.fileName = incFileName;
+
+ scopes.push({});
+ createRefs(ctx, ast.block);
+ scopes.pop();
+
+ ctx.filePath = oldFilePath;
+ ctx.fileName = oldFileName;
+ } else if (ast.type == "VARIABLE") {
+ ast.refId = name2ref(ast.name);
+ }
+
+ function define(name, entry) {
+ if (scopes[scopes.length-1][name]) return ctx.throwError(ast, `Name already defined: ${name}`);
+ for (let i=scopes.length-2; i>=0; i--) {
+ if (scopes[i][name]) ctx.logWarning(ast, `Shadowing variable: ${name}`);
+ }
+ const refId = ctx.refs.length;
+ entry.refId = refId;
+ ctx.refs.push(entry);
+ scopes[scopes.length-1][name] = refId;
+ return refId;
+ }
+
+ function name2ref(n) {
+ for (let i=scopes.length-1; i>=0; i--) {
+ if (typeof scopes[i][n] !== "undefined") return scopes[i][n];
+ }
+ return -1;
+ }
+
+
+ });
+
+}
+
+
diff --git a/src/exec.js b/src/exec.js
deleted file mode 100644
index 1d67bc5..0000000
--- a/src/exec.js
+++ /dev/null
@@ -1,1335 +0,0 @@
-/*
- 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 .
-*/
-
-const path = require("path");
-const fs = require("fs");
-
-const utils = require("./utils");
-
-const bigInt = require("big-integer");
-const __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
-const __MASK__ = new bigInt(2).pow(253).minus(1);
-
-const lc = require("./lcalgebra");
-const parser = require("../parser/jaz.js").parser;
-
-/* TODO: Add lines information
-
-function setLines(dst, first, last) {
- last = last || first;
- dst.first_line = first.first_line;
- dst.first_column = first.first_column;
- dst.last_line = last.last_line;
- dst.last_column = last.last_column;
-}
-*/
-
-module.exports = exec;
-
-
-function exec(ctx, ast) {
- if (!ast) {
- return error(ctx, ast, "Null AST");
- }
- if ((ast.type == "NUMBER") || (ast.type == "LINEARCOMBINATION") || (ast.type =="SIGNAL") || (ast.type == "QEQ")) {
- return ast;
- } else if (ast.type == "VARIABLE") {
- return execVariable(ctx, ast);
- } else if (ast.type == "PIN") {
- return execPin(ctx, ast);
- } else if (ast.type == "OP") {
- if (ast.op == "=") {
- return execVarAssignement(ctx, ast);
- } else if (ast.op == "<--") {
- return execSignalAssign(ctx, ast);
- } else if (ast.op == "<==") {
- return execSignalAssignConstrain(ctx, ast);
- } else if (ast.op == "===") {
- return execConstrain(ctx, ast);
- } else if (ast.op == "+=") {
- return execVarAddAssignement(ctx, ast);
- } else if (ast.op == "*=") {
- return execVarMulAssignement(ctx, ast);
- } else if (ast.op == "+") {
- return execAdd(ctx, ast);
- } else if (ast.op == "-") {
- return execSub(ctx, ast);
- } else if (ast.op == "UMINUS") {
- return execUMinus(ctx, ast);
- } else if (ast.op == "*") {
- return execMul(ctx, ast);
- } else if (ast.op == "%") {
- return execMod(ctx, ast);
- } else if (ast.op == "PLUSPLUSRIGHT") {
- return execPlusPlusRight(ctx, ast);
- } else if (ast.op == "PLUSPLUSLEFT") {
- return execPlusPlusLeft(ctx, ast);
- } else if (ast.op == "MINUSMINUSRIGHT") {
- return execMinusMinusRight(ctx, ast);
- } else if (ast.op == "MINUSMINUSLEFT") {
- return execMinusMinusLeft(ctx, ast);
- } else if (ast.op == "/") {
- return execDiv(ctx, ast);
- } else if (ast.op == "\\") {
- return execIDiv(ctx, ast);
- } else if (ast.op == "**") {
- return execExp(ctx, ast);
- } else if (ast.op == "&") {
- return execBAnd(ctx, ast);
- } else if (ast.op == "|") {
- return execBOr(ctx, ast);
- } else if (ast.op == "^") {
- return execBXor(ctx, ast);
- } else if (ast.op == "~") {
- return execBNot(ctx, ast);
- } else if (ast.op == "&&") {
- return execAnd(ctx, ast);
- } else if (ast.op == "||") {
- return execOr(ctx, ast);
- } else if (ast.op == "!") {
- return execLNot(ctx, ast);
- } else if (ast.op == "<<") {
- return execShl(ctx, ast);
- } else if (ast.op == ">>") {
- return execShr(ctx, ast);
- } else if (ast.op == "<") {
- return execLt(ctx, ast);
- } else if (ast.op == ">") {
- return execGt(ctx, ast);
- } else if (ast.op == "<=") {
- return execLte(ctx, ast);
- } else if (ast.op == ">=") {
- return execGte(ctx, ast);
- } else if (ast.op == "==") {
- return execEq(ctx, ast);
- } else if (ast.op == "!=") {
- return execNeq(ctx, ast);
- } else if (ast.op == "?") {
- return execTerCon(ctx, ast);
- } else {
- error(ctx, ast, "Invalid operation: " + ast.op);
- }
- } else if (ast.type == "DECLARE") {
- if (ast.declareType == "COMPONENT") {
- return execDeclareComponent(ctx, ast);
- } else if ((ast.declareType == "SIGNALIN")||
- (ast.declareType == "SIGNALOUT")||
- (ast.declareType == "SIGNAL")) {
- return execDeclareSignal(ctx, ast);
- } else if (ast.declareType == "VARIABLE") {
- return execDeclareVariable(ctx, ast);
- } else {
- error(ctx, ast, "Invalid declaration: " + ast.declareType);
- }
- } else if (ast.type == "FUNCTIONCALL") {
- return execFunctionCall(ctx, ast);
- } else if (ast.type == "BLOCK") {
- return execBlock(ctx, ast);
- } else if (ast.type == "COMPUTE") {
- return ;
- } else if (ast.type == "FOR") {
- return execFor(ctx, ast);
- } else if (ast.type == "WHILE") {
- return execWhile(ctx, ast);
- } else if (ast.type == "IF") {
- return execIf(ctx, ast);
- } else if (ast.type == "RETURN") {
- return execReturn(ctx, ast);
- } else if (ast.type == "TEMPLATEDEF") {
- return execTemplateDef(ctx, ast);
- } else if (ast.type == "FUNCTIONDEF") {
- return execFunctionDef(ctx, ast);
- } else if (ast.type == "INCLUDE") {
- return execInclude(ctx, ast);
- } else if (ast.type == "ARRAY") {
- return execArray(ctx, ast);
- } else {
- error(ctx, ast, "Invalid AST node type: " + ast.type);
- }
-}
-
-function error(ctx, ast, errStr) {
- ctx.error = {
- pos: {
- first_line: ast.first_line,
- first_column: ast.first_column,
- last_line: ast.last_line,
- last_column: ast.last_column
- },
- errStr: errStr,
- errFile: ctx.fileName,
- ast: ast,
- message: errStr
- };
-}
-
-function iterateSelectors(ctx, sizes, baseName, fn) {
- if (sizes.length == 0) {
- return fn(baseName);
- }
- const res = [];
- for (let i=0; i=0; i--) {
- if (ctx.scopes[i][name]) return select(ctx.scopes[i][name].value, sels);
- }
- return null;
-
- function reduce(v, _sels, idxName) {
- let sels = _sels || [];
- let sizes = v.sizes || [];
-
- let accSizes = [1];
- for (let i=sizes.length-1; i>0; i--) {
- accSizes = [accSizes[0]*sizes[i], ...accSizes];
- }
- const res = Object.assign({}, v);
- res.sizes = sizes.slice(sels.length);
- for (let i=0; i=0; i--) {
- if (ctx.scopes[i][name]) {
- if (ctx.scopes[i][name].type == "COMPONENT") {
- return [null, sels, "COMPONENT"];
- } else {
- return select(ctx.scopes[i][name].value, sels, ctx.scopes[i][name].type);
- }
- }
- }
- return [null, [], ""];
-}
-
-
-function setScopeRef(ctx, name, sels, value) {
- let l = getScopeLevel(ctx, name);
- if (l==-1) l= ctx.scopes.length-1;
-
- if (sels.length == 0) {
- ctx.scopes[l][name].value = value;
- } else {
- setScopeArray(ctx.scopes[l][name].value, sels);
- }
-
- function setScopeArray(a, sels) {
- if (sels.length == 1) {
- a[sels[0]] = value;
- } else {
- setScopeArray(a[sels[0]], sels.slice(1));
- }
- }
-}
-
-function getScopeLevel(ctx, name) {
- for (let i=ctx.scopes.length-1; i>=0; i--) {
- if (ctx.scopes[i][name]) return i;
- }
- return -1;
-}
-
-function execBlock(ctx, ast) {
- for (let i=0; i=0)) return error(ctx, ast, "Cannot assign to a signal with `=` use <-- or <== ops");
- if ((["NUMBER", "COMPONENT"].indexOf(typ) >= 0 )&&(ast.op != "=")) return error(ctx, ast, `Cannot assign to a var with ${ast.op}. use = op`);
-
- const res = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
- setScopeRef(ctx, v.name, sels, res);
-
- return v;
-}
-
-function execLt(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.lt(b.value) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execGt(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.gt(b.value) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execLte(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.lesserOrEquals(b.value) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execGte(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.greaterOrEquals(b.value) ? bigInt(1) : bigInt(0)
- };
-}
-
-
-function execEq(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.eq(b.value) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execNeq(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.eq(b.value) ? bigInt(0) : bigInt(1)
- };
-}
-
-function execBAnd(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.and(b.value).and(__MASK__)
- };
-}
-
-function execBOr(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.or(b.value).and(__MASK__)
- };
-}
-
-function execBXor(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.xor(b.value).and(__MASK__)
- };
-}
-
-function execBNot(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value) return { type: "NUMBER" };
-
- const res = lc.negate(a);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- return {
- type: "NUMBER",
- value: a.value.xor(__MASK__).and(__MASK__)
- };
-}
-
-
-
-function execAnd(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: (a.value.neq(0) && b.value.neq(0)) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execOr(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: (a.value.neq(0) || b.value.neq(0)) ? bigInt(1) : bigInt(0)
- };
-}
-
-function execLNot(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: (a.value.eq(0)) ? bigInt(1) : bigInt(0)
- };
-}
-
-
-function execShl(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- const v = b.value.greater(256) ? 256 : b.value.value;
- return {
- type: "NUMBER",
- value: a.value.shiftLeft(v).and(__MASK__)
- };
-}
-
-function execShr(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- const v = b.value.greater(256) ? 256 : b.value.value;
- return {
- type: "NUMBER",
- value: a.value.shiftRight(v).and(__MASK__)
- };
-}
-
-function execMod(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.mod(b.value)
- };
-}
-
-
-function execExp(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- return {
- type: "NUMBER",
- value: a.value.modPow(b.value, __P__)
- };
-}
-
-function execDiv(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- if (b.value.isZero()) return error(ctx, ast, "Division by zero");
- return {
- type: "NUMBER",
- value: a.value.times(b.value.modInv(__P__)).mod(__P__)
- };
-}
-
-function execIDiv(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- if (a.type != "NUMBER") return { type: "NUMBER" };
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- if (b.type != "NUMBER") return { type: "NUMBER" };
- if (!a.value || !b.value) return { type: "NUMBER" };
- if (b.value.isZero()) return error(ctx, ast, "Division by zero");
- return {
- type: "NUMBER",
- value: a.value.divide(b.value)
- };
-}
-
-function execAdd(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
- const res = lc.add(a,b);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- return res;
-}
-
-function execSub(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
- const res = lc.sub(a,b);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- return res;
-}
-
-function execUMinus(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
-
- const res = lc.negate(a);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- return res;
-}
-
-function execMul(ctx, ast) {
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
- const res = lc.mul(a,b);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- return res;
-}
-
-
-function execVarAddAssignement(ctx, ast) {
- const res = execAdd(ctx,{ values: [ast.values[0], ast.values[1]] } );
- if (ctx.error) return;
- return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
-}
-
-function execVarMulAssignement(ctx, ast) {
- const res = execMul(ctx,{ values: [ast.values[0], ast.values[1]] } );
- if (ctx.error) return;
- return execVarAssignement(ctx, { op:"=", values: [ast.values[0], res] });
-}
-
-function execPlusPlusRight(ctx, ast) {
- const resBefore = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
- if (ctx.error) return;
- execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
- return resBefore;
-}
-
-function execPlusPlusLeft(ctx, ast) {
- if (ctx.error) return;
- const resAfter = execAdd(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
- if (ctx.error) return;
- execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
- return resAfter;
-}
-
-function execMinusMinusRight(ctx, ast) {
- const resBefore = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
- if (ctx.error) return;
- execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
- return resBefore;
-}
-
-function execMinusMinusLeft(ctx, ast) {
- if (ctx.error) return;
- const resAfter = execSub(ctx,{ values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}] } );
- if (ctx.error) return;
- execVarAssignement(ctx, { op:"=", values: [ast.values[0], resAfter] });
- return resAfter;
-}
-
-function execTerCon(ctx, ast) {
- const cond = exec(ctx, ast.values[0]);
- if (ctx.error) return;
-
- if (!cond.value) return { type: "NUMBER" };
-
- if (cond.value.neq(0)) {
- return exec(ctx, ast.values[1]);
- } else {
- return exec(ctx, ast.values[2]);
- }
-}
-
-function execSignalAssign(ctx, ast) {
- let vDest;
- if (ast.values[0].type == "DECLARE") {
- vDest = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- } else {
- vDest = ast.values[0];
- }
-
- let dst;
- if (vDest.type == "VARIABLE") {
- dst = getScope(ctx, vDest.name, vDest.selectors);
- if (ctx.error) return;
- } else if (vDest.type == "PIN") {
- dst = execPin(ctx, vDest);
- if (ctx.error) return;
- } else {
- error(ctx, ast, "Bad assignement");
- }
-
- if (!dst) return error(ctx, ast, "Signal not defined");
- if (dst.type != "SIGNAL") return error(ctx, ast, "Signal assigned to a non signal");
-
- let sDest=ctx.signals[dst.sIdx];
- if (!sDest) return error(ctx, ast, "Invalid signal: "+dst.sIdx);
-
- let isOut = (sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT);
- while (sDest.e>=0) {
- sDest=ctx.signals[sDest.e];
- isOut = isOut || ((sDest.o & ctx.MAIN)&&(sDest.o & ctx.OUT));
- }
-
- if (sDest.value) return error(ctx, ast, "Signals cannot be assigned twice");
-
- let src = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
-
- /*
- let vSrc;
- if (ast.values[1].type == "DECLARE") {
- vSrc = exec(ctx, ast.values[1]);
- if (ctx.error) return;
- } else {
- vSrc = ast.values[1];
- }
-
- if (vSrc.type == "VARIABLE") {
- src = getScope(ctx, vSrc.name, vSrc.selectors);
- if (!src) error(ctx, ast, "Variable not defined: " + vSrc.name);
- if (ctx.error) return;
- } else if (vSrc.type == "PIN") {
- src = execPin(ctx, vSrc);
- }
- */
-
- let assignValue = true;
- if (src.type == "SIGNAL") {
- let sSrc = ctx.signals[src.sIdx];
- let isIn = (sSrc.o & ctx.main)&&(sSrc.o & ctx.IN);
- while (sSrc.e>=0) {
- sSrc=ctx.signals[sSrc.e];
- isIn = isIn || ((sSrc.o & ctx.main)&&(sSrc.o & ctx.IN));
- }
-
- // Skip if an out is assigned directly to an input.
- if ((!isIn)||(!isOut)) {
- sDest.e = src.sIdx;
- while (sDest.e >= 0) sDest=ctx.signals[sDest.e];
- assignValue = false;
- }
- }
-
- if (assignValue) {
- // const resLC = exec(ctx, vSrc);
- if (ctx.error) return;
-
- // const v = lc.evaluate(ctx, resLC);
- const v = lc.evaluate(ctx, src);
-
- if (v.value) {
- sDest.v = v.value;
- }
- }
-
- return vDest;
-}
-
-function execConstrain(ctx, ast) {
- ast.fileName = ctx.fileName;
- ast.filePath = ctx.filePath;
- const a = exec(ctx, ast.values[0]);
- if (ctx.error) return;
- const b = exec(ctx, ast.values[1]);
- if (ctx.error) return;
-
- const res = lc.sub(a,b);
- if (res.type == "ERROR") return error(ctx, ast, res.errStr);
-
- if (!lc.isZero(res)) {
- ctx.constraints.push(lc.toQEQ(res));
- if ((ctx.constraints.length % 10000 == 0)&&(ctx.constraints.length>0)) console.log("Constraints: " + ctx.constraints.length);
- }
-
- return res;
-}
-
-function execSignalAssignConstrain(ctx, ast) {
- const v = execSignalAssign(ctx,ast);
- if (ctx.error) return;
- execConstrain(ctx, ast);
- if (ctx.error) return;
- return v;
-}
-
-function execInclude(ctx, ast) {
- const incFileName = path.resolve(ctx.filePath, ast.file);
- const incFilePath = path.dirname(incFileName);
-
- ctx.includedFiles = ctx.includedFiles || [];
- if (ctx.includedFiles[incFileName]) return;
-
-
- const src = fs.readFileSync(incFileName, "utf8");
-
- ctx.includedFiles[incFileName] = src.split("\n");
-
- if (!src) return error(ctx, ast, "Include file not found: "+incFileName);
-
- const incAst = parser.parse(src);
-
- const oldFilePath = ctx.filePath;
- const oldFileName = ctx.fileName;
- ctx.filePath = incFilePath;
- ctx.fileName = incFileName;
-
- exec(ctx, incAst);
-
- ast.block = incAst;
-
- ctx.filePath = oldFilePath;
- ctx.fileName = oldFileName;
-}
-
-function execArray(ctx, ast) {
- const res = [];
-
- for (let i=0; i Invalid AST iteration: " + ast.type);
}
diff --git a/src/lcalgebra.js b/src/lcalgebra.js
index 4419931..5ed8c43 100644
--- a/src/lcalgebra.js
+++ b/src/lcalgebra.js
@@ -18,491 +18,555 @@
*/
/*
- NUMBER: a
+ // Number
+ ///////////////
+ N: a
{
- type: "NUMBER",
- value: bigInt(a)
+ t: "N",
+ v: bigInt(a)
}
- LINEARCOMBINATION: c1*s1 + c2*s2 + c3*s3
+ // Signal
+ ///////////////
+ {
+ t: "S",
+ sIdx: sIdx
+ }
+ // Linear Convination
+ //////////////////
+ LC: c1*s1 + c2*s2 + c3*s3
{
- type: "LINEARCOMBINATION",
- values: {
+ 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: {...} }
+ }
- QEQ: a*b + c WHERE a,b,c are LINEARCOMBINATION
+ NQ: Non quadratic expression
{
- type: "QEQ"
- a: { type: LINEARCOMBINATION, values: {...} },
- b: { type: LINEARCOMBINATION, values: {...} },
- c: { type: LINEARCOMBINATION, values: {...} }
+ t: "NQ"
}
*/
/*
-+ NUM LC QEQ
-NUM NUM LC QEQ
-LC LC LC QEQ
-QEQ QEQ QEQ ERR
-
-* NUM LC QEQ
-NUM NUM LC QEQ
-LC LC QEQ ERR
-QEQ QEQ ERR ERR
++ 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 __P__ = new bigInt("21888242871839275222246405745257275088548364400416034343698204186575808495617");
+const utils = require("./utils");
const sONE = 0;
-const utils = require("./utils.js");
-
-exports.add = add;
-exports.mul = mul;
-exports.evaluate = evaluate;
-exports.negate = negate;
-exports.sub = sub;
-exports.toQEQ = toQEQ;
-exports.isZero = isZero;
-exports.toString = toString;
-exports.canonize = canonize;
-exports.substitute = substitute;
-
-function signal2lc(a) {
- let lc;
- if (a.type == "SIGNAL") {
- lc = {
- type: "LINEARCOMBINATION",
- values: {}
- };
- lc.values[a.sIdx] = bigInt(1);
- return lc;
- } else {
- return a;
+
+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]);
+ });
}
-}
-function clone(a) {
- const res = {};
- res.type = a.type;
- if (a.type == "NUMBER") {
- res.value = bigInt(a.value);
- } else if (a.type == "LINEARCOMBINATION") {
- res.values = {};
- for (let k in a.values) {
- res.values[k] = bigInt(a.values[k]);
- }
- } else if (a.type == "QEQ") {
- res.a = clone(a.a);
- res.b = clone(a.b);
- res.c = clone(a.c);
- } else if (a.type == "ERROR") {
- res.errStr = a.errStr;
- } else {
- res.type = "ERROR";
- res.errStr = "Invilid type when clonning: "+a.type;
+ _genNQOp(op, nOps) {
+ const self=this;
+ self[op] = function() {
+ const operands = [];
+ for (let i=0; i= 0) {
- return getSignalValue(ctx, s.e);
- } else {
- const res = {
- type: "NUMBER"
- };
- if (s.v) {
- res.value = s.v;
+ neg(_a) {
+ const a = this._signal2lc(_a);
+ let res = this._clone(a);
+ if (res.t == "N") {
+ res.v = this.field.neg(a.v);
+ } else if (res.t == "LC") {
+ for (let k in res.coefs) {
+ res.coefs[k] = this.field.neg(res.coefs[k]);
+ }
+ } else if (res.t == "QEX") {
+ res.a = this.neg(res.a);
+ res.c = this.neg(res.c);
+ } else {
+ res = {t: "NQ"};
}
return res;
}
-}
-function evaluate(ctx, n) {
- if (n.type == "NUMBER") {
- return n;
- } else if (n.type == "SIGNAL") {
- return getSignalValue(ctx, n.sIdx);
- } else if (n.type == "LINEARCOMBINATION") {
- const v= {
- type: "NUMBER",
- value: bigInt(0)
- };
- for (let k in n.values) {
- const s = getSignalValue(ctx, k);
- if (s.type != "NUMBER") return {type: "ERROR", errStr: "Invalid signal in linear Combination: " + k};
- if (!s.value) return { type: "NUMBER" };
- v.value = v.value.add( n.values[k].times(s.value)).mod(__P__);
+ sub(a, b) {
+ return this.add(a, this.neg(b));
+ }
+
+ div(a, b) {
+ if (b.t == "N") {
+ if (b.v.isZero()) throw new Error("Division by zero");
+ const inv = {
+ t: "N",
+ v: this.field.inv(b.v)
+ };
+ return this.mul(a, inv);
+ } else {
+ return {t: "NQ"};
}
- return v;
- } else if (n.type == "QEQ") {
- const a = evaluate(ctx, n.a);
- if (a.type == "ERROR") return a;
- if (!a.value) return { type: "NUMBER" };
- const b = evaluate(ctx, n.b);
- if (b.type == "ERROR") return b;
- if (!b.value) return { type: "NUMBER" };
- const c = evaluate(ctx, n.c);
- if (c.type == "ERROR") return c;
- if (!c.value) return { type: "NUMBER" };
-
- return {
- type: "NUMBER",
- value: (a.value.times(b.value).add(c.value)).mod(__P__)
- };
- } else if (n.type == "ERROR") {
- return n;
- } else {
- return {type: "ERROR", errStr: "Invalid type in evaluate: "+n.type};
}
-}
-function negate(_a) {
- const a = signal2lc(_a);
- let res = clone(a);
- if (res.type == "NUMBER") {
- res.value = __P__.minus(a.value).mod(__P__);
- } else if (res.type == "LINEARCOMBINATION") {
- for (let k in res.values) {
- res.values[k] = __P__.minus(res.values[k]).mod(__P__);
+ pow(a, b) {
+ if (b.t == "N") {
+ if (b.v.isZero()) {
+ if (this.isZero(a)) {
+ throw new Error("Zero to the Zero");
+ }
+ return {
+ t: "N",
+ v: this.field.one
+ };
+ } else if (b.v.eq(this.field.one)) {
+ return a;
+ } else if (b.v.eq(bigInt(2))) {
+ return this.mul(a,a);
+ } else {
+ if (a.t=="N") {
+ return {
+ t: "N",
+ v: this.field.pow(a.v, b.v)
+ };
+ } else {
+ return {t: "NQ"};
+ }
+ }
+ } else {
+ return {t: "NQ"};
}
- } else if (res.type == "QEQ") {
- res.a = negate(res.a);
- res.c = negate(res.c);
- } else if (res.type == "ERROR") {
- return res;
- } else {
- res = {type: "ERROR", errStr: "LC Negate invalid Type: "+res.type};
}
- return res;
-}
-function sub(a, b) {
- return add(a, negate(b));
-}
+ substitute(where, signal, equivalence) {
+ if (equivalence.t != "LC") throw new Error("Equivalence must be a Linear Combination");
+ if (where.t == "LC") {
+ if (!utils.isDefined(where.coefs[signal]) || where.coefs[signal].isZero()) return where;
+ const res=this._clone(where);
+ const coef = res.coefs[signal];
+ for (let k in equivalence.coefs) {
+ if (k != signal) {
+ const v = this.field.mul( coef, equivalence.coefs[k] );
+ if (!utils.isDefined(res.coefs[k])) {
+ res.coefs[k]=v;
+ } else {
+ res.coefs[k]= this.field.add(res.coefs[k],v);
+ }
+ if (res.coefs[k].isZero()) delete res.coefs[k];
+ }
+ }
+ delete res.coefs[signal];
+ return res;
+ } else if (where.t == "QEX") {
+ const res = {
+ t: "QEX",
+ a: this.substitute(where.a, signal, equivalence),
+ b: this.substitute(where.b, signal, equivalence),
+ c: this.substitute(where.c, signal, equivalence)
+ };
+ return res;
+ } else {
+ return where;
+ }
+ }
-function toQEQ(a) {
- if (a.type == "NUMBER") {
- return {
- type: "QEQ",
- a: {type: "LINEARCOMBINATION", values: {}},
- b: {type: "LINEARCOMBINATION", values: {}},
- c: {type: "LINEARCOMBINATION", values: {sONE: bigInt(a.value)}}
- };
- } else if (a.type == "LINEARCOMBINATION") {
- return {
- type: "QEQ",
- a: {type: "LINEARCOMBINATION", values: {}},
- b: {type: "LINEARCOMBINATION", values: {}},
- c: clone(a)
- };
- } else if (a.type == "QEQ") {
- return clone(a);
- } else if (a.type == "ERROR") {
- return clone(a);
- } else {
- return {type: "ERROR", errStr: "toQEQ invalid Type: "+a.type};
+ toQEX(a) {
+ if (a.t == "N") {
+ const res = {
+ t: "QEX",
+ a: {t: "LC", coefs: {}},
+ b: {t: "LC", coefs: {}},
+ c: {t: "LC", coefs: {}}
+ };
+ res.c[sONE] = a.v;
+ return res;
+ } else if (a.t == "LC") {
+ return {
+ t: "QEX",
+ a: {t: "LC", coefs: {}},
+ b: {t: "LC", coefs: {}},
+ c: this._clone(a)
+ };
+ } else if (a.t == "QEX") {
+ return this._clone(a);
+ } else {
+ throw new Error(`Type ${a.t} can not be converted to QEX`);
+ }
}
-}
-function isZero(a) {
- if (a.type == "NUMBER") {
- return a.value.isZero();
- } else if (a.type == "LINEARCOMBINATION") {
- for (let k in a.values) {
- if (!a.values[k].isZero()) return false;
+ isZero(a) {
+ if (a.t == "N") {
+ return a.v.isZero();
+ } else if (a.t == "LC") {
+ for (let k in a.coefs) {
+ if (!a.coefs[k].isZero()) return false;
+ }
+ return true;
+ } else if (a.t == "QEX") {
+ return (this.isZero(a.a) || this.isZero(a.b)) && this.isZero(a.c);
+ } else {
+ return false;
}
- return true;
- } else if (a.type == "QEQ") {
- return (isZero(a.a) || isZero(a.b)) && isZero(a.c);
- } else if (a.type == "ERROR") {
- return false;
- } else {
- return false;
}
-}
-function toString(a, ctx) {
- if (a.type == "NUMBER") {
- return a.value.toString();
- } else if (a.type == "LINEARCOMBINATION") {
- let S="";
- for (let k in a.values) {
- if (!a.values[k].isZero()) {
- let c;
- if (a.values[k].greater(__P__.divide(2))) {
- S = S + "-";
- c = __P__.minus(a.values[k]);
- } else {
- if (S!="") S=S+" + ";
- c = a.values[k];
+ toString(a, ctx) {
+ if (a.t == "N") {
+ return a.v.toString();
+ } else if (a.t == "LC") {
+ let S="";
+ for (let k in a.coefs) {
+ if (!a.coefs[k].isZero()) {
+ let c;
+ if (a.coefs[k].greater(this.field.p.divide(2))) {
+ S = S + "-";
+ c = this.field.p.minus(a.coefs[k]);
+ } else {
+ if (S!="") S=S+" + ";
+ c = a.coefs[k];
+ }
+ if (!c.equals(bigInt.one)) {
+ S = S + c.toString() + "*";
+ }
+ let sIdx = k;
+ if (ctx) {
+ while (ctx.signals[sIdx].e>=0) sIdx = ctx.signals[sIdx].e;
+ }
+ S = S + "[" + sIdx + "]";
}
- if (!c.equals(1)) {
- S = S + c.toString() + "*";
- }
- let sIdx = k;
- if (ctx) {
- while (ctx.signals[sIdx].e>=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";
}
- if (S=="") return "0"; else return S;
- } else if (a.type == "QEQ") {
- return "( "+toString(a.a, ctx)+" ) * ( "+toString(a.b, ctx)+" ) + " + toString(a.c, ctx);
- } else if (a.type == "ERROR") {
- return "ERROR: "+a.errStr;
- } else {
- return "INVALID";
}
-}
-function canonize(ctx, a) {
- if (a.type == "LINEARCOMBINATION") {
- const res = clone(a);
- for (let k in a.values) {
- 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 = res.values[k].times(ctx.signals[s].v).mod(__P__);
- if (!res.values[sONE]) {
- res.values[sONE]=v;
- } else {
- res.values[sONE]= res.values[sONE].add(v).mod(__P__);
- }
- delete res.values[k];
- } else if (s != k) {
- if (!res.values[s]) {
- res.values[s]=bigInt(res.values[k]);
- } else {
- res.values[s]= res.values[s].add(res.values[k]).mod(__P__);
- }
- delete res.values[k];
+ 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;
}
- for (let k in res.values) {
- if (res.values[k].isZero()) delete res.values[k];
+
+
+ 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;
}
- return res;
- } else if (a.type == "QEQ") {
- const res = {
- type: "QEQ",
- a: canonize(ctx, a.a),
- b: canonize(ctx, a.b),
- c: canonize(ctx, a.c)
- };
- return res;
- } else {
- return a;
+
}
-}
-function substitute(where, signal, equivalence) {
- if (equivalence.type != "LINEARCOMBINATION") throw new Error("Equivalence must be a Linear Combination");
- if (where.type == "LINEARCOMBINATION") {
- if (!where.values[signal] || where.values[signal].isZero()) return where;
- const res=clone(where);
- const coef = res.values[signal];
- for (let k in equivalence.values) {
- if (k != signal) {
- const v = coef.times(equivalence.values[k]).mod(__P__);
- if (!res.values[k]) {
- res.values[k]=v;
- } else {
- res.values[k]= res.values[k].add(v).mod(__P__);
+
+ 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];
}
- if (res.values[k].isZero()) delete res.values[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;
}
- delete res.values[signal];
- return res;
- } else if (where.type == "QEQ") {
- const res = {
- type: "QEQ",
- a: substitute(where.a, signal, equivalence),
- b: substitute(where.b, signal, equivalence),
- c: substitute(where.c, signal, equivalence)
- };
- return res;
- } else {
- return where;
}
}
+module.exports = LCAlgebra;
+
+
+
+
+
+
diff --git a/src/r1csfile.js b/src/r1csfile.js
index 870adea..b81d620 100644
--- a/src/r1csfile.js
+++ b/src/r1csfile.js
@@ -1,7 +1,6 @@
const fs = require("fs");
const assert = require("assert");
-const lc = require("./lcalgebra");
const bigInt = require("big-integer");
module.exports.buildR1cs = buildR1cs;
@@ -262,19 +261,19 @@ async function buildR1cs(ctx, fileName) {
async function writeConstraint(c) {
await writeLC(c.a);
await writeLC(c.b);
- await writeLC(lc.negate(c.c));
+ await writeLC(ctx.lc.neg(c.c));
}
async function writeLC(lc) {
- const idxs = Object.keys(lc.values);
+ const idxs = Object.keys(lc.coefs);
await writeU32(idxs.length);
- for (let s in lc.values) {
+ for (let s in lc.coefs) {
let lSignal = ctx.signals[s];
while (lSignal.e >=0 ) lSignal = ctx.signals[lSignal.e];
await writeU32(lSignal.id);
- await writeBigInt(lc.values[s]);
+ await writeBigInt(lc.coefs[s]);
}
}
diff --git a/src/utils.js b/src/utils.js
index e790c32..a6bf11b 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -124,3 +124,4 @@ function accSizes2Str(sizes) {
+
diff --git a/src/zqfield.js b/src/zqfield.js
deleted file mode 100644
index d0fef73..0000000
--- a/src/zqfield.js
+++ /dev/null
@@ -1,118 +0,0 @@
-const bigInt = require("big-integer");
-const assert = require("assert");
-
-module.exports = class ZqField {
- constructor(p) {
- this.one = bigInt.one;
- this.zero = bigInt.zero;
- this.p = p;
- this.bitLength = p.bitLength();
- this.mask = bigInt.one.shiftLeft(this.bitLength - 1).minus(bigInt.one);
- }
-
- add(a, b) {
- let res = a.add(b);
- if (res.geq(this.p)) {
- res = res.minus(this.p);
- }
- return res;
- }
-
- sub(a, b) {
- if (a.geq(b)) {
- return a.minus(b);
- } else {
- return this.p.minus(b.minus(a));
- }
- }
-
- neg(a) {
- if (a.isZero()) return a;
- return this.p.minus(a);
- }
-
- mul(a, b) {
- return a.times(b).mod(this.p);
- }
-
- lt(a, b) {
- return a.lt(b) ? bigInt(1) : bigInt(0);
- }
-
- eq(a, b) {
- return a.eq(b) ? bigInt(1) : bigInt(0);
- }
-
- gt(a, b) {
- return a.gt(b) ? bigInt(1) : bigInt(0);
- }
-
- leq(a, b) {
- return a.leq(b) ? bigInt(1) : bigInt(0);
- }
-
- geq(a, b) {
- return a.geq(b) ? bigInt(1) : bigInt(0);
- }
-
- neq(a, b) {
- return a.neq(b) ? bigInt(1) : bigInt(0);
- }
-
- div(a, b) {
- assert(!b.isZero(), "Division by zero");
- return a.times(b.modInv(this.p)).mod(this.p);
- }
-
- idiv(a, b) {
- assert(!b.isZero(), "Division by zero");
- return a.divide(b);
- }
-
- mod(a, b) {
- return a.mod(b);
- }
-
- pow(a, b) {
- return a.modPow(b, this.p);
- }
-
- band(a, b) {
- return a.and(b).and(this.mask);
- }
-
- bor(a, b) {
- return a.or(b).and(this.mask);
- }
-
- bxor(a, b) {
- return a.xor(b).and(this.mask);
- }
-
- bnot(a) {
- return a.xor(this.mask).and(this.mask);
- }
-
- shl(a, b) {
- if (b.geq(this.bitLength)) return bigInt.zero;
- return a.shiftLeft(b).and(this.mask);
- }
-
- shr(a, b) {
- if (b.geq(this.bitLength)) return bigInt.zero;
- return a.shiftRight(b).and(this.mask);
- }
-
- land(a, b) {
- return (a.isZero() || b.isZero) ? bigInt.zero : bigInt.one;
- }
-
- lor(a, b) {
- return (a.isZero() && b.isZero) ? bigInt.zero : bigInt.one;
- }
-
- lnot(a) {
- return a.isZero() ? bigInt.one : bigInt.zero;
- }
-};
-
diff --git a/test/basiccases.js b/test/basiccases.js
index 1b45f82..b5619e7 100644
--- a/test/basiccases.js
+++ b/test/basiccases.js
@@ -44,7 +44,8 @@ async function doTest(circuit, testVectors) {
describe("basic cases", function () {
this.timeout(100000);
-/* it("inout", async () => {
+/*
+ it("inout", async () => {
await doTest(
"inout.circom",
[
@@ -298,6 +299,17 @@ describe("basic cases", function () {
]
);
});
+*/
+ it("Component array 2d", async () => {
+ await doTest(
+ "componentarray2.circom",
+ [
+ [{in: [1,2]}, {out: [1, 256]}],
+ [{in: [0,3]}, {out: [0, 6561]}],
+ ]
+ );
+ });
+/*
it("Constant circuit", async () => {
await doTest(
"constantcircuit.circom",
@@ -306,12 +318,11 @@ describe("basic cases", function () {
[{}, {out: [1,0,1,0, 0,0,0,1, 0,1,1,1, 0,1,0,1, 1,1,1,0, 0,1,1,0, 1,1,0,1, 1,1,0,1]}],
]
);
- }); */
+ });
it("Constant internal circuit", async () => {
await doTest(
"constantinternalcircuit.circom",
[
- // 0xbb67ae85
[{in: 1}, {out: 5}],
[{in: 0}, {out: 4}],
[{in: -2}, {out: 2}],
@@ -319,4 +330,14 @@ describe("basic cases", function () {
]
);
});
+ it("include", async () => {
+ await doTest(
+ "include.circom",
+ [
+ [{in: 3}, {out: 6}],
+ [{in: 6}, {out: 15}],
+ ]
+ );
+ });
+*/
});
diff --git a/test/circuits/arrays.circom b/test/circuits/arrays.circom
index 17df863..30ca5ce 100644
--- a/test/circuits/arrays.circom
+++ b/test/circuits/arrays.circom
@@ -4,18 +4,15 @@
function Add3(arr1, arr2, arr3) {
var res[3];
- var i;
- var j;
-
res[0] = arr1;
res[1] = 0;
- for (i=0; i<2; i += 1) {
+ for (var i=0; i<2; i += 1) {
res[1] = res[1] + arr2[i];
}
res[2] = 0;
- for (i=0; i<2; i++) {
- for (j=0; j<3; j += 1) {
+ for (var i=0; i<2; i++) {
+ for (var j=0; j<3; j += 1) {
res[2] = res[2] + arr3[i][j];
}
}
@@ -27,8 +24,8 @@ template Main() {
signal input in;
signal output out[3];
- var c = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
- var d = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
+ var c[3] = Add3(1, [2,3], [[4,5,6], [7,8,9]]); // [1, 5, 39];
+ var d[3] = Add3(in, [in+1, in+2], [[in+1, in+2, in+3], [in+1, in+2, in+3]]);
out[0] <-- d[0] + c[0];
out[0] === in+c[0];
diff --git a/test/circuits/componentarray2.circom b/test/circuits/componentarray2.circom
new file mode 100644
index 0000000..7b3669e
--- /dev/null
+++ b/test/circuits/componentarray2.circom
@@ -0,0 +1,27 @@
+template Square() {
+ signal input in;
+ signal output out;
+
+ out <== in**2;
+}
+
+template Main(n, nrounds) {
+ signal input in[n];
+ signal output out[n];
+
+ component squares[n][nrounds];
+
+ for (var i=0; i out[i];
+ }
+}
+
+component main = Main(2, 3);
diff --git a/test/circuits/constantcircuit.circom b/test/circuits/constantcircuit.circom
index 6cff5dd..8fec25c 100644
--- a/test/circuits/constantcircuit.circom
+++ b/test/circuits/constantcircuit.circom
@@ -1,6 +1,6 @@
template H(x) {
signal output out[32];
- var c = [0x6a09e667,
+ var c[8] = [0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
diff --git a/test/circuits/include.circom b/test/circuits/include.circom
new file mode 100644
index 0000000..2d25388
--- /dev/null
+++ b/test/circuits/include.circom
@@ -0,0 +1,16 @@
+include "included.circom";
+include "included.circom"; // Include twice is fine. The second one is not included
+
+template Main() {
+ signal input in;
+ signal output out;
+
+ component t1 = T1();
+
+ var a = F1(3);
+
+ in ==> t1.in;
+ t1.out + a ==> out; /// out <-- in**2/3+3
+}
+
+component main = Main();
diff --git a/test/circuits/included.circom b/test/circuits/included.circom
new file mode 100644
index 0000000..70e7dde
--- /dev/null
+++ b/test/circuits/included.circom
@@ -0,0 +1,10 @@
+template T1() {
+ signal input in;
+ signal output out;
+
+ out <== in**2/3;
+}
+
+function F1(a) {
+ return a**2/3;
+}