arnaucube
arnaucube/circomPublic Self-hosted repository
circom/src/compiler.js
src/compiler.jsRaw
  1/*
  2    Copyright 2018 0KIMS association.
  3
  4    This file is part of circom (Zero Knowledge Circuit Compiler).
  5
  6    circom is a free software: you can redistribute it and/or modify it
  7    under the terms of the GNU General Public License as published by
  8    the Free Software Foundation, either version 3 of the License, or
  9    (at your option) any later version.
 10
 11    circom is distributed in the hope that it will be useful, but WITHOUT
 12    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 13    or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
 14    License for more details.
 15
 16    You should have received a copy of the GNU General Public License
 17    along with circom. If not, see <https://www.gnu.org/licenses/>.
 18*/
 19
 20const Scalar = require("ffjavascript").Scalar;
 21const sONE = 0;
 22const build = require("./build");
 23const BuilderC = require("../ports/c/builder.js");
 24const BuilderWasm = require("../ports/wasm/builder.js");
 25const constructionPhase = require("./construction_phase");
 26const Ctx = require("./ctx");
 27const utils = require("./utils");
 28const buildR1cs = require("./r1csfile").buildR1cs;
 29const BigArray = require("./bigarray");
 30const buildSyms = require("./buildsyms");
 31const {performance} = require("perf_hooks");
 32
 33module.exports = compile;
 34const measures = {};
 35
 36function ms2String(v) {
 37    v = Math.floor(v);
 38    const ms = v % 1000;
 39    v = Math.floor(v/1000);
 40    const secs = v % 60;
 41    v = Math.floor(v/60);
 42    const mins = v % 60;
 43    v = Math.floor(v/60);
 44    const hours = v % 24;
 45    const days = Math.floor(v/24);
 46    let S = "";
 47    if (days) S = S + days + "D ";
 48    if ((S!="")||(hours)) S = S + hours.toString().padStart(2, "0") + ":";
 49    if ((S!="")||(mins)) S = S + mins.toString().padStart(2, "0") + ":";
 50    if ((S!="")||(secs)) S = S + secs.toString().padStart(2, "0");
 51    S+=".";
 52    S = S + ms.toString().padStart(3, "0");
 53    return S;
 54}
 55
 56async function compile(srcFile, options) {
 57    options.prime = options.prime || Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
 58    if (!options) {
 59        options = {};
 60    }
 61    if (typeof options.reduceConstraints === "undefined") {
 62        options.reduceConstraints = true;
 63    }
 64    const ctx = new Ctx(options.prime);
 65    ctx.verbose= options.verbose || false;
 66    ctx.mainComponent = options.mainComponent || "main";
 67    ctx.newThreadTemplates = options.newThreadTemplates;
 68
 69    measures.constructionPhase = -performance.now();
 70    constructionPhase(ctx, srcFile);
 71    measures.constructionPhase += performance.now();
 72
 73    if (ctx.verbose) console.log("NConstraints Before: "+ctx.constraints.length);
 74    if (ctx.verbose) console.log("NSignals Before: "+ctx.signals.length);
 75
 76    if (ctx.error) {
 77        throw(ctx.error);
 78    }
 79
 80    if (ctx.getComponentIdx(ctx.mainComponent)<0) {
 81        throw new Error("A main component must be defined");
 82    }
 83
 84    if (ctx.verbose) console.log("Classify Signals");
 85    measures.classifySignals = -performance.now();
 86    classifySignals(ctx);
 87    measures.classifySignals += performance.now();
 88
 89    if (ctx.verbose) console.log("Reduce Constants");
 90    measures.reduceConstants = -performance.now();
 91    reduceConstants(ctx);
 92    measures.reduceConstants += performance.now();
 93
 94    if (options.reduceConstraints) {
 95
 96        if (ctx.verbose) console.log("Reduce Constraints");
 97        // Repeat while reductions are performed
 98/*
 99        let oldNConstrains = -1;
100        while (ctx.constraints.length != oldNConstrains) {
101            if (ctx.verbose) console.log("Reducing constraints: "+ctx.constraints.length);
102            oldNConstrains = ctx.constraints.length;
103            reduceConstrains(ctx);
104        }
105*/
106        measures.reduceConstraints = -performance.now();
107        await reduceConstrains(ctx);
108        measures.reduceConstraints += performance.now();
109
110    }
111
112    if (ctx.verbose) console.log("NConstraints After: "+ctx.constraints.length);
113
114    measures.generateWitnessNames = -performance.now();
115    generateWitnessNames(ctx);
116    measures.generateWitnessNames += performance.now();
117
118    if (ctx.error) {
119        throw(ctx.error);
120    }
121
122    if (options.r1csFileName) {
123        measures.generateR1cs = -performance.now();
124        await buildR1cs(ctx, options.r1csFileName);
125        measures.generateR1cs += performance.now();
126    }
127
128    if (ctx.error) throw(ctx.error);
129
130    delete ctx.constraints;  // Liberate memory.
131
132    if (options.cSourceFile) {
133        if (ctx.verbose) console.log("Generating c...");
134        measures.generateC = -performance.now();
135        ctx.builder = new BuilderC(options.prime, ctx.verbose);
136        build(ctx);
137        await ctx.builder.build(options.cSourceFile);
138        measures.generateC += performance.now();
139    }
140
141    if (ctx.error) throw(ctx.error);
142
143    if ((options.wasmFile)||(options.watFile)) {
144        if (ctx.verbose) console.log("Generating wasm...");
145        measures.generateWasm = -performance.now();
146        ctx.builder = new BuilderWasm(options.prime);
147        build(ctx);
148        if (options.wasmFile) {
149            await ctx.builder.build(options.wasmFile, "wasm");
150        }
151        if (options.watFile) {
152            await ctx.builder.build(options.watFile, "wat");
153        }
154        measures.generateWasm += performance.now();
155    }
156
157    // const mainCode = gen(ctx,ast);
158    if (ctx.error) throw(ctx.error);
159
160    if (options.symWriteStream) {
161        measures.generateSyms = -performance.now();
162        const rdStream = buildSyms(ctx);
163        rdStream.pipe(options.symWriteStream);
164        measures.generateSyms += performance.now();
165
166        await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
167    }
168
169//    const def = buildCircuitDef(ctx, mainCode);
170
171    if (ctx.verbose) {
172        for (let [mStr, mValue] of Object.entries(measures)) {
173            console.log(mStr + ": " + ms2String(mValue));
174        }
175    }
176}
177
178
179
180function classifySignals(ctx) {
181
182    const ERROR = 0xFFFF;
183
184    function priorize(t1, t2) {
185        if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
186        if (t1 == ctx.stINTERNAL) {
187            return t2;
188        } else if (t2==ctx.stINTERNAL) {
189            return t1;
190        }
191        if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
192        if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
193        if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
194        if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
195        if (t1!=t2) return ERROR;
196        return t1;
197    }
198
199    // First classify the signals
200    for (let s=0; s<ctx.signals.length; s++) {
201        if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
202        const signal = ctx.signals[s];
203        let tAll = ctx.stINTERNAL;
204        let lSignal = signal;
205        let end = false;
206        while (!end) {
207            let t = lSignal.c || ctx.stINTERNAL;
208            if (s == 0) {
209                t = ctx.stONE;
210            } else if (lSignal.o & ctx.MAIN) {
211                if (lSignal.o & ctx.IN) {
212                    if (lSignal.o & ctx.PRV) {
213                        t = ctx.stPRVINPUT;
214                    } else {
215                        t = ctx.stPUBINPUT;
216                    }
217                } else if (lSignal.o & ctx.OUT) {
218                    t = ctx.stOUTPUT;
219                }
220            } else if (utils.isDefined(lSignal.v)) {
221                t = ctx.stCONSTANT;
222            }
223            tAll = priorize(t,tAll);
224            if (lSignal.e>=0) {
225                lSignal = ctx.signals[lSignal.e];
226            } else {
227                end=true;
228            }
229        }
230        if (tAll == ERROR) {
231            throw new Error("Incompatible types in signal: " + s);
232        }
233        lSignal.c = tAll;
234    }
235}
236
237
238function generateWitnessNames(ctx) {
239    const totals = {};
240    totals[ctx.stONE] = 0;
241    totals[ctx.stOUTPUT] = 0;
242    totals[ctx.stPUBINPUT] = 0;
243    totals[ctx.stPRVINPUT] = 0;
244    totals[ctx.stINTERNAL] = 0;
245    totals[ctx.stDISCARDED] = 0;
246    totals[ctx.stCONSTANT] = 0;
247    const ids = {};
248
249    // First classify the signals
250    for (let s=0; s<ctx.signals.length; s++) {
251
252        if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
253
254        const signal = ctx.signals[s];
255        let lSignal = signal;
256        while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
257
258        if (!( lSignal.o & ctx.COUNTED) ) {
259            lSignal.o |= ctx.COUNTED;
260            totals[lSignal.c] ++;
261        }
262    }
263
264    ids[ctx.stONE] = 0;
265    ids[ctx.stOUTPUT] = 1;
266    ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
267    ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
268    ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
269    ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
270    ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
271    const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
272
273    for (let s=0; s<ctx.signals.length; s++) {
274
275        if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
276
277        const signal = ctx.signals[s];
278        let lSignal = signal;
279        while (lSignal.e>=0) {
280            lSignal = ctx.signals[lSignal.e];
281        }
282        if ( typeof(lSignal.id) === "undefined" ) {
283            lSignal.id = ids[lSignal.c] ++;
284        }
285
286        signal.id = lSignal.id;
287    }
288
289    ctx.totals = totals;
290}
291
292function reduceConstants(ctx) {
293    const newConstraints = new BigArray();
294    for (let i=0; i<ctx.constraints.length; i++) {
295        if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
296        const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
297        if (!ctx.lc.isZero(c)) {
298            newConstraints.push(c);
299        }
300        delete ctx.constraints[i];
301    }
302    ctx.constraints = newConstraints;
303}
304
305async function reduceConstrains(ctx) {
306    const sig2constraint = new BigArray();
307    let removedSignals = new BigArray();
308    let nRemoved;
309    let lIdx;
310
311
312    let possibleConstraints = new BigArray(ctx.constraints.length);
313    let nextPossibleConstraints;
314    for (let i=0; i<ctx.constraints.length; i++) {
315        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`indexing constraints: ${i}/${ctx.constraints.length}`);
316
317        const insertedSig = { 0: true};  // Do not insert one.
318        const c = ctx.constraints[i];
319        for (let s in c.a.coefs) {
320            if (!insertedSig[s]) {
321                if (!sig2constraint[s]) sig2constraint[s] = [];
322                sig2constraint[s].push(i);
323                insertedSig[s] = true;
324            }
325        }
326        for (let s in c.b.coefs) {
327            if (!insertedSig[s]) {
328                if (!sig2constraint[s]) sig2constraint[s] = [];
329                sig2constraint[s].push(i);
330                insertedSig[s] = true;
331            }
332        }
333        for (let s in c.c.coefs) {
334            if (!insertedSig[s]) {
335                if (!sig2constraint[s]) sig2constraint[s] = [];
336                sig2constraint[s].push(i);
337                insertedSig[s] = true;
338            }
339        }
340        possibleConstraints[i] = i;
341    }
342
343    while (possibleConstraints.length >0) {
344        nextPossibleConstraints = new BigArray();
345        removedSignals = new BigArray();
346        nRemoved = 0;
347        lIdx = new BigArray();
348        for (let i=0;i<possibleConstraints.length;i++) {
349            if ((ctx.verbose)&&(i%10000 == 0)) {
350                await Promise.resolve();
351                console.log(`reducing constraints: ${i}/${possibleConstraints.length}   reduced: ${nRemoved}`);
352            }
353            const c = ctx.constraints[possibleConstraints[i]];
354            if (!c) continue;
355
356            // Swap a and b if b has more variables.
357            if (Object.keys(c.b).length > Object.keys(c.a).length) {
358                const aux = c.a;
359                c.a=c.b;
360                c.b=aux;
361            }
362
363            // Mov to C if possible.
364            if (isConstant(c.a)) {
365                const ct = {t: "N", v: c.a.coefs[sONE]};
366                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
367                c.a = { t: "LC", coefs: {} };
368                c.b = { t: "LC", coefs: {} };
369            }
370            if (isConstant(c.b)) {
371                const ct = {t: "N", v: c.b.coefs[sONE]};
372                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
373                c.a = { t: "LC", coefs: {} };
374                c.b = { t: "LC", coefs: {} };
375            }
376
377            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
378                const freeC = substituteRemoved(c.c);
379                const isolatedSignal = getFirstInternalSignal(ctx, freeC);
380                if (isolatedSignal) {
381    //                console.log(isolatedSignal);
382    //                console.log(freeC);
383                    removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
384                    if (lIdx[isolatedSignal]) {
385                        lIdx[isolatedSignal].forEach( (s) => {
386                            removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
387                        });
388                    }
389
390                    addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
391                    ctx.constraints[possibleConstraints[i]] = null;
392                    nRemoved ++;
393
394                    sig2constraint[isolatedSignal].forEach( (s) => {
395                        nextPossibleConstraints[s] = true;
396                    });
397                }
398            }
399        }
400
401        nextPossibleConstraints = nextPossibleConstraints.getKeys();
402
403        for (let i=0; i<nextPossibleConstraints.length;i++) {
404            if ((ctx.verbose)&&(i%10000 == 0)) {
405                await Promise.resolve();
406                console.log(`substituting constraints: ${i}/${nextPossibleConstraints.length}`);
407            }
408            const c = ctx.constraints[nextPossibleConstraints[i]];
409            if (c) {
410                const nc = {
411                    a: substituteRemoved(c.a),
412                    b: substituteRemoved(c.b),
413                    c: substituteRemoved(c.c)
414                };
415                if (ctx.lc.isZero(nc)) {
416                    delete ctx.constraints[nextPossibleConstraints[i]];
417                } else {
418                    ctx.constraints[nextPossibleConstraints[i]] = nc;
419                }
420            }
421        }
422
423        const removedSignalsList = removedSignals.getKeys;
424
425        for (let i=0; i<removedSignalsList.length; i++) {
426            if ((ctx.verbose    )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
427            const s = removedSignalsList[i];
428
429            let lSignal = ctx.signals[s];
430            while (lSignal.e>=0) {
431                lSignal = ctx.signals[lSignal.e];
432            }
433
434            lSignal.c = ctx.stDISCARDED;
435        }
436
437        possibleConstraints = nextPossibleConstraints;
438    }
439
440    let o=0;
441    for (let i=0; i<ctx.constraints.length;i++) {
442        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
443        if (ctx.constraints[i]) {
444            if (!ctx.lc.isZero(ctx.constraints[i])) {
445                ctx.constraints[o] = ctx.constraints[i];
446                o++;
447            }
448        }
449    }
450
451    ctx.constraints.length = o;
452
453    function getFirstInternalSignal(ctx, l) {
454        for (let k in l.coefs) {
455            k = Number(k);
456            const signal = ctx.signals[k];
457            if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
458        }
459        return null;
460    }
461
462    function isolateSignal(lc, s) {
463        const eq = {
464            t: "LC",
465            coefs: {}
466        };
467        const invCoef = ctx.F.inv(lc.coefs[s]);
468        for (const k in lc.coefs) {
469            if (k != s) {
470                const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef);
471                if (!ctx.F.isZero(v)) {
472                    eq.coefs[k] = v;
473                }
474            }
475        }
476        return eq;
477    }
478
479    function substituteRemoved(lc) {
480        const newLc = ctx.lc._clone(lc);
481        for (let k in lc.coefs) {
482            if (removedSignals[k]) {
483                delete newLc.coefs[k];
484                for (let k2 in removedSignals[k].coefs) {
485                    const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]);
486                    if (!ctx.F.isZero(newP)) {
487                        if (newLc.coefs[k2]) {
488                            newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
489                            if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
490                        } else {
491                            newLc.coefs[k2] = newP;
492                        }
493                    }
494                }
495            }
496        }
497        return newLc;
498    }
499
500    function substitute(lc, s, eq) {
501        if (!lc.coefs[s]) return lc;
502        const newLc = ctx.lc._clone(lc);
503        delete newLc.coefs[s];
504        for (let k2 in eq.coefs) {
505            const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]);
506            if (!ctx.F.isZero(newP)) {
507                if (newLc.coefs[k2]) {
508                    newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
509                    if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
510                } else {
511                    newLc.coefs[k2] = newP;
512                }
513            }
514        }
515        return newLc;
516    }
517
518    function isConstant(l) {
519        for (let k in l.coefs) {
520            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
521        }
522        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
523        return true;
524    }
525
526    function addTolIdx(lc, newS) {
527        for (let s in lc.coefs) {
528            if (!lIdx[s]) lIdx[s] = [];
529            lIdx[s].push(newS);
530        }
531    }
532
533
534}
535
536
537function reduceConstrains_old(ctx) {
538    indexVariables();
539    let possibleConstraints = ctx.constraints;
540    let ii=0;
541    while (possibleConstraints.length>0) {
542        let nextPossibleConstraints = new BigArray();
543        for (let i=0; i<possibleConstraints.length; i++) {
544            ii++;
545            if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
546            if (!ctx.constraints[i]) continue;
547            const c = ctx.constraints[i];
548
549            // Swap a and b if b has more variables.
550            if (Object.keys(c.b).length > Object.keys(c.a).length) {
551                const aux = c.a;
552                c.a=c.b;
553                c.b=aux;
554            }
555
556            // Mov to C if possible.
557            if (isConstant(c.a)) {
558                const ct = {t: "N", v: c.a.coefs[sONE]};
559                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
560                c.a = { t: "LC", coefs: {} };
561                c.b = { t: "LC", coefs: {} };
562            }
563            if (isConstant(c.b)) {
564                const ct = {t: "N", v: c.b.coefs[sONE]};
565                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
566                c.a = { t: "LC", coefs: {} };
567                c.b = { t: "LC", coefs: {} };
568            }
569
570            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
571                const isolatedSignal = getFirstInternalSignal(ctx, c.c);
572                if (isolatedSignal) {
573
574                    let lSignal = ctx.signals[isolatedSignal];
575                    while (lSignal.e>=0) {
576                        lSignal = ctx.signals[lSignal.e];
577                    }
578
579
580                    const isolatedSignalEquivalence = {
581                        t: "LC",
582                        coefs: {}
583                    };
584                    const invCoef = ctx.F.inv(c.c.coefs[isolatedSignal]);
585                    for (const s in c.c.coefs) {
586                        if (s != isolatedSignal) {
587                            const v = ctx.F.mul( ctx.F.neg(c.c.coefs[s]), invCoef);
588                            if (!ctx.F.isZero(v)) {
589                                isolatedSignalEquivalence.coefs[s] = v;
590                            }
591                        }
592                    }
593
594                    for (let j in lSignal.inConstraints) {
595                        if ((j!=i)&&(ctx.constraints[j])) {
596                            ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
597                            linkSignalsConstraint(j);
598                            if (j<i) {
599                                nextPossibleConstraints.push(j);
600                            }
601                        }
602                    }
603
604                    ctx.constraints[i] = null;
605
606                    lSignal.c = ctx.stDISCARDED;
607                } else {
608                    if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
609                }
610            }
611        }
612        possibleConstraints = nextPossibleConstraints;
613    }
614    unindexVariables();
615
616    // Pack the constraints
617    let o = 0;
618    for (let i=0; i<ctx.constraints.length; i++) {
619        if (ctx.constraints[i]) {
620            if (o != i) {
621                ctx.constraints[o] = ctx.constraints[i];
622            }
623            o++;
624        }
625    }
626    ctx.constraints.length = o;
627
628    function indexVariables() {
629        for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
630    }
631
632    function linkSignalsConstraint(cidx) {
633        const ct = ctx.constraints[cidx];
634        for (let k in ct.a.coefs) linkSignal(k, cidx);
635        for (let k in ct.b.coefs) linkSignal(k, cidx);
636        for (let k in ct.c.coefs) linkSignal(k, cidx);
637    }
638
639    function unindexVariables() {
640        for (let s=0; s<ctx.signals.length; s++) {
641            let lSignal = ctx.signals[s];
642            while (lSignal.e>=0) {
643                lSignal = ctx.signals[lSignal.e];
644            }
645            if (lSignal.inConstraints) delete lSignal.inConstraints;
646        }
647    }
648
649/*
650    function unlinkSignal(signalName, cidx) {
651        let lSignal = ctx.signals[signalName];
652        while (lSignal.e>=0) {
653            lSignal = ctx.signals[lSignal.e];
654        }
655        if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
656            delete lSignal.inConstraints[cidx];
657        }
658    }
659*/
660
661    function linkSignal(signalName, cidx) {
662        let lSignal = ctx.signals[signalName];
663        while (lSignal.e>=0) {
664            lSignal = ctx.signals[lSignal.e];
665        }
666        if (!lSignal.inConstraints) lSignal.inConstraints = {};
667        lSignal.inConstraints[cidx] = true;
668    }
669
670    function getFirstInternalSignal(ctx, l) {
671        for (let k in l.coefs) {
672            const signal = ctx.signals[k];
673            if (signal.c == ctx.stINTERNAL) return k;
674        }
675        return null;
676    }
677
678    function isConstant(l) {
679        for (let k in l.coefs) {
680            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
681        }
682        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
683        return true;
684    }
685
686}
687
688/*
689
690function buildCircuitDef(ctx, mainCode) {
691    const res = {
692        mainCode: mainCode
693    };
694    res.signalName2Idx = ctx.signalName2Idx;
695
696    res.components = [];
697    res.componentName2Idx = {};
698    for (let c in ctx.components) {
699        const idCoponent = res.components.length;
700        res.components.push({
701            name: c,
702            params: ctx.components[c].params,
703            template: ctx.components[c].template,
704            inputSignals: 0
705        });
706        res.componentName2Idx[c] = idCoponent;
707    }
708
709    res.signals = new Array(ctx.signalNames.length);
710    for (let i=0; i<ctx.signalNames.length; i++) {
711        res.signals[i] = {
712            names: ctx.signalNames[i],
713            triggerComponents: []
714        };
715        ctx.signalNames[i].map( (fullName) => {
716            const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
717            if (ctx.signals[fullName].direction == "IN") {
718                res.signals[i].triggerComponents.push(idComponet);
719                res.components[idComponet].inputSignals++;
720            }
721        });
722    }
723
724    res.constraints = buildConstraints(ctx);
725
726    res.templates = ctx.templates;
727
728    res.functions = {};
729    for (let f in ctx.functions) {
730        res.functions[f] = {
731            params: ctx.functionParams[f],
732            func: ctx.functions[f]
733        };
734    }
735
736    res.nPrvInputs = ctx.totals.prvInput;
737    res.nPubInputs = ctx.totals.pubInput;
738    res.nInputs = res.nPrvInputs + res.nPubInputs;
739    res.nOutputs = ctx.totals.output;
740    res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
741    res.nConstants = ctx.totals.constant;
742    res.nSignals = res.nVars + res.nConstants;
743
744    return res;
745}
746
747*/
748
749
750/*
751    Build constraints
752
753A constraint like this
754
755      [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0        ] = 0
756      [     5*s2 + 6*s3] * [ s2 +     ] - [s0 + 2* s2] = 0
757      [s1 +          s3] * [ s2 + 5*s3] - [s4        ] = 0
758
759is converted to
760
761[
762    [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1"         }],
763    [{        "2":"5","3":"6"} , {"2":"1"        } , {"0":"1", "2":"2"}],
764    [{"1":"1",        "3":"1"} , {"2":"1","3":"5"} , {"4":"1"         }]
765]
766           ^                            ^                  ^
767           |                            |                  |
768           A                            B                  C
769
770*/
771/*
772function buildConstraints(ctx) {
773    const res = [];
774
775    function fillLC(dst, src) {
776        if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
777        for (let s in src.coefs) {
778            const v = src.coefs[s].toString();
779            const id = ctx.signalName2Idx[s];
780            dst[id] = v;
781        }
782    }
783
784    for (let i=0; i<ctx.constraints.length; i++) {
785        const A = {};
786        const B = {};
787        const C = {};
788
789        fillLC(A, ctx.constraints[i].a);
790        fillLC(B, ctx.constraints[i].b);
791        fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
792
793        res.push([A,B,C]);
794    }
795
796    return res;
797}
798*/
799
800/*
801function buildSyms(ctx, strm) {
802
803    let nSyms;
804
805    addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
806
807
808    function addSymbolsComponent(prefix, idComponet) {
809        for (let n in  ctx.components[idComponet].names.o) {
810            const entrie = ctx.components[idComponet].names.o[n];
811            addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
812        }
813    }
814
815    function addSymbolArray(prefix, type, sizes, offset) {
816        if (sizes.length==0) {
817            if (type == "S") {
818                let s=offset;
819                while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
820                let wId = ctx.signals[s].id;
821                if (typeof(wId) == "undefined") wId=-1;
822                strm.write(`${offset},${wId},${prefix}\n`);
823                nSyms ++;
824                if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
825            } else {
826                addSymbolsComponent(prefix+".", offset);
827            }
828            return 1;
829        } else {
830            let acc = 0;
831            for (let i=0; i<sizes[0]; i++) {
832                acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
833            }
834            return acc;
835        }
836    }
837
838}
839
840*/
841