//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

package curves

import (
	"fmt"
	"io"
	"math/big"

	"golang.org/x/crypto/sha3"

	"source.quilibrium.com/quilibrium/monorepo/nekryptology/internal"
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native"
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native/bls12381"
)

var bls12381modulus = bhex("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab")

type ScalarBls12381 struct {
	Value *native.Field
	point Point
}

type PointBls12381G1 struct {
	Value *bls12381.G1
}

type PointBls12381G2 struct {
	Value *bls12381.G2
}

type ScalarBls12381Gt struct {
	Value *bls12381.Gt
}

func (s *ScalarBls12381) Random(reader io.Reader) Scalar {
	if reader == nil {
		return nil
	}
	var seed [64]byte
	_, _ = reader.Read(seed[:])
	return s.Hash(seed[:])
}

func (s *ScalarBls12381) Hash(bytes []byte) Scalar {
	dst := []byte("BLS12381_XMD:SHA-256_SSWU_RO_")
	xmd := native.ExpandMsgXmd(native.EllipticPointHasherSha256(), bytes, dst, 48)
	var t [64]byte
	copy(t[:48], internal.ReverseScalarBytes(xmd))

	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().SetBytesWide(&t),
		point: s.point,
	}
}

func (s *ScalarBls12381) Zero() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().SetZero(),
		point: s.point,
	}
}

func (s *ScalarBls12381) One() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().SetOne(),
		point: s.point,
	}
}

func (s *ScalarBls12381) IsZero() bool {
	return s.Value.IsZero() == 1
}

func (s *ScalarBls12381) IsOne() bool {
	return s.Value.IsOne() == 1
}

func (s *ScalarBls12381) IsOdd() bool {
	bytes := s.Value.Bytes()
	return bytes[0]&1 == 1
}

func (s *ScalarBls12381) IsEven() bool {
	bytes := s.Value.Bytes()
	return bytes[0]&1 == 0
}

func (s *ScalarBls12381) New(value int) Scalar {
	t := bls12381.Bls12381FqNew()
	v := big.NewInt(int64(value))
	if value < 0 {
		v.Mod(v, t.Params.BiModulus)
	}
	return &ScalarBls12381{
		Value: t.SetBigInt(v),
		point: s.point,
	}
}

func (s *ScalarBls12381) Cmp(rhs Scalar) int {
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return s.Value.Cmp(r.Value)
	} else {
		return -2
	}
}

func (s *ScalarBls12381) Square() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().Square(s.Value),
		point: s.point,
	}
}

func (s *ScalarBls12381) Double() Scalar {
	v := bls12381.Bls12381FqNew().Double(s.Value)
	return &ScalarBls12381{
		Value: v,
		point: s.point,
	}
}

func (s *ScalarBls12381) Invert() (Scalar, error) {
	value, wasInverted := bls12381.Bls12381FqNew().Invert(s.Value)
	if !wasInverted {
		return nil, fmt.Errorf("inverse doesn't exist")
	}
	return &ScalarBls12381{
		Value: value,
		point: s.point,
	}, nil
}

func (s *ScalarBls12381) Sqrt() (Scalar, error) {
	value, wasSquare := bls12381.Bls12381FqNew().Sqrt(s.Value)
	if !wasSquare {
		return nil, fmt.Errorf("not a square")
	}
	return &ScalarBls12381{
		Value: value,
		point: s.point,
	}, nil
}

func (s *ScalarBls12381) Cube() Scalar {
	value := bls12381.Bls12381FqNew().Square(s.Value)
	value.Mul(value, s.Value)
	return &ScalarBls12381{
		Value: value,
		point: s.point,
	}
}

