diff --git a/c/calcwit.cpp b/c/calcwit.cpp index fe0b6a1..8e14e06 100644 --- a/c/calcwit.cpp +++ b/c/calcwit.cpp @@ -142,7 +142,7 @@ void Circom_CalcWit::checkConstraint(PBigInt value1, PBigInt value2, char const #ifdef SANITY_CHECK if (mpz_cmp(*value1, *value2) != 0) { char *pcV1 = mpz_get_str(0, 10, *value1); - char *pcV2 = mpz_get_str(0, 10, *value1); + char *pcV2 = mpz_get_str(0, 10, *value2); std::string sV1 = std::string(pcV1); std::string sV2 = std::string(pcV2); free(pcV1); diff --git a/c/zqfield.cpp b/c/zqfield.cpp index e2c08df..764337e 100644 --- a/c/zqfield.cpp +++ b/c/zqfield.cpp @@ -1,14 +1,27 @@ #include "zqfield.h" ZqField::ZqField(PBigInt ap) { + mpz_init2(tmp, 1024); mpz_init_set(p, *ap); mpz_init_set_ui(zero, 0); mpz_init_set_ui(one, 1); } +ZqField::~ZqField() { + mpz_clear(tmp); + mpz_clear(p); + mpz_clear(zero); + mpz_clear(one); +} + void ZqField::add(PBigInt r, PBigInt a, PBigInt b) { - mpz_add(*r,*a,*b); - mpz_fdiv_r(*r, *r, p); + mpz_add(tmp,*a,*b); + mpz_fdiv_r(*r, tmp, p); +} + +void ZqField::mul(PBigInt r, PBigInt a, PBigInt b) { + mpz_mul(tmp,*a,*b); + mpz_fdiv_r(*r, tmp, p); } void ZqField::lt(PBigInt r, PBigInt a, PBigInt b) { diff --git a/c/zqfield.h b/c/zqfield.h index 57f6288..fcb8e7b 100644 --- a/c/zqfield.h +++ b/c/zqfield.h @@ -4,15 +4,18 @@ #include "circom.h" class ZqField { + mpz_t tmp; public: BigInt p; BigInt one; BigInt zero; ZqField(PBigInt ap); + ~ZqField(); void copyn(PBigInt a, PBigInt b, int n); void add(PBigInt r,PBigInt a, PBigInt b); + void mul(PBigInt r,PBigInt a, PBigInt b); void lt(PBigInt r, PBigInt a, PBigInt b); int isTrue(PBigInt a); }; diff --git a/src/c_build.js b/src/c_build.js index 728274f..4dfc4f1 100644 --- a/src/c_build.js +++ b/src/c_build.js @@ -445,6 +445,7 @@ function buildFunction(name, paramValues) { } else { res.type = "CONSTVAL"; res.returnValue = ctx.returnValue; + res.returnSizes = ctx.returnSizes; } ctx.scopes = oldScopes; @@ -507,7 +508,7 @@ function hashFunctionCall(ctx, name, paramValues) { const constParams = []; for (let i=0; i0) S+=","; S+=value2str(v[i]); } + S+="]"; return S; } else { return bigInt(v).toString(); diff --git a/src/c_gen.js b/src/c_gen.js index 3b9a5b1..4acb188 100644 --- a/src/c_gen.js +++ b/src/c_gen.js @@ -17,7 +17,7 @@ function newRef(ctx, type, _name, value, sizes) { } } if (Array.isArray(sizes)) { - sizes = utils.accSizes(sizes); + // sizes = sizes; } else if (utils.isDefined(value)) { sizes = utils.accSizes(utils.extractSizes(value)); } else { @@ -37,13 +37,12 @@ function newRef(ctx, type, _name, value, sizes) { }; if (utils.isDefined(value)) { - scope[name].value = value; + scope[name].value = utils.flatArray(value); } return name; } - function instantiateRef(ctx, name, initValue) { const v = getScope(ctx, name); if (!v.stack) return error(ctx, null, "Using a non existing var: " + name); @@ -59,13 +58,16 @@ function instantiateRef(ctx, name, initValue) { } v.used = true; if (utils.isDefined(initValue)) { - const flatedValue = utils.flatArray(initValue); - for (let i=0; i0) { + sizes=[]; + for (let i=0; i< ast.name.selectors.length; i++) { + const sizeName = gen(ctx, ast.name.selectors[i]); + const size = getScope(ctx, sizeName); + if (size.sizes[0] != 1) return error(ctx, ast, "A selector cannot be an array"); + if (ctx.error) return; + + if (size.used) { + return error(ctx, ast, "Variable size variables not allowed"); + } else { + sizes.push(size.value[0].toJSNumber()); + } } + sizes = utils.accSizes(sizes); + } else { + sizes = null; // If not sizes, the sized are defined in the first assignement. } - sizes = utils.accSizes(sizes); const res = scope[varName] = { stack: true, @@ -320,6 +330,10 @@ function genDeclareVariable(ctx, ast) { used: false, }; + if (sizes) { + res.value = new Array(sizes[0]); + } + scope[varName] = res; return varName; @@ -396,7 +410,7 @@ function genGetOffset(ctx, vOffset, vSizes, sels) { rStr += rN; rN =0; } - if (rStr == iOffset.label) { + if ((vOffset)&&(rStr == iOffset.label)) { return vOffset; } else { const res = newRef(ctx, "INT", "_offset"); @@ -410,9 +424,11 @@ function genGetOffset(ctx, vOffset, vSizes, sels) { function genVariable(ctx, ast) { const v = getScope(ctx, ast.name); + const l = ast.selectors ? ast.selectors.length : 0; + if (v.type == "SIGNAL") { let vOffset; - if (ast.selectors.length>0) { + if (l>0) { const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", ast.name); const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", ast.name); vOffset = genGetOffset(ctx, vsOffset, vsSizes, ast.selectors ); @@ -422,36 +438,37 @@ function genVariable(ctx, ast) { return genGetSignal(ctx, "ctx->cIdx", vOffset); } else if (v.type == "BIGINT") { - const vOffset = genGetOffset(ctx, 0, v.sizes, ast.sels ); + const vOffset = genGetOffset(ctx, 0, v.sizes, ast.selectors ); const offset = getScope(ctx, vOffset); if (v.used) { if (offset.used) { - const res = newRef(ctx, "BIGINT", "_v", v.sizes.slice(ast.sels.length)); + const resN = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); + const res = getScope(ctx, resN); res.used = true; - ctx.codeHeader += `PBigInt ${res.label}`; - ctx.code += `${res} = ${ast.name} + ${vOffset.label};\n`; - return res; + ctx.codeHeader += `PBigInt ${res.label};\n`; + ctx.code += `${res.label} = ${ast.name} + ${offset.label};\n`; + return resN; } else if (offset.value) { - const res = newRef(ctx, "BIGINT", "_v", v.sizes.slice(ast.sels.length)); + const resN = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); + const res = getScope(ctx, resN); res.used = true; - ctx.codeHeader += `PBigInt ${res.label}`; - ctx.code += `${res} = ${ast.name} + ${vOffset.value};\n`; - return res; + ctx.codeHeader += `PBigInt ${res.label};\n`; + ctx.code += `${res.label} = ${ast.name} + ${offset.value};\n`; + return resN; } else { return ast.name; } } else { if (offset.used) { instantiateRef(ctx, ast.name, v.value); - const res = newRef(ctx, "BIGINT", "_v", v.sizes.slice(ast.sels.length)); + const res = newRef(ctx, "BIGINT", "_v", null, v.sizes.slice(l)); res.used = true; - ctx.codeHeader += `PBigInt ${res.label}`; - ctx.code += `${res} = ${ast.name} + ${vOffset.label};\n`; + ctx.codeHeader += `PBigInt ${res};\n`; + ctx.code += `${res} = ${ast.name} + ${offset.label};\n`; return res; } else { - const sa = utils.subArray(v.value, ast.selectors); - const res = newRef(ctx, "BIGINT", "_v", sa); - return res; + // return newSubRef(ctx, ast.name, ast.selectors); + return newRef(ctx, "BIGINT", "_v", v.value.slice(offset.value[0], offset.value[0] + v.sizes[l]),v.sizes.slice(l)); } } } @@ -550,39 +567,106 @@ function genGetSignal(ctx, cIdx, sIdx) { return res; } -function genVarAssignement(ctx, ast) { +function genPinAssignement(ctx, ast) { + let vcIdx; + if (ast.values[0].component.selectors.length>0) { + const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); + const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.values[0].component.name); + vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); + } else { + vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); + } - let lName; - let sels; + let vsIdx; + if (ast.values[0].pin.selectors.length>0) { + const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); + const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); + vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); + } else { + vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); + } - if (ctx.error) return; + const vVal = gen(ctx, ast.values[1]); + + genSetSignal(ctx, vcIdx, vsIdx, vVal); + + return vVal; +} + +function genSignalAssignmen(ctx, ast, lName, sels, rName) { + let vsIdx; + if (sels.length>0) { + const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", lName); + const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", lName); + vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); + } else { + vsIdx = genGetSigalOffset(ctx, "ctx->cIdx", lName); + } - if (ast.values[0].type == "PIN") { + return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); +} + +function genVarAssignment(ctx, ast, lName, sels, rName) { - let vcIdx; - if (ast.values[0].component.selectors.length>0) { - const vcOffset = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); - const vcSizes = genGetSubComponentSizes(ctx, "ctx->cIdx", ast.values[0].component.name); - vcIdx = genGetOffset(ctx, vcOffset, vcSizes, ast.values[0].component.selectors ); + const left = getScope(ctx, lName); + const right = getScope(ctx, rName); + if (!utils.isDefined(left.sizes)) { + left.sizes = right.sizes; + left.value = new Array(left.sizes[0]); + } + if (!utils.sameSizes(left.sizes.slice(sels.length), right.sizes)) return error(ctx, ast, "Sizes do not match"); + + const oName = genGetOffset(ctx, 0, left.sizes, sels); + const offset = getScope(ctx, oName); + + let instantiated=false; + if (left.used) { + instantiateRef(ctx, rName, right.value); + instantiated=true; + } else if (right.used) { + if (sels.length == 0) { + instantiateRef(ctx,lName); + } else { + instantiateRef(ctx,lName, left.value); + } + instantiated=true; + } else if (offset.used) { + instantiateRef(ctx, rName, right.value); + if (sels.length == 0) { + instantiateRef(ctx,lName); } else { - vcIdx = genGetSubComponentOffset(ctx, "ctx->cIdx", ast.values[0].component.name); + instantiateRef(ctx,lName, left.value); } + instantiated=true; + } - let vsIdx; - if (ast.values[0].pin.selectors.length>0) { - const vsOffset = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); - const vsSizes = genGetSignalSizes(ctx, vcIdx, ast.values[0].pin.name); - vsIdx = genGetOffset(ctx, vsOffset, vsSizes, ast.values[0].pin.selectors ); + if (instantiated) { + if (offset.used) { + ctx.code += `ctx->field->copyn(${lName} + ${oName}, ${rName}, ${right.sizes[0]});\n`; } else { - vsIdx = genGetSigalOffset(ctx, vcIdx, ast.values[0].pin.name); + if (offset.value[0]>0) { + ctx.code += `ctx->field->copyn(${lName} + ${offset.value[0]}, ${rName}, ${right.sizes[0]});\n`; + } else { + ctx.code += `ctx->field->copyn(${lName}, ${rName}, ${right.sizes[0]});\n`; + } } + } else { + if (offset.value[0]>0) { + for (let i=0; i0) { - const vsOffset = genGetSigalOffset(ctx, "ctx->cIdx", lName); - const vsSizes = genGetSignalSizes(ctx, "ctx->cIdx", lName); - vsIdx = genGetOffset(ctx, vsOffset, vsSizes, sels ); - } else { - vsIdx = genGetSigalOffset(ctx, "ctx->cIdx", lName); - } + if (left.type == "BIGINT") return genVarAssignment(ctx, ast, lName, sels, rName); - return genSetSignal(ctx, "ctx->cIdx", vsIdx, rName); - } else if (left.type == "BIGINT") { - if (!utils.sameSizes(left.sizes, right.sizes)) return error(ctx, ast, "Sizes do not match"); - if (left.used) { - if (!right.used) { - instantiateRef(ctx, rName, right.value); - } - ctx.code += `ctx->field->copyn(${left.label}, ${right.label}, ${right.sizes[0]});\n`; - } else { - if (right.used) { - instantiateRef(ctx, lName); - ctx.code += `ctx->field->copyn(${left.label}, ${right.label}, ${right.sizes[0]});\n`; - } else { - left.value = right.value; - } - } - } else { - return error(ctx, ast, "Assigning to invalid"); - } - return lName; + return error(ctx, ast, "Assigning to invalid"); } function genConstraint(ctx, ast) { @@ -655,13 +711,43 @@ function genConstraint(ctx, ast) { function genArray(ctx, ast) { - let S = "["; + let subSizes; + let instantiate = false; + if (ast.values.length == 0) return error(ctx, ast, "Arrays with zero elements not allowed"); + const value = []; + const names = []; for (let i=0; i0) S += ","; - S += gen(ctx, ast.values[i]); + const eName = gen(ctx, ast.values[i]); + const e = getScope(ctx, eName); + + if (i==0) { + subSizes = e.sizes; + } else { + if (!utils.sameSizes(subSizes, e.sizes)) return error(ctx, ast, "Heteroeneus array not allowed"); + } + if (e.used) { + instantiate = true; + } + if (!instantiate) { + value.push(...e.value); + } + names.push(eName); + } + + const newSize = [subSizes[0]*ast.values.length , ...subSizes]; + + if (instantiate) { + const rName = newRef(ctx, "BIGINT", "_arr", null, newSize); + instantiateRef(ctx, rName); + for (let i=0; ifield->copyn(${rName}+${i*subSizes[0]}, ${names[i]}, ${subSizes[0]});\n`; + } + return rName; + } else { + const rName = newRef(ctx, "BIGINT", "_arr", value, newSize); + return rName; } - S+="]"; - return S; + } @@ -675,7 +761,7 @@ function genFunctionCall(ctx, ast) { const fn = ctx.buildFunction(ast.name, params); if (fn.type == "VARVAL_CONSTSIZE") { - const res = newRef(ctx, "BIGINT", `_ret${ast.name}Sizes`, fn.retSizes); + const res = newRef(ctx, "BIGINT", `_ret${ast.name}Sizes`, null, fn.returnSizes); instantiateRef(ctx, res); ctx.code +=`${fn.fnName}(ctx, ${res}`; @@ -686,7 +772,7 @@ function genFunctionCall(ctx, ast) { return res; } else { - const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue); + const res = newRef(ctx, "BIGINT", "_retVal", fn.returnValue, fn.returnSizes); return res; } } @@ -727,6 +813,8 @@ function genFor(ctx, ast) { const condName = gen(ctx, ast.condition); const cond = getScope(ctx, condName); + if (!utils.sameSizes(cond.sizes, [1,0])) return error(ctx, ast.condition, "Operation cannot be done on an array"); + if (cond.used) { inLoop = true; enterConditionalCode(ctx); @@ -738,7 +826,7 @@ function genFor(ctx, ast) { `while (${condVar}) {\n`; } else { if (!utils.isDefined(cond.value)) return error(ctx, ast, "condition value not assigned"); - if (cond.value.isZero()) end=true; + if (cond.value[0].isZero()) end=true; } @@ -772,7 +860,7 @@ function genFor(ctx, ast) { `while (${condVar}) {\n`; } else { ctx.code = oldCode + ctx.code; - if (cond2.value.isZero()) end=true; + if (cond2.value[0].isZero()) end=true; } } else { ctx.code = @@ -844,35 +932,35 @@ function genReturn(ctx, ast) { -function genSignalAssignConstrain(ctx, ast) { - const res = genVarAssignement(ctx, ast); +function genSignalAssignConstraint(ctx, ast) { + const res = genAssignement(ctx, ast); // genConstraint(ctx, ast); return res; // return genVarAssignement(ctx, ast); } function genVarAddAssignement(ctx, ast) { - return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: ast.values}]}); } function genVarMulAssignement(ctx, ast) { - return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "*", values: ast.values}]}); } function genPlusPlusRight(ctx, ast) { - return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; + return `(${genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; } function genPlusPlusLeft(ctx, ast) { - return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "+", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); } function genMinusMinusRight(ctx, ast) { - return `(${genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; + return `(${genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]})}).add(__P__).sub(bigInt(1)).mod(__P__)`; } function genMinusMinusLeft(ctx, ast) { - return genVarAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); + return genAssignement(ctx, {values: [ast.values[0], {type: "OP", op: "-", values: [ast.values[0], {type: "NUMBER", value: bigInt(1)}]}]}); } function genBinaryOp(ctx, ast, op) { @@ -886,6 +974,8 @@ function genBinaryOp(ctx, ast, op) { if ((!a.used)&&(!utils.isDefined(a.value))) return error(ctx, ast, "Using a not assigned varialble: "+aName); if ((!b.used)&&(!utils.isDefined(b.value))) return error(ctx, ast, "Using a not assigned varialble: "+bName); + if (!utils.sameSizes(a.sizes, [1,0])) return error(ctx, ast, "Operation cannot be done on an array"); + if (!utils.sameSizes(b.sizes, [1,0])) return error(ctx, ast, "Operation cannot be done on an array"); let rName; if (a.used || b.used) { @@ -900,18 +990,11 @@ function genBinaryOp(ctx, ast, op) { instantiateRef(ctx, rName); ctx.code += `ctx->field->${op}(${rName},${aName}, ${bName});\n`; } else { - rName = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](a.value, b.value)); + rName = newRef(ctx, "BIGINT", "_tmp", ctx.field[op](a.value[0], b.value[0])); } return rName; } -function genMul(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).mul(bigInt(${b})).mod(__P__)`; -} function genSub(ctx, ast) { const a = gen(ctx, ast.values[0]); @@ -995,14 +1078,6 @@ function genMod(ctx, ast) { return `bigInt(${a}).mod(bigInt(${b}))`; } -function genLt(ctx, ast) { - const a = gen(ctx, ast.values[0]); - if (ctx.error) return; - const b = gen(ctx, ast.values[1]); - if (ctx.error) return; - return `bigInt(${a}).lt(bigInt(${b})) ? 1 : 0`; -} - function genGt(ctx, ast) { const a = gen(ctx, ast.values[0]); if (ctx.error) return; diff --git a/src/utils.js b/src/utils.js index ed27f80..b5dc2a7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,7 +6,6 @@ module.exports.ident =ident; module.exports.extractSizes =extractSizes; module.exports.flatArray = flatArray; module.exports.csArr = csArr; -module.exports.subArray = subArray; module.exports.accSizes = accSizes; module.exports.fnvHash = fnvHash; module.exports.stringifyBigInts = stringifyBigInts; @@ -54,12 +53,6 @@ function csArr(_arr) { return S; } -function subArray(value, sels) { - if ((!sels) || (sels.length == 0)) return value; - - return subArray(value[sels[0]], sels.slice(1)); -} - function accSizes(_sizes) { const sizes = _sizes || []; const accSizes = [1, 0]; @@ -91,6 +84,8 @@ function stringifyBigInts(o) { } } + + function unstringifyBigInts(o) { if ((typeof(o) == "string") && (/^[0-9]+$/.test(o) )) { return bigInt(o); diff --git a/src/zqfield.js b/src/zqfield.js index 1d0caf9..fac7f4c 100644 --- a/src/zqfield.js +++ b/src/zqfield.js @@ -9,6 +9,10 @@ module.exports = class ZqField { return a.add(b).mod(this.p); } + mul(a, b) { + return a.mul(b).mod(this.p); + } + lt(a, b) { return a.lt(b) ? bigInt(1) : bigInt(0); } diff --git a/test/basiccases.js b/test/basiccases.js index c4f6c87..c19c4ea 100644 --- a/test/basiccases.js +++ b/test/basiccases.js @@ -97,4 +97,14 @@ describe("basic cases", function () { ] ); }); + it("arrays", async () => { + await doTest( + "arrays.circom", + [ + [{in: 0}, {out: [1, 8, 51]}], + [{in: 10}, {out: [11, 28, 111]}], + [{in: __P__.minus(2)}, {out: [__P__.minus(1), 4, 39]}], + ] + ); + }); }); diff --git a/test/circuits/arrays.circom b/test/circuits/arrays.circom new file mode 100644 index 0000000..17df863 --- /dev/null +++ b/test/circuits/arrays.circom @@ -0,0 +1,43 @@ +// arr1 + + +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) { + res[1] = res[1] + arr2[i]; + } + + res[2] = 0; + for (i=0; i<2; i++) { + for (j=0; j<3; j += 1) { + res[2] = res[2] + arr3[i][j]; + } + } + + return res; +} + +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]]); + + out[0] <-- d[0] + c[0]; + out[0] === in+c[0]; + + out[1] <-- d[1]+c[1]; + out[1] === 2*in+3+c[1]; + + out[2] <-- d[2]+c[2]; + out[2] === 6*in+12+c[2]; +} + +component main = Main();