github.com/consensys/gnark-crypto@v0.14.0/field/generator/internal/templates/element/sqrt.go (about) 1 package element 2 3 const Sqrt = ` 4 5 {{ if not .UseAddChain}} 6 var ( 7 _bLegendreExponent{{.ElementName}} *big.Int 8 _bSqrtExponent{{.ElementName}} *big.Int 9 ) 10 11 func init() { 12 _bLegendreExponent{{.ElementName}}, _ = new(big.Int).SetString("{{.LegendreExponent}}", 16) 13 {{- if .SqrtQ3Mod4}} 14 const sqrtExponent{{.ElementName}} = "{{.SqrtQ3Mod4Exponent}}" 15 {{- else if .SqrtAtkin}} 16 const sqrtExponent{{.ElementName}} = "{{.SqrtAtkinExponent}}" 17 {{- else if .SqrtTonelliShanks}} 18 const sqrtExponent{{.ElementName}} = "{{.SqrtSMinusOneOver2}}" 19 {{- end }} 20 _bSqrtExponent{{.ElementName}}, _ = new(big.Int).SetString(sqrtExponent{{.ElementName}}, 16) 21 } 22 23 {{- end }} 24 25 // Legendre returns the Legendre symbol of z (either +1, -1, or 0.) 26 func (z *{{.ElementName}}) Legendre() int { 27 var l {{.ElementName}} 28 // z^((q-1)/2) 29 {{- if .UseAddChain}} 30 l.expByLegendreExp(*z) 31 {{- else}} 32 l.Exp(*z, _bLegendreExponent{{.ElementName}}) 33 {{- end}} 34 35 if l.IsZero() { 36 return 0 37 } 38 39 // if l == 1 40 if l.IsOne() { 41 return 1 42 } 43 return -1 44 } 45 46 47 // Sqrt z = √x (mod q) 48 // if the square root doesn't exist (x is not a square mod q) 49 // Sqrt leaves z unchanged and returns nil 50 func (z *{{.ElementName}}) Sqrt(x *{{.ElementName}}) *{{.ElementName}} { 51 {{- if .SqrtQ3Mod4}} 52 // q ≡ 3 (mod 4) 53 // using z ≡ ± x^((p+1)/4) (mod q) 54 var y, square {{.ElementName}} 55 {{- if .UseAddChain}} 56 y.expBySqrtExp(*x) 57 {{- else}} 58 y.Exp(*x, _bSqrtExponent{{.ElementName}}) 59 {{- end }} 60 // as we didn't compute the legendre symbol, ensure we found y such that y * y = x 61 square.Square(&y) 62 if square.Equal(x) { 63 return z.Set(&y) 64 } 65 return nil 66 {{- else if .SqrtAtkin}} 67 // q ≡ 5 (mod 8) 68 // see modSqrt5Mod8Prime in math/big/int.go 69 var one, alpha, beta, tx, square {{.ElementName}} 70 one.SetOne() 71 tx.Double(x) 72 {{- if .UseAddChain}} 73 alpha.expBySqrtExp(tx) 74 {{ else }} 75 alpha.Exp(tx, _bSqrtExponent{{.ElementName}}) 76 {{- end }} 77 beta.Square(&alpha). 78 Mul(&beta, &tx). 79 Sub(&beta, &one). 80 Mul(&beta, x). 81 Mul(&beta, &alpha) 82 83 // as we didn't compute the legendre symbol, ensure we found beta such that beta * beta = x 84 square.Square(&beta) 85 if square.Equal(x) { 86 return z.Set(&beta) 87 } 88 return nil 89 {{- else if .SqrtTonelliShanks}} 90 // q ≡ 1 (mod 4) 91 // see modSqrtTonelliShanks in math/big/int.go 92 // using https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf 93 94 var y, b,t, w {{.ElementName}} 95 // w = x^((s-1)/2)) 96 {{- if .UseAddChain}} 97 w.expBySqrtExp(*x) 98 {{- else}} 99 w.Exp(*x, _bSqrtExponent{{.ElementName}}) 100 {{- end}} 101 102 // y = x^((s+1)/2)) = w * x 103 y.Mul(x, &w) 104 105 // b = xˢ = w * w * x = y * x 106 b.Mul(&w, &y) 107 108 // g = nonResidue ^ s 109 var g = {{.ElementName}}{ 110 {{- range $i := .SqrtG}} 111 {{$i}},{{end}} 112 } 113 r := uint64({{.SqrtE}}) 114 115 // compute legendre symbol 116 // t = x^((q-1)/2) = r-1 squaring of xˢ 117 t = b 118 for i:=uint64(0); i < r-1; i++ { 119 t.Square(&t) 120 } 121 if t.IsZero() { 122 return z.SetZero() 123 } 124 if !t.IsOne() { 125 // t != 1, we don't have a square root 126 return nil 127 } 128 for { 129 var m uint64 130 t = b 131 132 // for t != 1 133 for !t.IsOne() { 134 t.Square(&t) 135 m++ 136 } 137 138 if m == 0 { 139 return z.Set(&y) 140 } 141 // t = g^(2^(r-m-1)) (mod q) 142 ge := int(r - m - 1) 143 t = g 144 for ge > 0 { 145 t.Square(&t) 146 ge-- 147 } 148 149 g.Square(&t) 150 y.Mul(&y, &t) 151 b.Mul(&b, &g) 152 r = m 153 } 154 155 {{- else}} 156 panic("not implemented") 157 {{- end}} 158 } 159 160 161 162 `