func (s *ScalarBls12381) Add(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return &ScalarBls12381{
			Value: bls12381.Bls12381FqNew().Add(s.Value, r.Value),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381) Sub(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return &ScalarBls12381{
			Value: bls12381.Bls12381FqNew().Sub(s.Value, r.Value),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381) Mul(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return &ScalarBls12381{
			Value: bls12381.Bls12381FqNew().Mul(s.Value, r.Value),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381) MulAdd(y, z Scalar) Scalar {
	return s.Mul(y).Add(z)
}

func (s *ScalarBls12381) Div(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		v, wasInverted := bls12381.Bls12381FqNew().Invert(r.Value)
		if !wasInverted {
			return nil
		}
		v.Mul(v, s.Value)
		return &ScalarBls12381{
			Value: v,
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381) Neg() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().Neg(s.Value),
		point: s.point,
	}
}

func (s *ScalarBls12381) SetBigInt(v *big.Int) (Scalar, error) {
	if v == nil {
		return nil, fmt.Errorf("invalid value")
	}
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().SetBigInt(v),
		point: s.point,
	}, nil
}

func (s *ScalarBls12381) BigInt() *big.Int {
	return s.Value.BigInt()
}

func (s *ScalarBls12381) Bytes() []byte {
	t := s.Value.Bytes()
	return internal.ReverseScalarBytes(t[:])
}

func (s *ScalarBls12381) SetBytes(bytes []byte) (Scalar, error) {
	if len(bytes) != 32 {
		return nil, fmt.Errorf("invalid length")
	}
	var seq [32]byte
	copy(seq[:], internal.ReverseScalarBytes(bytes))
	value, err := bls12381.Bls12381FqNew().SetBytes(&seq)
	if err != nil {
		return nil, err
	}
	return &ScalarBls12381{
		value, s.point,
	}, nil
}

func (s *ScalarBls12381) SetBytesWide(bytes []byte) (Scalar, error) {
	if len(bytes) != 64 {
		return nil, fmt.Errorf("invalid length")
	}
	var seq [64]byte
	copy(seq[:], bytes)
	return &ScalarBls12381{
		bls12381.Bls12381FqNew().SetBytesWide(&seq), s.point,
	}, nil
}

func (s *ScalarBls12381) Point() Point {
	return s.point.Identity()
}

func (s *ScalarBls12381) Clone() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().Set(s.Value),
		point: s.point,
	}
}

func (s *ScalarBls12381) SetPoint(p Point) PairingScalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew().Set(s.Value),
		point: p,
	}
}

func (s *ScalarBls12381) Order() *big.Int {
	return s.Value.Params.BiModulus
}

func (s *ScalarBls12381) MarshalBinary() ([]byte, error) {
	return scalarMarshalBinary(s)
}

func (s *ScalarBls12381) UnmarshalBinary(input []byte) error {
	sc, err := scalarUnmarshalBinary(input)
	if err != nil {
		return err
	}
	ss, ok := sc.(*ScalarBls12381)
	if !ok {
		return fmt.Errorf("invalid scalar")
	}
	s.Value = ss.Value
	s.point = ss.point
	return nil
}

func (s *ScalarBls12381) MarshalText() ([]byte, error) {
	return scalarMarshalText(s)
}

func (s *ScalarBls12381) UnmarshalText(input []byte) error {
	sc, err := scalarUnmarshalText(input)
	if err != nil {
		return err
	}
	ss, ok := sc.(*ScalarBls12381)
	if !ok {
		return fmt.Errorf("invalid scalar")
	}
	s.Value = ss.Value
	s.point = ss.point
	return nil
}

func (s *ScalarBls12381) MarshalJSON() ([]byte, error) {
	return scalarMarshalJson(s)
}

func (s *ScalarBls12381) UnmarshalJSON(input []byte) error {
	sc, err := scalarUnmarshalJson(input)
	if err != nil {
		return err
	}
	S, ok := sc.(*ScalarBls12381)
	if !ok {
		return fmt.Errorf("invalid type")
	}
	s.Value = S.Value
	return nil
}

func (p *PointBls12381G1) Random(reader io.Reader) Point {
	var seed [64]byte
	_, _ = reader.Read(seed[:])
	return p.Hash(seed[:])
}

func (p *PointBls12381G1) Hash(bytes []byte) Point {
	var domain = []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_")
	pt := new(bls12381.G1).Hash(native.EllipticPointHasherSha256(), bytes, domain)
	return &PointBls12381G1{Value: pt}
}

func (p *PointBls12381G1) Identity() Point {
	return &PointBls12381G1{
		Value: new(bls12381.G1).Identity(),
	}
}

