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  `