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.cSourceWriteStream) {
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        const rdStream = ctx.builder.build();
138        rdStream.pipe(options.cSourceWriteStream);
139        measures.generateC += performance.now();
140
141        // await new Promise(fulfill => options.cSourceWriteStream.on("finish", fulfill));
142    }
143
144    if (ctx.error) throw(ctx.error);
145
146    if ((options.wasmWriteStream)||(options.watWriteStream)) {
147        if (ctx.verbose) console.log("Generating wasm...");
148        measures.generateWasm = -performance.now();
149        ctx.builder = new BuilderWasm(options.prime);
150        build(ctx);
151        if (options.wasmWriteStream) {
152            const rdStream = ctx.builder.build("wasm");
153            rdStream.pipe(options.wasmWriteStream);
154        }
155        if (options.watWriteStream) {
156            const rdStream = ctx.builder.build("wat");
157            rdStream.pipe(options.watWriteStream);
158        }
159        measures.generateWasm += performance.now();
160
161        // await new Promise(fulfill => options.wasmWriteStream.on("finish", fulfill));
162    }
163
164    // const mainCode = gen(ctx,ast);
165    if (ctx.error) throw(ctx.error);
166
167    if (options.symWriteStream) {
168        measures.generateSyms = -performance.now();
169        const rdStream = buildSyms(ctx);
170        rdStream.pipe(options.symWriteStream);
171        measures.generateSyms += performance.now();
172
173        await new Promise(fulfill => options.symWriteStream.on("finish", fulfill));
174    }
175
176//    const def = buildCircuitDef(ctx, mainCode);
177
178    if (ctx.verbose) {
179        for (let [mStr, mValue] of Object.entries(measures)) {
180            console.log(mStr + ": " + ms2String(mValue));
181        }
182    }
183}
184
185
186
187function classifySignals(ctx) {
188
189    const ERROR = 0xFFFF;
190
191    function priorize(t1, t2) {
192        if ((t1 == ERROR) || (t2==ERROR)) return ERROR;
193        if (t1 == ctx.stINTERNAL) {
194            return t2;
195        } else if (t2==ctx.stINTERNAL) {
196            return t1;
197        }
198        if ((t1 == ctx.stONE) || (t2 == ctx.stONE)) return ctx.stONE;
199        if ((t1 == ctx.stOUTPUT) || (t2 == ctx.stOUTPUT)) return ctx.stOUTPUT;
200        if ((t1 == ctx.stCONSTANT) || (t2 == ctx.stCONSTANT)) return ctx.stCONSTANT;
201        if ((t1 == ctx.stDISCARDED) || (t2 == ctx.stDISCARDED)) return ctx.stDISCARDED;
202        if (t1!=t2) return ERROR;
203        return t1;
204    }
205
206    // First classify the signals
207    for (let s=0; s<ctx.signals.length; s++) {
208        if ((ctx.verbose)&&(s%100000 == 0)) console.log(`classify signals: ${s}/${ctx.signals.length}`);
209        const signal = ctx.signals[s];
210        let tAll = ctx.stINTERNAL;
211        let lSignal = signal;
212        let end = false;
213        while (!end) {
214            let t = lSignal.c || ctx.stINTERNAL;
215            if (s == 0) {
216                t = ctx.stONE;
217            } else if (lSignal.o & ctx.MAIN) {
218                if (lSignal.o & ctx.IN) {
219                    if (lSignal.o & ctx.PRV) {
220                        t = ctx.stPRVINPUT;
221                    } else {
222                        t = ctx.stPUBINPUT;
223                    }
224                } else if (lSignal.o & ctx.OUT) {
225                    t = ctx.stOUTPUT;
226                }
227            } else if (utils.isDefined(lSignal.v)) {
228                t = ctx.stCONSTANT;
229            }
230            tAll = priorize(t,tAll);
231            if (lSignal.e>=0) {
232                lSignal = ctx.signals[lSignal.e];
233            } else {
234                end=true;
235            }
236        }
237        if (tAll == ERROR) {
238            throw new Error("Incompatible types in signal: " + s);
239        }
240        lSignal.c = tAll;
241    }
242}
243
244
245function generateWitnessNames(ctx) {
246    const totals = {};
247    totals[ctx.stONE] = 0;
248    totals[ctx.stOUTPUT] = 0;
249    totals[ctx.stPUBINPUT] = 0;
250    totals[ctx.stPRVINPUT] = 0;
251    totals[ctx.stINTERNAL] = 0;
252    totals[ctx.stDISCARDED] = 0;
253    totals[ctx.stCONSTANT] = 0;
254    const ids = {};
255
256    // First classify the signals
257    for (let s=0; s<ctx.signals.length; s++) {
258
259        if ((ctx.verbose)&&(s%10000 == 0)) console.log("generate witness (counting): ", s);
260
261        const signal = ctx.signals[s];
262        let lSignal = signal;
263        while (lSignal.e>=0) lSignal = ctx.signals[lSignal.e];
264
265        if (!( lSignal.o & ctx.COUNTED) ) {
266            lSignal.o |= ctx.COUNTED;
267            totals[lSignal.c] ++;
268        }
269    }
270
271    ids[ctx.stONE] = 0;
272    ids[ctx.stOUTPUT] = 1;
273    ids[ctx.stPUBINPUT] = ids[ctx.stOUTPUT] + totals[ctx.stOUTPUT];
274    ids[ctx.stPRVINPUT] = ids[ctx.stPUBINPUT] + totals[ctx.stPUBINPUT];
275    ids[ctx.stINTERNAL] = ids[ctx.stPRVINPUT] + totals[ctx.stPRVINPUT];
276    ids[ctx.stDISCARDED] = ids[ctx.stINTERNAL] + totals[ctx.stINTERNAL];
277    ids[ctx.stCONSTANT] = ids[ctx.stDISCARDED] + totals[ctx.stDISCARDED];
278    const nSignals = ids[ctx.stCONSTANT] + totals[ctx.stCONSTANT];
279
280    for (let s=0; s<ctx.signals.length; s++) {
281
282        if ((ctx.verbose)&&(s%10000 == 0)) console.log("seting id: ", s);
283
284        const signal = ctx.signals[s];
285        let lSignal = signal;
286        while (lSignal.e>=0) {
287            lSignal = ctx.signals[lSignal.e];
288        }
289        if ( typeof(lSignal.id) === "undefined" ) {
290            lSignal.id = ids[lSignal.c] ++;
291        }
292
293        signal.id = lSignal.id;
294    }
295
296    ctx.totals = totals;
297}
298
299function reduceConstants(ctx) {
300    const newConstraints = new BigArray();
301    for (let i=0; i<ctx.constraints.length; i++) {
302        if ((ctx.verbose)&&(i%10000 == 0)) console.log("reducing constants: ", i);
303        const c = ctx.lc.canonize(ctx, ctx.constraints[i]);
304        if (!ctx.lc.isZero(c)) {
305            newConstraints.push(c);
306        }
307        delete ctx.constraints[i];
308    }
309    ctx.constraints = newConstraints;
310}
311
312async function reduceConstrains(ctx) {
313    const sig2constraint = new BigArray();
314    let removedSignals = new BigArray();
315    let nRemoved;
316    let lIdx;
317
318
319    let possibleConstraints = new BigArray(ctx.constraints.length);
320    let nextPossibleConstraints;
321    for (let i=0; i<ctx.constraints.length; i++) {
322        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`indexing constraints: ${i}/${ctx.constraints.length}`);
323
324        const insertedSig = { 0: true};  // Do not insert one.
325        const c = ctx.constraints[i];
326        for (let s in c.a.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.b.coefs) {
334            if (!insertedSig[s]) {
335                if (!sig2constraint[s]) sig2constraint[s] = [];
336                sig2constraint[s].push(i);
337                insertedSig[s] = true;
338            }
339        }
340        for (let s in c.c.coefs) {
341            if (!insertedSig[s]) {
342                if (!sig2constraint[s]) sig2constraint[s] = [];
343                sig2constraint[s].push(i);
344                insertedSig[s] = true;
345            }
346        }
347        possibleConstraints[i] = i;
348    }
349
350    while (possibleConstraints.length >0) {
351        nextPossibleConstraints = new BigArray();
352        removedSignals = new BigArray();
353        nRemoved = 0;
354        lIdx = new BigArray();
355        for (let i=0;i<possibleConstraints.length;i++) {
356            if ((ctx.verbose)&&(i%10000 == 0)) {
357                await Promise.resolve();
358                console.log(`reducing constraints: ${i}/${possibleConstraints.length}   reduced: ${nRemoved}`);
359            }
360            const c = ctx.constraints[possibleConstraints[i]];
361            if (!c) continue;
362
363            // Swap a and b if b has more variables.
364            if (Object.keys(c.b).length > Object.keys(c.a).length) {
365                const aux = c.a;
366                c.a=c.b;
367                c.b=aux;
368            }
369
370            // Mov to C if possible.
371            if (isConstant(c.a)) {
372                const ct = {t: "N", v: c.a.coefs[sONE]};
373                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
374                c.a = { t: "LC", coefs: {} };
375                c.b = { t: "LC", coefs: {} };
376            }
377            if (isConstant(c.b)) {
378                const ct = {t: "N", v: c.b.coefs[sONE]};
379                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
380                c.a = { t: "LC", coefs: {} };
381                c.b = { t: "LC", coefs: {} };
382            }
383
384            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
385                const freeC = substituteRemoved(c.c);
386                const isolatedSignal = getFirstInternalSignal(ctx, freeC);
387                if (isolatedSignal) {
388    //                console.log(isolatedSignal);
389    //                console.log(freeC);
390                    removedSignals[isolatedSignal] = isolateSignal(freeC, isolatedSignal);
391                    if (lIdx[isolatedSignal]) {
392                        lIdx[isolatedSignal].forEach( (s) => {
393                            removedSignals[s] = substitute(removedSignals[s], isolatedSignal, removedSignals[isolatedSignal]);
394                        });
395                    }
396
397                    addTolIdx(removedSignals[isolatedSignal], isolatedSignal);
398                    ctx.constraints[possibleConstraints[i]] = null;
399                    nRemoved ++;
400
401                    sig2constraint[isolatedSignal].forEach( (s) => {
402                        nextPossibleConstraints[s] = true;
403                    });
404                }
405            }
406        }
407
408        nextPossibleConstraints = nextPossibleConstraints.getKeys();
409
410        for (let i=0; i<nextPossibleConstraints.length;i++) {
411            if ((ctx.verbose)&&(i%10000 == 0)) {
412                await Promise.resolve();
413                console.log(`substituting constraints: ${i}/${nextPossibleConstraints.length}`);
414            }
415            const c = ctx.constraints[nextPossibleConstraints[i]];
416            if (c) {
417                const nc = {
418                    a: substituteRemoved(c.a),
419                    b: substituteRemoved(c.b),
420                    c: substituteRemoved(c.c)
421                };
422                if (ctx.lc.isZero(nc)) {
423                    delete ctx.constraints[nextPossibleConstraints[i]];
424                } else {
425                    ctx.constraints[nextPossibleConstraints[i]] = nc;
426                }
427            }
428        }
429
430        const removedSignalsList = removedSignals.getKeys;
431
432        for (let i=0; i<removedSignalsList.length; i++) {
433            if ((ctx.verbose    )&&(i%100000 == 0)) console.log(`removing signals: ${i}/${removedSignalsList.length}`);
434            const s = removedSignalsList[i];
435
436            let lSignal = ctx.signals[s];
437            while (lSignal.e>=0) {
438                lSignal = ctx.signals[lSignal.e];
439            }
440
441            lSignal.c = ctx.stDISCARDED;
442        }
443
444        possibleConstraints = nextPossibleConstraints;
445    }
446
447    let o=0;
448    for (let i=0; i<ctx.constraints.length;i++) {
449        if ((ctx.verbose)&&(i%100000 == 0)) console.log(`reordering constraints: ${i}/${ctx.constraints.length}`);
450        if (ctx.constraints[i]) {
451            if (!ctx.lc.isZero(ctx.constraints[i])) {
452                ctx.constraints[o] = ctx.constraints[i];
453                o++;
454            }
455        }
456    }
457
458    ctx.constraints.length = o;
459
460    function getFirstInternalSignal(ctx, l) {
461        for (let k in l.coefs) {
462            k = Number(k);
463            const signal = ctx.signals[k];
464            if ((signal.c == ctx.stINTERNAL)&&(!ctx.F.isZero(l.coefs[k])) &&(!removedSignals[k])) return k;
465        }
466        return null;
467    }
468
469    function isolateSignal(lc, s) {
470        const eq = {
471            t: "LC",
472            coefs: {}
473        };
474        const invCoef = ctx.F.inv(lc.coefs[s]);
475        for (const k in lc.coefs) {
476            if (k != s) {
477                const v = ctx.F.mul( ctx.F.neg(lc.coefs[k]), invCoef);
478                if (!ctx.F.isZero(v)) {
479                    eq.coefs[k] = v;
480                }
481            }
482        }
483        return eq;
484    }
485
486    function substituteRemoved(lc) {
487        const newLc = ctx.lc._clone(lc);
488        for (let k in lc.coefs) {
489            if (removedSignals[k]) {
490                delete newLc.coefs[k];
491                for (let k2 in removedSignals[k].coefs) {
492                    const newP = ctx.F.mul(removedSignals[k].coefs[k2], lc.coefs[k]);
493                    if (!ctx.F.isZero(newP)) {
494                        if (newLc.coefs[k2]) {
495                            newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
496                            if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
497                        } else {
498                            newLc.coefs[k2] = newP;
499                        }
500                    }
501                }
502            }
503        }
504        return newLc;
505    }
506
507    function substitute(lc, s, eq) {
508        if (!lc.coefs[s]) return lc;
509        const newLc = ctx.lc._clone(lc);
510        delete newLc.coefs[s];
511        for (let k2 in eq.coefs) {
512            const newP = ctx.F.mul(eq.coefs[k2], lc.coefs[s]);
513            if (!ctx.F.isZero(newP)) {
514                if (newLc.coefs[k2]) {
515                    newLc.coefs[k2] = ctx.F.add(newLc.coefs[k2], newP);
516                    if (ctx.F.isZero(newLc.coefs[k2])) delete newLc.coefs[k2];
517                } else {
518                    newLc.coefs[k2] = newP;
519                }
520            }
521        }
522        return newLc;
523    }
524
525    function isConstant(l) {
526        for (let k in l.coefs) {
527            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
528        }
529        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
530        return true;
531    }
532
533    function addTolIdx(lc, newS) {
534        for (let s in lc.coefs) {
535            if (!lIdx[s]) lIdx[s] = [];
536            lIdx[s].push(newS);
537        }
538    }
539
540
541}
542
543
544function reduceConstrains_old(ctx) {
545    indexVariables();
546    let possibleConstraints = ctx.constraints;
547    let ii=0;
548    while (possibleConstraints.length>0) {
549        let nextPossibleConstraints = new BigArray();
550        for (let i=0; i<possibleConstraints.length; i++) {
551            ii++;
552            if ((ctx.verbose)&&(ii%10000 == 0)) console.log("reducing constraints: ", i);
553            if (!ctx.constraints[i]) continue;
554            const c = ctx.constraints[i];
555
556            // Swap a and b if b has more variables.
557            if (Object.keys(c.b).length > Object.keys(c.a).length) {
558                const aux = c.a;
559                c.a=c.b;
560                c.b=aux;
561            }
562
563            // Mov to C if possible.
564            if (isConstant(c.a)) {
565                const ct = {t: "N", v: c.a.coefs[sONE]};
566                c.c = ctx.lc.add(ctx.lc.mul(c.b, ct), c.c);
567                c.a = { t: "LC", coefs: {} };
568                c.b = { t: "LC", coefs: {} };
569            }
570            if (isConstant(c.b)) {
571                const ct = {t: "N", v: c.b.coefs[sONE]};
572                c.c = ctx.lc.add(ctx.lc.mul(c.a, ct), c.c);
573                c.a = { t: "LC", coefs: {} };
574                c.b = { t: "LC", coefs: {} };
575            }
576
577            if (ctx.lc.isZero(c.a) || ctx.lc.isZero(c.b)) {
578                const isolatedSignal = getFirstInternalSignal(ctx, c.c);
579                if (isolatedSignal) {
580
581                    let lSignal = ctx.signals[isolatedSignal];
582                    while (lSignal.e>=0) {
583                        lSignal = ctx.signals[lSignal.e];
584                    }
585
586
587                    const isolatedSignalEquivalence = {
588                        t: "LC",
589                        coefs: {}
590                    };
591                    const invCoef = ctx.F.inv(c.c.coefs[isolatedSignal]);
592                    for (const s in c.c.coefs) {
593                        if (s != isolatedSignal) {
594                            const v = ctx.F.mul( ctx.F.neg(c.c.coefs[s]), invCoef);
595                            if (!ctx.F.isZero(v)) {
596                                isolatedSignalEquivalence.coefs[s] = v;
597                            }
598                        }
599                    }
600
601                    for (let j in lSignal.inConstraints) {
602                        if ((j!=i)&&(ctx.constraints[j])) {
603                            ctx.constraints[j] = ctx.lc.substitute(ctx.constraints[j], isolatedSignal, isolatedSignalEquivalence);
604                            linkSignalsConstraint(j);
605                            if (j<i) {
606                                nextPossibleConstraints.push(j);
607                            }
608                        }
609                    }
610
611                    ctx.constraints[i] = null;
612
613                    lSignal.c = ctx.stDISCARDED;
614                } else {
615                    if (ctx.lc.isZero(c.c)) ctx.constraints[i] = null;
616                }
617            }
618        }
619        possibleConstraints = nextPossibleConstraints;
620    }
621    unindexVariables();
622
623    // Pack the constraints
624    let o = 0;
625    for (let i=0; i<ctx.constraints.length; i++) {
626        if (ctx.constraints[i]) {
627            if (o != i) {
628                ctx.constraints[o] = ctx.constraints[i];
629            }
630            o++;
631        }
632    }
633    ctx.constraints.length = o;
634
635    function indexVariables() {
636        for (let i=0; i<ctx.constraints.length; i++) linkSignalsConstraint(i);
637    }
638
639    function linkSignalsConstraint(cidx) {
640        const ct = ctx.constraints[cidx];
641        for (let k in ct.a.coefs) linkSignal(k, cidx);
642        for (let k in ct.b.coefs) linkSignal(k, cidx);
643        for (let k in ct.c.coefs) linkSignal(k, cidx);
644    }
645
646    function unindexVariables() {
647        for (let s=0; s<ctx.signals.length; s++) {
648            let lSignal = ctx.signals[s];
649            while (lSignal.e>=0) {
650                lSignal = ctx.signals[lSignal.e];
651            }
652            if (lSignal.inConstraints) delete lSignal.inConstraints;
653        }
654    }
655
656/*
657    function unlinkSignal(signalName, cidx) {
658        let lSignal = ctx.signals[signalName];
659        while (lSignal.e>=0) {
660            lSignal = ctx.signals[lSignal.e];
661        }
662        if ((lSignal.inConstraints)&&(lSignal.inConstraints[cidx])) {
663            delete lSignal.inConstraints[cidx];
664        }
665    }
666*/
667
668    function linkSignal(signalName, cidx) {
669        let lSignal = ctx.signals[signalName];
670        while (lSignal.e>=0) {
671            lSignal = ctx.signals[lSignal.e];
672        }
673        if (!lSignal.inConstraints) lSignal.inConstraints = {};
674        lSignal.inConstraints[cidx] = true;
675    }
676
677    function getFirstInternalSignal(ctx, l) {
678        for (let k in l.coefs) {
679            const signal = ctx.signals[k];
680            if (signal.c == ctx.stINTERNAL) return k;
681        }
682        return null;
683    }
684
685    function isConstant(l) {
686        for (let k in l.coefs) {
687            if ((k != sONE) && (!ctx.F.isZero(l.coefs[k]))) return false;
688        }
689        if (!l.coefs[sONE] || ctx.F.isZero(l.coefs[sONE])) return false;
690        return true;
691    }
692
693}
694
695/*
696
697function buildCircuitDef(ctx, mainCode) {
698    const res = {
699        mainCode: mainCode
700    };
701    res.signalName2Idx = ctx.signalName2Idx;
702
703    res.components = [];
704    res.componentName2Idx = {};
705    for (let c in ctx.components) {
706        const idCoponent = res.components.length;
707        res.components.push({
708            name: c,
709            params: ctx.components[c].params,
710            template: ctx.components[c].template,
711            inputSignals: 0
712        });
713        res.componentName2Idx[c] = idCoponent;
714    }
715
716    res.signals = new Array(ctx.signalNames.length);
717    for (let i=0; i<ctx.signalNames.length; i++) {
718        res.signals[i] = {
719            names: ctx.signalNames[i],
720            triggerComponents: []
721        };
722        ctx.signalNames[i].map( (fullName) => {
723            const idComponet = res.componentName2Idx[ctx.signals[fullName].component];
724            if (ctx.signals[fullName].direction == "IN") {
725                res.signals[i].triggerComponents.push(idComponet);
726                res.components[idComponet].inputSignals++;
727            }
728        });
729    }
730
731    res.constraints = buildConstraints(ctx);
732
733    res.templates = ctx.templates;
734
735    res.functions = {};
736    for (let f in ctx.functions) {
737        res.functions[f] = {
738            params: ctx.functionParams[f],
739            func: ctx.functions[f]
740        };
741    }
742
743    res.nPrvInputs = ctx.totals.prvInput;
744    res.nPubInputs = ctx.totals.pubInput;
745    res.nInputs = res.nPrvInputs + res.nPubInputs;
746    res.nOutputs = ctx.totals.output;
747    res.nVars = res.nInputs + res.nOutputs + ctx.totals.one + ctx.totals.internal;
748    res.nConstants = ctx.totals.constant;
749    res.nSignals = res.nVars + res.nConstants;
750
751    return res;
752}
753
754*/
755
756
757/*
758    Build constraints
759
760A constraint like this
761
762      [s1 + 2*s2 + 3*s3] * [ s2 + 5*s4] - [s0        ] = 0
763      [     5*s2 + 6*s3] * [ s2 +     ] - [s0 + 2* s2] = 0
764      [s1 +          s3] * [ s2 + 5*s3] - [s4        ] = 0
765
766is converted to
767
768[
769    [{"1":"1","2":"2","3":"3"} , {"2":"1","4":"5"} , {"0":"1"         }],
770    [{        "2":"5","3":"6"} , {"2":"1"        } , {"0":"1", "2":"2"}],
771    [{"1":"1",        "3":"1"} , {"2":"1","3":"5"} , {"4":"1"         }]
772]
773           ^                            ^                  ^
774           |                            |                  |
775           A                            B                  C
776
777*/
778/*
779function buildConstraints(ctx) {
780    const res = [];
781
782    function fillLC(dst, src) {
783        if (src.t != "LC") throw new Error("Constraint is not a LINEARCOMBINATION");
784        for (let s in src.coefs) {
785            const v = src.coefs[s].toString();
786            const id = ctx.signalName2Idx[s];
787            dst[id] = v;
788        }
789    }
790
791    for (let i=0; i<ctx.constraints.length; i++) {
792        const A = {};
793        const B = {};
794        const C = {};
795
796        fillLC(A, ctx.constraints[i].a);
797        fillLC(B, ctx.constraints[i].b);
798        fillLC(C, ctx.lc.negate(ctx.constraints[i].c));
799
800        res.push([A,B,C]);
801    }
802
803    return res;
804}
805*/
806
807/*
808function buildSyms(ctx, strm) {
809
810    let nSyms;
811
812    addSymbolsComponent(ctx.mainComponent + ".", ctx.getComponentIdx(ctx.mainComponent));
813
814
815    function addSymbolsComponent(prefix, idComponet) {
816        for (let n in  ctx.components[idComponet].names.o) {
817            const entrie = ctx.components[idComponet].names.o[n];
818            addSymbolArray(prefix+n, entrie.type, entrie.sizes, entrie.offset);
819        }
820    }
821
822    function addSymbolArray(prefix, type, sizes, offset) {
823        if (sizes.length==0) {
824            if (type == "S") {
825                let s=offset;
826                while (ctx.signals[s].e >= 0) s = ctx.signals[s].e;
827                let wId = ctx.signals[s].id;
828                if (typeof(wId) == "undefined") wId=-1;
829                strm.write(`${offset},${wId},${prefix}\n`);
830                nSyms ++;
831                if ((ctx.verbose)&&(nSyms%10000 == 0)) console.log("Symbols saved: "+nSyms);
832            } else {
833                addSymbolsComponent(prefix+".", offset);
834            }
835            return 1;
836        } else {
837            let acc = 0;
838            for (let i=0; i<sizes[0]; i++) {
839                acc += addSymbolArray(`${prefix}[${i}]`, type, sizes.slice(1), offset + acc );
840            }
841            return acc;
842        }
843    }
844
845}
846
847*/
848