package field import ( "math/bits" "github.com/consensys/gnark-crypto/field/goldilocks" "github.com/consensys/gnark/frontend" ) const D = 2 type QuadraticExtension = [2]F type QEAlgebra = [D]QuadraticExtension type QuadraticExtensionAPI struct { api frontend.API fieldAPI FieldAPI W F DTH_ROOT F ONE_QE QuadraticExtension ZERO_QE QuadraticExtension ZERO_QE_ALGEBRA QEAlgebra } func NewQuadraticExtensionAPI(api frontend.API, fieldAPI FieldAPI) *QuadraticExtensionAPI { var ZERO_QE = QuadraticExtension{ZERO_F, ZERO_F} var ZERO_QE_ALGEBRA QEAlgebra for i := 0; i < D; i++ { ZERO_QE_ALGEBRA[i] = ZERO_QE } return &QuadraticExtensionAPI{ api: api, fieldAPI: fieldAPI, W: NewFieldConst(7), DTH_ROOT: NewFieldConst(18446744069414584320), ONE_QE: QuadraticExtension{ONE_F, ZERO_F}, ZERO_QE: ZERO_QE, ZERO_QE_ALGEBRA: ZERO_QE_ALGEBRA, } } func (c *QuadraticExtensionAPI) SquareExtension(a QuadraticExtension) QuadraticExtension { return c.MulExtension(a, a) } func (c *QuadraticExtensionAPI) MulExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { c_0 := c.fieldAPI.Add(c.fieldAPI.Mul(a[0], b[0]), c.fieldAPI.Mul(c.fieldAPI.Mul(c.W, a[1]), b[1])) c_1 := c.fieldAPI.Add(c.fieldAPI.Mul(a[0], b[1]), c.fieldAPI.Mul(a[1], b[0])) return QuadraticExtension{c_0, c_1} } func (c *QuadraticExtensionAPI) AddExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { c_0 := c.fieldAPI.Add(a[0], b[0]) c_1 := c.fieldAPI.Add(a[1], b[1]) return QuadraticExtension{c_0, c_1} } func (c *QuadraticExtensionAPI) SubExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { c_0 := c.fieldAPI.Sub(a[0], b[0]) c_1 := c.fieldAPI.Sub(a[1], b[1]) return QuadraticExtension{c_0, c_1} } func (c *QuadraticExtensionAPI) DivExtension(a QuadraticExtension, b QuadraticExtension) QuadraticExtension { return c.MulExtension(a, c.InverseExtension(b)) } func (c *QuadraticExtensionAPI) IsZero(a QuadraticExtension) frontend.Variable { return c.api.Mul(IsZero(c.api, c.fieldAPI, a[0]), IsZero(c.api, c.fieldAPI, a[1])) } // TODO: Instead of calculating the inverse within the circuit, can witness the // inverse and assert that a_inverse * a = 1. Should reduce # of constraints. func (c *QuadraticExtensionAPI) InverseExtension(a QuadraticExtension) QuadraticExtension { // First assert that a doesn't have 0 value coefficients a0_is_zero := IsZero(c.api, c.fieldAPI, a[0]) a1_is_zero := IsZero(c.api, c.fieldAPI, a[1]) // assert that a0_is_zero OR a1_is_zero == false c.api.AssertIsEqual(c.api.Mul(a0_is_zero, a1_is_zero), frontend.Variable(0)) a_pow_r_minus_1 := QuadraticExtension{a[0], c.fieldAPI.Mul(a[1], c.DTH_ROOT)} a_pow_r := c.MulExtension(a_pow_r_minus_1, a) return c.ScalarMulExtension(a_pow_r_minus_1, c.fieldAPI.Inverse(a_pow_r[0])) } func (c *QuadraticExtensionAPI) ScalarMulExtension(a QuadraticExtension, scalar F) QuadraticExtension { return QuadraticExtension{c.fieldAPI.Mul(a[0], scalar), c.fieldAPI.Mul(a[1], scalar)} } func (c *QuadraticExtensionAPI) FieldToQE(a F) QuadraticExtension { return QuadraticExtension{a, ZERO_F} } // / Exponentiate `base` to the power of a known `exponent`. func (c *QuadraticExtensionAPI) ExpU64Extension(a QuadraticExtension, exponent uint64) QuadraticExtension { switch exponent { case 0: return c.ONE_QE case 1: return a case 2: return c.SquareExtension(a) default: } current := a product := c.ONE_QE for i := 0; i < bits.Len64(exponent); i++ { if i != 0 { current = c.SquareExtension(current) } if (exponent >> i & 1) != 0 { product = c.MulExtension(product, current) } } return product } func (c *QuadraticExtensionAPI) ReduceWithPowers(terms []QuadraticExtension, scalar QuadraticExtension) QuadraticExtension { sum := c.ZERO_QE for i := len(terms) - 1; i >= 0; i-- { sum = c.AddExtension( c.MulExtension( sum, scalar, ), terms[i], ) } return sum } func (c *QuadraticExtensionAPI) Select(b frontend.Variable, qe0, qe1 QuadraticExtension) QuadraticExtension { var retQE QuadraticExtension for i := 0; i < 2; i++ { retQE[i] = c.fieldAPI.Select(b, qe0[i], qe1[i]) } return retQE } func (c *QuadraticExtensionAPI) Lookup2(b0 frontend.Variable, b1 frontend.Variable, qe0, qe1, qe2, qe3 QuadraticExtension) QuadraticExtension { var retQE QuadraticExtension for i := 0; i < 2; i++ { retQE[i] = c.fieldAPI.Lookup2(b0, b1, qe0[i], qe1[i], qe2[i], qe3[i]) } return retQE } func (c *QuadraticExtensionAPI) AssertIsEqual(a, b QuadraticExtension) { for i := 0; i < 2; i++ { c.fieldAPI.AssertIsEqual(a[i], b[i]) } } func (c *QuadraticExtensionAPI) InnerProductExtension(constant F, startingAcc QuadraticExtension, pairs [][2]QuadraticExtension) QuadraticExtension { acc := startingAcc for i := 0; i < len(pairs); i++ { a := pairs[i][0] b := pairs[i][1] mul := c.ScalarMulExtension(a, constant) mul = c.MulExtension(mul, b) acc = c.AddExtension(acc, mul) } return acc } /* func (c *QuadraticExtensionAPI) Println(a QuadraticExtension) { fmt.Print("Degree 0 coefficient") c.fieldAPI.Println(a[0]) fmt.Print("Degree 1 coefficient") c.fieldAPI.Println(a[1]) } */ func (c *QuadraticExtensionAPI) MulExtensionAlgebra(a, b QEAlgebra) QEAlgebra { var inner [D][][2]QuadraticExtension var inner_w [D][][2]QuadraticExtension for i := 0; i < D; i++ { for j := 0; j < D-i; j++ { idx := (i + j) % D inner[idx] = append(inner[idx], [2]QuadraticExtension{a[i], b[j]}) } for j := D - i; j < D; j++ { idx := (i + j) % D inner_w[idx] = append(inner_w[idx], [2]QuadraticExtension{a[i], b[j]}) } } var product QEAlgebra for i := 0; i < D; i++ { acc := c.InnerProductExtension(c.W, c.ZERO_QE, inner_w[i]) product[i] = c.InnerProductExtension(ONE_F, acc, inner[i]) } return product } func (c *QuadraticExtensionAPI) ScalarMulExtensionAlgebra(a QuadraticExtension, b QEAlgebra) QEAlgebra { var product QEAlgebra for i := 0; i < D; i++ { product[i] = c.MulExtension(a, b[i]) } return product } func (c *QuadraticExtensionAPI) AddExtensionAlgebra(a, b QEAlgebra) QEAlgebra { var sum QEAlgebra for i := 0; i < D; i++ { sum[i] = c.AddExtension(a[i], b[i]) } return sum } func (c *QuadraticExtensionAPI) SubExtensionAlgebra(a, b QEAlgebra) QEAlgebra { var diff QEAlgebra for i := 0; i < D; i++ { diff[i] = c.SubExtension(a[i], b[i]) } return diff } func (c *QuadraticExtensionAPI) PartialInterpolateExtAlgebra( domain []goldilocks.Element, values []QEAlgebra, barycentricWeights []goldilocks.Element, point QEAlgebra, initialEval QEAlgebra, initialPartialProd QEAlgebra, ) (QEAlgebra, QEAlgebra) { n := len(values) if n == 0 { panic("Cannot interpolate with no values") } if n != len(domain) { panic("Domain and values must have the same length") } if n != len(barycentricWeights) { panic("Domain and barycentric weights must have the same length") } newEval := initialEval newPartialProd := initialPartialProd for i := 0; i < n; i++ { val := values[i] x := domain[i] xField := NewFieldConst(x.Uint64()) xQE := QuadraticExtension{xField, ZERO_F} xQEAlgebra := QEAlgebra{xQE, c.ZERO_QE} weight := QuadraticExtension{NewFieldConst(barycentricWeights[i].Uint64()), ZERO_F} term := c.SubExtensionAlgebra(point, xQEAlgebra) weightedVal := c.ScalarMulExtensionAlgebra(weight, val) newEval = c.MulExtensionAlgebra(newEval, term) tmp := c.MulExtensionAlgebra(weightedVal, newPartialProd) newEval = c.AddExtensionAlgebra(newEval, tmp) newPartialProd = c.MulExtensionAlgebra(newPartialProd, term) } return newEval, newPartialProd }