// +build ignore
// The previous line (and this one up to the warning below) is removed by the
// template generator.

// Code generated from ./templates/field.go.tmpl. DO NOT EDIT.

package {{.Name | ToLower}}

import (
	"bytes"
	"crypto/subtle"
	"encoding/binary"
	"errors"
	"io"

	"github.com/cloudflare/circl/internal/conv"
	"github.com/cloudflare/circl/internal/sha3"
	"golang.org/x/crypto/cryptobyte"
)

// Size is the length in bytes of an {{.Name}} element.
const Size = {{.NumUint8}}

// Fp represents a prime field element as a positive integer less than Order.
type Fp [{{.NumUint64}}]uint64

func (z Fp) String() string                  { x := z.fromMont(); return conv.Uint64Le2Hex(x[:]) }
func (z Fp) Size() uint                      { return Size }
func (z Fp) OrderRootUnity() uint            { return numRootsUnity }
func (z *Fp) AddAssign(x *Fp)                { fiatFpAdd(z, z, x) }
func (z *Fp) SubAssign(x *Fp)                { fiatFpSub(z, z, x) }
func (z *Fp) MulAssign(x *Fp)                { fiatFpMul(z, z, x) }
func (z *Fp) Add(x, y *Fp)                   { fiatFpAdd(z, x, y) }
func (z *Fp) Sub(x, y *Fp)                   { fiatFpSub(z, x, y) }
func (z *Fp) Mul(x, y *Fp)                   { fiatFpMul(z, x, y) }
func (z *Fp) Sqr(x *Fp)                      { fiatFpSquare(z, x) }
func (z *Fp) IsZero() bool                   { return ctEqual(z, &Fp{}) }
func (z *Fp) IsOne() bool                    { return ctEqual(z, &rootOfUnityTwoN[0]) }
func (z *Fp) IsEqual(x *Fp) bool             { return ctEqual(z, x) }
func (z *Fp) SetOne()                        { *z = rootOfUnityTwoN[0] }
func (z *Fp) toMont()                        { fiatFpMul(z, z, &rSquare) }
func (z *Fp) fromMont() (out Fp)             { fiatFpMul(&out, z, &Fp{1}); return }
func (z *Fp) MarshalBinary() ([]byte, error) { return conv.MarshalBinaryLen(z, Size) }
func (z *Fp) UnmarshalBinary(b []byte) error { return conv.UnmarshalBinary(z, b) }
func (z *Fp) Marshal(b *cryptobyte.Builder) error {
	var x [Size]byte
	for i, zi := range z.fromMont() {
		binary.LittleEndian.PutUint64(x[8*i:], zi)
	}
	b.AddBytes(x[:])
	return nil
}

func (z* Fp) Unmarshal(s *cryptobyte.String) bool{
	var b [Size]byte
	if s.CopyBytes(b[:]) {
		n, ok := isInRange(&b)
		if ok {
			*z = n
			z.toMont()
			return true
		}
	}
	return false
}

func (z* Fp) Random(r io.Reader) error {
	var b [Size]byte
	var ok bool
	for range maxNumTries {
		_, err := r.Read(b[:])
		if err != nil {
			return err
		}

		*z, ok = isInRange(&b)
		if ok {
			z.toMont()
			return nil
		}
	}

	return ErrMaxNumTries
}

func (z* Fp) RandomSHA3(s *sha3.State) error {
	var b [Size]byte
	var ok bool
	for range maxNumTries {
		_, err := s.Read(b[:])
		if err != nil {
			return err
		}

		*z, ok = isInRange(&b)
		if ok {
			z.toMont()
			return nil
		}
	}

	return ErrMaxNumTries
}

func (z* Fp) InvUint64(x uint64) {
	if 0 < x && x <= numInverseInt {
		*z = inverseInt[x-1]
	} else {
		err := z.SetUint64(x)
		if err != nil {
			panic(ErrFieldEltDecode)
		}
		z.Inv(z)
	}
}

func (z* Fp) InvTwoN(n uint) {
	z.SetOne()
	for range n {
		z.Mul(z, &half)
	}
}

func (z* Fp) SetUint64(n uint64) error {
	{{- if eq .Bits 64 }}
	if n >= {{.OrderVar}} {
		return ErrFieldEltDecode
	}
	{{- end }}
	*z = Fp{n}
	z.toMont()
	return nil
}

func (z* Fp) GetUint64() (uint64, error) {
	x := z.fromMont();
	{{- if eq .Bits 128 }}
	if x[1] != 0 {
		return 0, ErrNumberTooLarge
	}
	{{- end }}
	return x[0], nil
}

func (z* Fp) SetRootOfUnityTwoN(n uint) {
	if n > numRootsUnity {
		panic(ErrRootsOfUnity)
	}
	*z = rootOfUnityTwoN[n]
}


func (z Fp) Order() []byte {
	var x [Size]byte
	binary.Write(bytes.NewBuffer(x[:0]), binary.BigEndian, []uint64{ {{.OrderVar}} })
	return x[:]
}

func (z* Fp) sqri(x *Fp, n uint) {
	z.Sqr(x)
	for range n - 1 {
		z.Sqr(z)
	}
}

func fiatFpCmovznzU64(z *uint64, b, x, y uint64) { *z = (x &^ (-b)) | (y & (-b)) }

func ctEqual(x, y *Fp) bool {
	var v uint64
	for i := 0; i < len(*x); i++ {
		v |= (*x)[i] ^ (*y)[i]
	}
	v32 := uint32(v>>32) | uint32(v)
	return subtle.ConstantTimeEq(int32(v32), 0) == 1
}


const (
	// order is the order of the {{.Name}} field.
	{{.OrderConst -}}
	// numRootsUnity is ..
	numRootsUnity = {{.NumRootsUnity}}
	// numInverseInt{{.Name}} is the number of precomputed inverses.
	numInverseInt = {{.NumInverseInt}}
	// maxNumTries is the maximum tries for rejection sampling.
	maxNumTries = 10
)

var (
	// rSquare is R^2 mod {{.Name}}Order, where R=2^{{.Bits}} (little-endian).
	rSquare = Fp{ {{.RSquare}} }
	// half is 1/2 mod Order.
	half = Fp{ {{.Half}} }
	// rootOfUnityTwoN are the (principal) roots of unity that generate
	// a multiplicative group of order 2^n.
	// i.e., rootOfUnityTwoN[i] generates a group of order 2^i.
	// Thus, by definition,
	// - rootOfUnityTwoN[0] = One
	// - rootOfUnityTwoN[numRoots] = Generator
	// Constants are encoded in Montgomery domain (little-endian).
	rootOfUnityTwoN = [numRootsUnity + 1]Fp{
		{{.RootsOfUnity}}
	}
	// inverseInt has the inverse of the first `numInverseInt` integers.
	inverseInt = [numInverseInt]Fp{
		{{.InverseInt}}
	}
)


var (
	ErrMatchLen       = errors.New("inputs mismatched length")
	ErrFieldEltDecode = errors.New("incorrect field element value")
	ErrNumberTooLarge = errors.New("number of bits is not enough to represent the number")
	ErrMaxNumTries    = errors.New("random rejection sampling reached maximum number of tries")
	ErrRootsOfUnity   = errors.New("Fp has no roots of unity of order larger than 2^{{.NumRootsUnity}}")
)
