|
| 1 | +// Copyright 2020-2026 Consensys Software Inc. |
| 2 | +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. |
| 3 | + |
| 4 | +package fields_bls12377 |
| 5 | + |
| 6 | +import ( |
| 7 | + "github.com/consensys/gnark/frontend" |
| 8 | +) |
| 9 | + |
| 10 | +// Torus-based arithmetic for cyclotomic subgroup |
| 11 | +// The torus T₆ represents elements of the cyclotomic subgroup of E12 using E6 |
| 12 | +// For x in cyclotomic subgroup: x = (1 + y·w) / (1 - y·w) where y ∈ E6 |
| 13 | + |
| 14 | +// TorusSquare computes the square in torus representation |
| 15 | +// Input: y ∈ E6 representing x ∈ T₆ |
| 16 | +// Output: y' ∈ E6 representing x² ∈ T₆ |
| 17 | +// Formula: y' = 2y / (1 + y²·v) where v is the cubic non-residue |
| 18 | +func TorusSquare(api frontend.API, y E6) E6 { |
| 19 | + // Compute numerator: 2y |
| 20 | + var num E6 |
| 21 | + num.Double(api, y) |
| 22 | + |
| 23 | + // Compute y² |
| 24 | + var ySq E6 |
| 25 | + ySq.Square(api, y) |
| 26 | + |
| 27 | + // Compute denominator: 1 + y²·v |
| 28 | + var denom E6 |
| 29 | + denom.MulByNonResidue(api, ySq) |
| 30 | + denom.B0.A0 = api.Add(denom.B0.A0, 1) |
| 31 | + |
| 32 | + // Compute result = num / denom (DivUnchecked uses hint internally) |
| 33 | + var result E6 |
| 34 | + result.DivUnchecked(api, num, denom) |
| 35 | + |
| 36 | + return result |
| 37 | +} |
| 38 | + |
| 39 | +// TorusMul computes multiplication in torus representation |
| 40 | +// Input: y1, y2 ∈ E6 representing x1, x2 ∈ T₆ |
| 41 | +// Output: y' ∈ E6 representing x1·x2 ∈ T₆ |
| 42 | +// Formula: y' = (y1 + y2) / (1 + y1·y2·v) |
| 43 | +func TorusMul(api frontend.API, y1, y2 E6) E6 { |
| 44 | + // Compute numerator: y1 + y2 |
| 45 | + var num E6 |
| 46 | + num.Add(api, y1, y2) |
| 47 | + |
| 48 | + // Compute y1·y2 |
| 49 | + var prod E6 |
| 50 | + prod.Mul(api, y1, y2) |
| 51 | + |
| 52 | + // Compute denominator: 1 + y1·y2·v |
| 53 | + var denom E6 |
| 54 | + denom.MulByNonResidue(api, prod) |
| 55 | + denom.B0.A0 = api.Add(denom.B0.A0, 1) |
| 56 | + |
| 57 | + // Compute result = num / denom (DivUnchecked uses hint internally) |
| 58 | + var result E6 |
| 59 | + result.DivUnchecked(api, num, denom) |
| 60 | + |
| 61 | + return result |
| 62 | +} |
| 63 | + |
| 64 | +// TorusMulBy01 computes multiplication by sparse element (l0, l1, 0) in torus |
| 65 | +// This is used for line multiplication where the line projects to (-c3, -c4, 0) |
| 66 | +// Formula: y' = (y + sparse) / (1 + y·sparse·v) |
| 67 | +func TorusMulBy01(api frontend.API, y E6, l0, l1 E2) E6 { |
| 68 | + // Sparse element: (l0, l1, 0) |
| 69 | + var sparse E6 |
| 70 | + sparse.B0 = l0 |
| 71 | + sparse.B1 = l1 |
| 72 | + // B2 is zero - must be explicit circuit zero |
| 73 | + sparse.B2.A0 = 0 |
| 74 | + sparse.B2.A1 = 0 |
| 75 | + |
| 76 | + // Compute numerator: y + sparse |
| 77 | + var num E6 |
| 78 | + num.Add(api, y, sparse) |
| 79 | + |
| 80 | + // Compute y · sparse using MulBy01 |
| 81 | + prod := y |
| 82 | + prod.MulBy01(api, l0, l1) |
| 83 | + |
| 84 | + // Compute denominator: 1 + prod·v |
| 85 | + var denom E6 |
| 86 | + denom.MulByNonResidue(api, prod) |
| 87 | + denom.B0.A0 = api.Add(denom.B0.A0, 1) |
| 88 | + |
| 89 | + // Compute result = num / denom (DivUnchecked uses hint internally) |
| 90 | + var result E6 |
| 91 | + result.DivUnchecked(api, num, denom) |
| 92 | + |
| 93 | + return result |
| 94 | +} |
| 95 | + |
| 96 | +// TorusDecompress converts torus representation back to E12 |
| 97 | +// Input: y ∈ E6 representing x ∈ T₆ |
| 98 | +// Output: x ∈ E12 where x = (1 + y·w) / (1 - y·w) |
| 99 | +// |
| 100 | +// We compute C0 and C1 such that x = C0 + C1·w |
| 101 | +// From (1 + y·w) = (C0 + C1·w)(1 - y·w): |
| 102 | +// |
| 103 | +// 1 = C0 - C1·y·v => C0 = 1 + C1·y·v |
| 104 | +// y = C1 - C0·y => C1 = y + C0·y = y(1 + C0) |
| 105 | +// |
| 106 | +// Substituting: C1 = y(1 + 1 + C1·y·v) = y(2 + C1·y·v) |
| 107 | +// C1 = 2y + C1·y²·v |
| 108 | +// C1(1 - y²·v) = 2y |
| 109 | +// C1 = 2y / (1 - y²·v) |
| 110 | +func TorusDecompress(api frontend.API, y E6) E12 { |
| 111 | + // Compute y² |
| 112 | + var ySq E6 |
| 113 | + ySq.Square(api, y) |
| 114 | + |
| 115 | + // Compute y²·v |
| 116 | + var ySqV E6 |
| 117 | + ySqV.MulByNonResidue(api, ySq) |
| 118 | + |
| 119 | + // Compute denominator: 1 - y²·v |
| 120 | + var one E6 |
| 121 | + one.SetOne() |
| 122 | + var denom E6 |
| 123 | + denom.Sub(api, one, ySqV) |
| 124 | + |
| 125 | + // Compute numerator: 2y |
| 126 | + var num E6 |
| 127 | + num.Double(api, y) |
| 128 | + |
| 129 | + // C1 = 2y / (1 - y²·v) |
| 130 | + var c1 E6 |
| 131 | + c1.DivUnchecked(api, num, denom) |
| 132 | + |
| 133 | + // C0 = 1 + C1·y·v |
| 134 | + var c1y E6 |
| 135 | + c1y.Mul(api, c1, y) |
| 136 | + var c1yv E6 |
| 137 | + c1yv.MulByNonResidue(api, c1y) |
| 138 | + var c0 E6 |
| 139 | + c0.Add(api, one, c1yv) |
| 140 | + |
| 141 | + return E12{C0: c0, C1: c1} |
| 142 | +} |
| 143 | + |
| 144 | +// FrobeniusTorus computes the Frobenius endomorphism on torus element |
| 145 | +// For y ∈ E6 representing W in cyclotomic subgroup via W = (1+y·w)/(1-y·w): |
| 146 | +// Frob(W) = (1 + z·w) / (1 - z·w) where z = FrobeniusTorus(y) |
| 147 | +// Formula: |
| 148 | +// |
| 149 | +// z.B0 = Conj(y.B0) · frobw |
| 150 | +// z.B1 = Conj(y.B1) · frobvw |
| 151 | +// z.B2 = Conj(y.B2) · frobv2w |
| 152 | +func FrobeniusTorus(api frontend.API, y E6) E6 { |
| 153 | + var z E6 |
| 154 | + z.B0.Conjugate(api, y.B0).MulByFp(api, z.B0, ext.frobw) |
| 155 | + z.B1.Conjugate(api, y.B1).MulByFp(api, z.B1, ext.frobvw) |
| 156 | + z.B2.Conjugate(api, y.B2).MulByFp(api, z.B2, ext.frobv2w) |
| 157 | + return z |
| 158 | +} |
| 159 | + |
| 160 | +// TorusCompress computes compress(x) = C1 / (1 + C0) |
| 161 | +// Input: x ∈ E12 in cyclotomic subgroup |
| 162 | +// Output: y ∈ E6 such that x = (1 + y·w) / (1 - y·w) |
| 163 | +func TorusCompress(api frontend.API, x E12) E6 { |
| 164 | + // y = C1 / (1 + C0) |
| 165 | + var one E6 |
| 166 | + one.SetOne() |
| 167 | + var c0PlusOne E6 |
| 168 | + c0PlusOne.Add(api, x.C0, one) |
| 169 | + |
| 170 | + var y E6 |
| 171 | + y.DivUnchecked(api, x.C1, c0PlusOne) |
| 172 | + |
| 173 | + return y |
| 174 | +} |
| 175 | + |
| 176 | +// CompressFrobDivideByScaling computes compress(Frob(W) / s) where W = decompress(y) |
| 177 | +// Formula: result = 2·z / (1 + s + z²·v·(1 - s)) |
| 178 | +// where z = FrobeniusTorus(y) |
| 179 | +func CompressFrobDivideByScaling(api frontend.API, y E6, s E6) E6 { |
| 180 | + // Compute z = FrobeniusTorus(y) |
| 181 | + z := FrobeniusTorus(api, y) |
| 182 | + |
| 183 | + // Compute z² |
| 184 | + var zSquare E6 |
| 185 | + zSquare.Square(api, z) |
| 186 | + |
| 187 | + // Compute z²·v (multiply by non-residue) |
| 188 | + var zSquareV E6 |
| 189 | + zSquareV.MulByNonResidue(api, zSquare) |
| 190 | + |
| 191 | + // Compute (1 - s) |
| 192 | + var one E6 |
| 193 | + one.SetOne() |
| 194 | + var oneMinusS E6 |
| 195 | + oneMinusS.Sub(api, one, s) |
| 196 | + |
| 197 | + // Compute z²·v·(1 - s) |
| 198 | + var term E6 |
| 199 | + term.Mul(api, zSquareV, oneMinusS) |
| 200 | + |
| 201 | + // Compute denominator = 1 + s + z²·v·(1 - s) |
| 202 | + var denom E6 |
| 203 | + denom.Add(api, one, s) |
| 204 | + denom.Add(api, denom, term) |
| 205 | + |
| 206 | + // Compute numerator = 2·z |
| 207 | + var num E6 |
| 208 | + num.Double(api, z) |
| 209 | + |
| 210 | + // Compute result = num / denom |
| 211 | + var result E6 |
| 212 | + result.DivUnchecked(api, num, denom) |
| 213 | + |
| 214 | + return result |
| 215 | +} |
0 commit comments