func (p *PointBls12381G1) Generator() Point {
	return &PointBls12381G1{
		Value: new(bls12381.G1).Generator(),
	}
}

func (p *PointBls12381G1) IsIdentity() bool {
	return p.Value.IsIdentity() == 1
}

func (p *PointBls12381G1) IsNegative() bool {
	// According to https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#serialization
	// This bit represents the sign of the `y` coordinate which is what we want
	return (p.Value.ToCompressed()[0]>>5)&1 == 1
}

func (p *PointBls12381G1) IsOnCurve() bool {
	return p.Value.IsOnCurve() == 1
}

func (p *PointBls12381G1) Double() Point {
	return &PointBls12381G1{new(bls12381.G1).Double(p.Value)}
}

func (p *PointBls12381G1) Scalar() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew(),
		point: new(PointBls12381G1),
	}
}

func (p *PointBls12381G1) Neg() Point {
	return &PointBls12381G1{new(bls12381.G1).Neg(p.Value)}
}

func (p *PointBls12381G1) Add(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12381G1)
	if ok {
		return &PointBls12381G1{new(bls12381.G1).Add(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G1) Sub(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12381G1)
	if ok {
		return &PointBls12381G1{new(bls12381.G1).Sub(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G1) Mul(rhs Scalar) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return &PointBls12381G1{new(bls12381.G1).Mul(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G1) Equal(rhs Point) bool {
	r, ok := rhs.(*PointBls12381G1)
	if ok {
		return p.Value.Equal(r.Value) == 1
	} else {
		return false
	}
}

func (p *PointBls12381G1) Set(x, y *big.Int) (Point, error) {
	value, err := new(bls12381.G1).SetBigInt(x, y)
	if err != nil {
		return nil, fmt.Errorf("invalid coordinates")
	}
	return &PointBls12381G1{value}, nil
}

func (p *PointBls12381G1) ToAffineCompressed() []byte {
	out := p.Value.ToCompressed()
	return out[:]
}

func (p *PointBls12381G1) ToAffineUncompressed() []byte {
	out := p.Value.ToUncompressed()
	return out[:]
}

func (p *PointBls12381G1) FromAffineCompressed(bytes []byte) (Point, error) {
	var b [bls12381.FieldBytes]byte
	copy(b[:], bytes)
	value, err := new(bls12381.G1).FromCompressed(&b)
	if err != nil {
		return nil, err
	}
	return &PointBls12381G1{value}, nil
}

func (p *PointBls12381G1) FromAffineUncompressed(bytes []byte) (Point, error) {
	var b [96]byte
	copy(b[:], bytes)
	value, err := new(bls12381.G1).FromUncompressed(&b)
	if err != nil {
		return nil, err
	}
	return &PointBls12381G1{value}, nil
}

func (p *PointBls12381G1) CurveName() string {
	return "BLS12381G1"
}

func (p *PointBls12381G1) SumOfProducts(points []Point, scalars []Scalar) Point {
	nPoints := make([]*bls12381.G1, len(points))
	nScalars := make([]*native.Field, len(scalars))
	for i, pt := range points {
		pp, ok := pt.(*PointBls12381G1)
		if !ok {
			return nil
		}
		nPoints[i] = pp.Value
	}
	for i, sc := range scalars {
		s, ok := sc.(*ScalarBls12381)
		if !ok {
			return nil
		}
		nScalars[i] = s.Value
	}
	value, err := new(bls12381.G1).SumOfProducts(nPoints, nScalars)
	if err != nil {
		return nil
	}
	return &PointBls12381G1{value}
}

func (p *PointBls12381G1) OtherGroup() PairingPoint {
	return new(PointBls12381G2).Identity().(PairingPoint)
}

func (p *PointBls12381G1) Pairing(rhs PairingPoint) Scalar {
	pt, ok := rhs.(*PointBls12381G2)
	if !ok {
		return nil
	}
	e := new(bls12381.Engine)
	e.AddPair(p.Value, pt.Value)

	value := e.Result()

	return &ScalarBls12381Gt{value}
}

func (p *PointBls12381G1) MultiPairing(points ...PairingPoint) Scalar {
	return multiPairing(points...)
}

func (p *PointBls12381G1) X() *big.Int {
	return p.Value.GetX().BigInt()
}

func (p *PointBls12381G1) Y() *big.Int {
	return p.Value.GetY().BigInt()
}

func (p *PointBls12381G1) Modulus() *big.Int {
	return bls12381modulus
}

func (p *PointBls12381G1) MarshalBinary() ([]byte, error) {
	return pointMarshalBinary(p)
}

func (p *PointBls12381G1) UnmarshalBinary(input []byte) error {
	pt, err := pointUnmarshalBinary(input)
	if err != nil {
		return err
	}
	ppt, ok := pt.(*PointBls12381G1)
	if !ok {
		return fmt.Errorf("invalid point")
	}
	p.Value = ppt.Value
	return nil
}

func (p *PointBls12381G1) MarshalText() ([]byte, error) {
	return pointMarshalText(p)
}

func (p *PointBls12381G1) UnmarshalText(input []byte) error {
	pt, err := pointUnmarshalText(input)
	if err != nil {
		return err
	}
	ppt, ok := pt.(*PointBls12381G1)
	if !ok {
		return fmt.Errorf("invalid point")
	}
	p.Value = ppt.Value
	return nil
}

func (p *PointBls12381G1) MarshalJSON() ([]byte, error) {
	return pointMarshalJson(p)
}

func (p *PointBls12381G1) UnmarshalJSON(input []byte) error {
	pt, err := pointUnmarshalJson(input)
	if err != nil {
		return err
	}
	P, ok := pt.(*PointBls12381G1)
	if !ok {
		return fmt.Errorf("invalid type")
	}
	p.Value = P.Value
	return nil
}

func (p *PointBls12381G2) Random(reader io.Reader) Point {
	var seed [64]byte
	_, _ = reader.Read(seed[:])
	return p.Hash(seed[:])
}

func (p *PointBls12381G2) Hash(bytes []byte) Point {
	var domain = []byte("BLS12381G2_XMD:SHA-256_SSWU_RO_")
	pt := new(bls12381.G2).Hash(native.EllipticPointHasherSha256(), bytes, domain)
	return &PointBls12381G2{Value: pt}
}

func (p *PointBls12381G2) Identity() Point {
	return &PointBls12381G2{
		Value: new(bls12381.G2).Identity(),
	}
}

func (p *PointBls12381G2) Generator() Point {
	return &PointBls12381G2{
		Value: new(bls12381.G2).Generator(),
	}
}

func (p *PointBls12381G2) IsIdentity() bool {
	return p.Value.IsIdentity() == 1
}

func (p *PointBls12381G2) IsNegative() bool {
	// According to https://github.com/zcash/librustzcash/blob/6e0364cd42a2b3d2b958a54771ef51a8db79dd29/pairing/src/bls12_381/README.md#serialization
	// This bit represents the sign of the `y` coordinate which is what we want
	return (p.Value.ToCompressed()[0]>>5)&1 == 1
}

func (p *PointBls12381G2) IsOnCurve() bool {
	return p.Value.IsOnCurve() == 1
}

func (p *PointBls12381G2) Double() Point {
	return &PointBls12381G2{new(bls12381.G2).Double(p.Value)}
}

func (p *PointBls12381G2) Scalar() Scalar {
	return &ScalarBls12381{
		Value: bls12381.Bls12381FqNew(),
		point: new(PointBls12381G2),
	}
}

func (p *PointBls12381G2) Neg() Point {
	return &PointBls12381G2{new(bls12381.G2).Neg(p.Value)}
}

func (p *PointBls12381G2) Add(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12381G2)
	if ok {
		return &PointBls12381G2{new(bls12381.G2).Add(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G2) Sub(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12381G2)
	if ok {
		return &PointBls12381G2{new(bls12381.G2).Sub(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G2) Mul(rhs Scalar) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*ScalarBls12381)
	if ok {
		return &PointBls12381G2{new(bls12381.G2).Mul(p.Value, r.Value)}
	} else {
		return nil
	}
}

func (p *PointBls12381G2) Equal(rhs Point) bool {
	r, ok := rhs.(*PointBls12381G2)
	if ok {
		return p.Value.Equal(r.Value) == 1
	} else {
		return false
	}
}

func (p *PointBls12381G2) Set(x, y *big.Int) (Point, error) {
	value, err := new(bls12381.G2).SetBigInt(x, y)
	if err != nil {
		return nil, fmt.Errorf("invalid coordinates")
	}
	return &PointBls12381G2{value}, nil
}

func (p *PointBls12381G2) ToAffineCompressed() []byte {
	out := p.Value.ToCompressed()
	return out[:]
}

func (p *PointBls12381G2) ToAffineUncompressed() []byte {
	out := p.Value.ToUncompressed()
	return out[:]
}

func (p *PointBls12381G2) FromAffineCompressed(bytes []byte) (Point, error) {
	var b [bls12381.WideFieldBytes]byte
	copy(b[:], bytes)
	value, err := new(bls12381.G2).FromCompressed(&b)
	if err != nil {
		return nil, err
	}
	return &PointBls12381G2{value}, nil
}

func (p *PointBls12381G2) FromAffineUncompressed(bytes []byte) (Point, error) {
	var b [bls12381.DoubleWideFieldBytes]byte
	copy(b[:], bytes)
	value, err := new(bls12381.G2).FromUncompressed(&b)
	if err != nil {
		return nil, err
	}
	return &PointBls12381G2{value}, nil
}

func (p *PointBls12381G2) CurveName() string {
	return "BLS12381G2"
}

func (p *PointBls12381G2) SumOfProducts(points []Point, scalars []Scalar) Point {
	nPoints := make([]*bls12381.G2, len(points))
	nScalars := make([]*native.Field, len(scalars))
	for i, pt := range points {
		pp, ok := pt.(*PointBls12381G2)
		if !ok {
			return nil
		}
		nPoints[i] = pp.Value
	}
	for i, sc := range scalars {
		s, ok := sc.(*ScalarBls12381)
		if !ok {
			return nil
		}
		nScalars[i] = s.Value
	}
	value, err := new(bls12381.G2).SumOfProducts(nPoints, nScalars)
	if err != nil {
		return nil
	}
	return &PointBls12381G2{value}
}

func (p *PointBls12381G2) OtherGroup() PairingPoint {
	return new(PointBls12381G1).Identity().(PairingPoint)
}

func (p *PointBls12381G2) Pairing(rhs PairingPoint) Scalar {
	pt, ok := rhs.(*PointBls12381G1)
	if !ok {
		return nil
	}
	e := new(bls12381.Engine)
	e.AddPair(pt.Value, p.Value)

	value := e.Result()

	return &ScalarBls12381Gt{value}
}

func (p *PointBls12381G2) MultiPairing(points ...PairingPoint) Scalar {
	return multiPairing(points...)
}

func (p *PointBls12381G2) X() *big.Int {
	x := p.Value.ToUncompressed()
	return new(big.Int).SetBytes(x[:bls12381.WideFieldBytes])
}

func (p *PointBls12381G2) Y() *big.Int {
	y := p.Value.ToUncompressed()
	return new(big.Int).SetBytes(y[bls12381.WideFieldBytes:])
}

func (p *PointBls12381G2) Modulus() *big.Int {
	return bls12381modulus
}

func (p *PointBls12381G2) MarshalBinary() ([]byte, error) {
	return pointMarshalBinary(p)
}

func (p *PointBls12381G2) UnmarshalBinary(input []byte) error {
	pt, err := pointUnmarshalBinary(input)
	if err != nil {
		return err
	}
	ppt, ok := pt.(*PointBls12381G2)
	if !ok {
		return fmt.Errorf("invalid point")
	}
	p.Value = ppt.Value
	return nil
}

func (p *PointBls12381G2) MarshalText() ([]byte, error) {
	return pointMarshalText(p)
}

func (p *PointBls12381G2) UnmarshalText(input []byte) error {
	pt, err := pointUnmarshalText(input)
	if err != nil {
		return err
	}
	ppt, ok := pt.(*PointBls12381G2)
	if !ok {
		return fmt.Errorf("invalid point")
	}
	p.Value = ppt.Value
	return nil
}

func (p *PointBls12381G2) MarshalJSON() ([]byte, error) {
	return pointMarshalJson(p)
}

func (p *PointBls12381G2) UnmarshalJSON(input []byte) error {
	pt, err := pointUnmarshalJson(input)
	if err != nil {
		return err
	}
	P, ok := pt.(*PointBls12381G2)
	if !ok {
		return fmt.Errorf("invalid type")
	}
	p.Value = P.Value
	return nil
}

func multiPairing(points ...PairingPoint) Scalar {
	if len(points)%2 != 0 {
		return nil
	}
	valid := true
	eng := new(bls12381.Engine)
	for i := 0; i < len(points); i += 2 {
		pt1, ok := points[i].(*PointBls12381G1)
		valid = valid && ok
		pt2, ok := points[i+1].(*PointBls12381G2)
		valid = valid && ok
		if valid {
			eng.AddPair(pt1.Value, pt2.Value)
		}
	}
	if !valid {
		return nil
	}

	value := eng.Result()
	return &ScalarBls12381Gt{value}
}

func (s *ScalarBls12381Gt) Random(reader io.Reader) Scalar {
	value, err := new(bls12381.Gt).Random(reader)
	if err != nil {
		return nil
	}
	return &ScalarBls12381Gt{value}
}

func (s *ScalarBls12381Gt) Hash(bytes []byte) Scalar {
	reader := sha3.NewShake256()
	n, err := reader.Write(bytes)
	if err != nil {
		return nil
	}
	if n != len(bytes) {
		return nil
	}
	return s.Random(reader)
}

func (s *ScalarBls12381Gt) Zero() Scalar {
	return &ScalarBls12381Gt{new(bls12381.Gt)}
}

func (s *ScalarBls12381Gt) One() Scalar {
	return &ScalarBls12381Gt{new(bls12381.Gt).SetOne()}
}

func (s *ScalarBls12381Gt) IsZero() bool {
	return s.Value.IsZero() == 1
}

func (s *ScalarBls12381Gt) IsOne() bool {
	return s.Value.IsOne() == 1
}

func (s *ScalarBls12381Gt) MarshalBinary() ([]byte, error) {
	return scalarMarshalBinary(s)
}

func (s *ScalarBls12381Gt) UnmarshalBinary(input []byte) error {
	sc, err := scalarUnmarshalBinary(input)
	if err != nil {
		return err
	}
	ss, ok := sc.(*ScalarBls12381Gt)
	if !ok {
		return fmt.Errorf("invalid scalar")
	}
	s.Value = ss.Value
	return nil
}

func (s *ScalarBls12381Gt) MarshalText() ([]byte, error) {
	return scalarMarshalText(s)
}

func (s *ScalarBls12381Gt) UnmarshalText(input []byte) error {
	sc, err := scalarUnmarshalText(input)
	if err != nil {
		return err
	}
	ss, ok := sc.(*ScalarBls12381Gt)
	if !ok {
		return fmt.Errorf("invalid scalar")
	}
	s.Value = ss.Value
	return nil
}

func (s *ScalarBls12381Gt) MarshalJSON() ([]byte, error) {
	return scalarMarshalJson(s)
}

func (s *ScalarBls12381Gt) UnmarshalJSON(input []byte) error {
	sc, err := scalarUnmarshalJson(input)
	if err != nil {
		return err
	}
	S, ok := sc.(*ScalarBls12381Gt)
	if !ok {
		return fmt.Errorf("invalid type")
	}
	s.Value = S.Value
	return nil
}

func (s *ScalarBls12381Gt) IsOdd() bool {
	data := s.Value.Bytes()
	return data[0]&1 == 1
}

func (s *ScalarBls12381Gt) IsEven() bool {
	data := s.Value.Bytes()
	return data[0]&1 == 0
}

func (s *ScalarBls12381Gt) New(input int) Scalar {
	var data [bls12381.GtFieldBytes]byte
	data[3] = byte(input >> 24 & 0xFF)
	data[2] = byte(input >> 16 & 0xFF)
	data[1] = byte(input >> 8 & 0xFF)
	data[0] = byte(input & 0xFF)

	value, isCanonical := new(bls12381.Gt).SetBytes(&data)
	if isCanonical != 1 {
		return nil
	}
	return &ScalarBls12381Gt{value}
}

func (s *ScalarBls12381Gt) Cmp(rhs Scalar) int {
	r, ok := rhs.(*ScalarBls12381Gt)
	if ok && s.Value.Equal(r.Value) == 1 {
		return 0
	} else {
		return -2
	}
}

func (s *ScalarBls12381Gt) Square() Scalar {
	return &ScalarBls12381Gt{
		new(bls12381.Gt).Square(s.Value),
	}
}

func (s *ScalarBls12381Gt) Double() Scalar {
	return &ScalarBls12381Gt{
		new(bls12381.Gt).Double(s.Value),
	}
}

func (s *ScalarBls12381Gt) Invert() (Scalar, error) {
	value, wasInverted := new(bls12381.Gt).Invert(s.Value)
	if wasInverted != 1 {
		return nil, fmt.Errorf("not invertible")
	}
	return &ScalarBls12381Gt{
		value,
	}, nil
}

func (s *ScalarBls12381Gt) Sqrt() (Scalar, error) {
	// Not implemented
	return nil, nil
}

func (s *ScalarBls12381Gt) Cube() Scalar {
	value := new(bls12381.Gt).Square(s.Value)
	value.Add(value, s.Value)
	return &ScalarBls12381Gt{
		value,
	}
}

func (s *ScalarBls12381Gt) Add(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381Gt)
	if ok {
		return &ScalarBls12381Gt{
			new(bls12381.Gt).Add(s.Value, r.Value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381Gt) Sub(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381Gt)
	if ok {
		return &ScalarBls12381Gt{
			new(bls12381.Gt).Sub(s.Value, r.Value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381Gt) Mul(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381Gt)
	if ok {
		return &ScalarBls12381Gt{
			new(bls12381.Gt).Add(s.Value, r.Value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381Gt) MulAdd(y, z Scalar) Scalar {
	return s.Mul(y).Add(z)
}

func (s *ScalarBls12381Gt) Div(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12381Gt)
	if ok {
		return &ScalarBls12381Gt{
			new(bls12381.Gt).Sub(s.Value, r.Value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12381Gt) Neg() Scalar {
	return &ScalarBls12381Gt{
		new(bls12381.Gt).Neg(s.Value),
	}
}

func (s *ScalarBls12381Gt) SetBigInt(v *big.Int) (Scalar, error) {
	var bytes [bls12381.GtFieldBytes]byte
	v.FillBytes(bytes[:])
	return s.SetBytes(bytes[:])
}

func (s *ScalarBls12381Gt) BigInt() *big.Int {
	bytes := s.Value.Bytes()
	return new(big.Int).SetBytes(bytes[:])
}

func (s *ScalarBls12381Gt) Point() Point {
	return &PointBls12381G1{Value: new(bls12381.G1).Identity()}
}

func (s *ScalarBls12381Gt) Bytes() []byte {
	bytes := s.Value.Bytes()
	return bytes[:]
}

func (s *ScalarBls12381Gt) SetBytes(bytes []byte) (Scalar, error) {
	var b [bls12381.GtFieldBytes]byte
	copy(b[:], bytes)
	ss, isCanonical := new(bls12381.Gt).SetBytes(&b)
	if isCanonical == 0 {
		return nil, fmt.Errorf("invalid bytes")
	}
	return &ScalarBls12381Gt{ss}, nil
}

func (s *ScalarBls12381Gt) SetBytesWide(bytes []byte) (Scalar, error) {
	l := len(bytes)
	if l != bls12381.GtFieldBytes*2 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	var b [bls12381.GtFieldBytes]byte
	copy(b[:], bytes[:bls12381.GtFieldBytes])

	value, isCanonical := new(bls12381.Gt).SetBytes(&b)
	if isCanonical == 0 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	copy(b[:], bytes[bls12381.GtFieldBytes:])
	value2, isCanonical := new(bls12381.Gt).SetBytes(&b)
	if isCanonical == 0 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	value.Add(value, value2)
	return &ScalarBls12381Gt{value}, nil
}

func (s *ScalarBls12381Gt) Clone() Scalar {
	return &ScalarBls12381Gt{
		Value: new(bls12381.Gt).Set(s.Value),
	}
}
