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

// NOTE that the bls curves are NOT constant time. There is an open issue to address it: https://source.quilibrium.com/quilibrium/monorepo/nekryptology/issues/233

package curves

import (
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"fmt"
	"io"
	"math/big"

	bls12377 "github.com/consensys/gnark-crypto/ecc/bls12-377"
	"golang.org/x/crypto/sha3"

	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core"
)

// See 'r' = https://eprint.iacr.org/2018/962.pdf Figure 16
var bls12377modulus = bhex("12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001")
var g1Inf = bls12377.G1Affine{X: [6]uint64{}, Y: [6]uint64{}}
var g2Inf = bls12377.G2Affine{
	X: bls12377.E2{A0: [6]uint64{}, A1: [6]uint64{}},
	Y: bls12377.E2{A0: [6]uint64{}, A1: [6]uint64{}},
}

type ScalarBls12377 struct {
	value *big.Int
	point Point
}

type PointBls12377G1 struct {
	value *bls12377.G1Affine
}

type PointBls12377G2 struct {
	value *bls12377.G2Affine
}

type ScalarBls12377Gt struct {
	value *bls12377.GT
}

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

func (s *ScalarBls12377) Hash(bytes []byte) Scalar {
	xmd, err := expandMsgXmd(sha256.New(), bytes, []byte("BLS12377_XMD:SHA-256_SSWU_RO_"), 48)
	if err != nil {
		return nil
	}
	v := new(big.Int).SetBytes(xmd)
	return &ScalarBls12377{
		value: v.Mod(v, bls12377modulus),
		point: s.point,
	}
}

func (s *ScalarBls12377) Zero() Scalar {
	return &ScalarBls12377{
		value: big.NewInt(0),
		point: s.point,
	}
}

func (s *ScalarBls12377) One() Scalar {
	return &ScalarBls12377{
		value: big.NewInt(1),
		point: s.point,
	}
}

func (s *ScalarBls12377) IsZero() bool {
	return subtle.ConstantTimeCompare(s.value.Bytes(), []byte{}) == 1
}

func (s *ScalarBls12377) IsOne() bool {
	return subtle.ConstantTimeCompare(s.value.Bytes(), []byte{1}) == 1
}

func (s *ScalarBls12377) IsOdd() bool {
	return s.value.Bit(0) == 1
}

func (s *ScalarBls12377) IsEven() bool {
	return s.value.Bit(0) == 0
}

func (s *ScalarBls12377) New(value int) Scalar {
	v := big.NewInt(int64(value))
	if value < 0 {
		v.Mod(v, bls12377modulus)
	}
	return &ScalarBls12377{
		value: v,
		point: s.point,
	}
}

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

func (s *ScalarBls12377) Square() Scalar {
	return &ScalarBls12377{
		value: new(big.Int).Exp(s.value, big.NewInt(2), bls12377modulus),
		point: s.point,
	}
}

func (s *ScalarBls12377) Double() Scalar {
	v := new(big.Int).Add(s.value, s.value)
	return &ScalarBls12377{
		value: v.Mod(v, bls12377modulus),
		point: s.point,
	}
}

func (s *ScalarBls12377) Invert() (Scalar, error) {
	return &ScalarBls12377{
		value: new(big.Int).ModInverse(s.value, bls12377modulus),
		point: s.point,
	}, nil
}

func (s *ScalarBls12377) Sqrt() (Scalar, error) {
	return &ScalarBls12377{
		value: new(big.Int).ModSqrt(s.value, bls12377modulus),
		point: s.point,
	}, nil
}

func (s *ScalarBls12377) Cube() Scalar {
	return &ScalarBls12377{
		value: new(big.Int).Exp(s.value, big.NewInt(3), bls12377modulus),
		point: s.point,
	}
}

func (s *ScalarBls12377) Add(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		v := new(big.Int).Add(s.value, r.value)
		return &ScalarBls12377{
			value: v.Mod(v, bls12377modulus),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377) Sub(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		v := new(big.Int).Sub(s.value, r.value)
		return &ScalarBls12377{
			value: v.Mod(v, bls12377modulus),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377) Mul(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		v := new(big.Int).Mul(s.value, r.value)
		return &ScalarBls12377{
			value: v.Mod(v, bls12377modulus),
			point: s.point,
		}
	} else {
		return nil
	}
}

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

func (s *ScalarBls12377) Div(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		v := new(big.Int).ModInverse(r.value, bls12377modulus)
		v.Mul(v, s.value)
		return &ScalarBls12377{
			value: v.Mod(v, bls12377modulus),
			point: s.point,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377) Neg() Scalar {
	z := new(big.Int).Neg(s.value)
	return &ScalarBls12377{
		value: z.Mod(z, bls12377modulus),
		point: s.point,
	}
}

func (s *ScalarBls12377) SetBigInt(v *big.Int) (Scalar, error) {
	if v == nil {
		return nil, fmt.Errorf("invalid value")
	}
	t := new(big.Int).Mod(v, bls12377modulus)
	if t.Cmp(v) != 0 {
		return nil, fmt.Errorf("invalid value")
	}
	return &ScalarBls12377{
		value: t,
		point: s.point,
	}, nil
}

func (s *ScalarBls12377) BigInt() *big.Int {
	return new(big.Int).Set(s.value)
}

func (s *ScalarBls12377) Bytes() []byte {
	var out [32]byte
	return s.value.FillBytes(out[:])
}

func (s *ScalarBls12377) SetBytes(bytes []byte) (Scalar, error) {
	value := new(big.Int).SetBytes(bytes)
	t := new(big.Int).Mod(value, bls12377modulus)
	if t.Cmp(value) != 0 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	return &ScalarBls12377{
		value: t,
		point: s.point,
	}, nil
}

func (s *ScalarBls12377) SetBytesWide(bytes []byte) (Scalar, error) {
	if len(bytes) < 32 || len(bytes) > 128 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	value := new(big.Int).SetBytes(bytes)
	t := new(big.Int).Mod(value, bls12377modulus)
	return &ScalarBls12377{
		value: t,
		point: s.point,
	}, nil
}

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

func (s *ScalarBls12377) Clone() Scalar {
	return &ScalarBls12377{
		value: new(big.Int).Set(s.value),
		point: s.point,
	}
}

func (s *ScalarBls12377) SetPoint(p Point) PairingScalar {
	return &ScalarBls12377{
		value: new(big.Int).Set(s.value),
		point: p,
	}
}

func (s *ScalarBls12377) Order() *big.Int {
	return bls12377modulus
}

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

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

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

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

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

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

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

func (p *PointBls12377G1) Hash(bytes []byte) Point {
	var domain = []byte("BLS12377G1_XMD:SHA-256_SVDW_RO_")
	pt, err := bls12377.HashToCurveG1Svdw(bytes, domain)
	if err != nil {
		return nil
	}
	return &PointBls12377G1{value: &pt}
}

func (p *PointBls12377G1) Identity() Point {
	t := bls12377.G1Affine{}
	return &PointBls12377G1{
		value: t.Set(&g1Inf),
	}
}

func (p *PointBls12377G1) Generator() Point {
	t := bls12377.G1Affine{}
	_, _, g1Aff, _ := bls12377.Generators()
	return &PointBls12377G1{
		value: t.Set(&g1Aff),
	}
}

func (p *PointBls12377G1) IsIdentity() bool {
	return p.value.IsInfinity()
}

func (p *PointBls12377G1) 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.Bytes()[0]>>5)&1 == 1
}

func (p *PointBls12377G1) IsOnCurve() bool {
	return p.value.IsOnCurve()
}

func (p *PointBls12377G1) Double() Point {
	t := &bls12377.G1Jac{}
	t.FromAffine(p.value)
	t.DoubleAssign()
	value := bls12377.G1Affine{}
	return &PointBls12377G1{value.FromJacobian(t)}
}

func (p *PointBls12377G1) Scalar() Scalar {
	return &ScalarBls12377{
		value: new(big.Int),
		point: new(PointBls12377G1),
	}
}

func (p *PointBls12377G1) Neg() Point {
	value := &bls12377.G1Affine{}
	value.Neg(p.value)
	return &PointBls12377G1{value}
}

func (p *PointBls12377G1) Add(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12377G1)
	if ok {
		value := &bls12377.G1Affine{}
		return &PointBls12377G1{value.Add(p.value, r.value)}
	} else {
		return nil
	}
}

func (p *PointBls12377G1) Sub(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12377G1)
	if ok {
		value := &bls12377.G1Affine{}
		return &PointBls12377G1{value.Sub(p.value, r.value)}
	} else {
		return nil
	}
}

func (p *PointBls12377G1) Mul(rhs Scalar) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		value := &bls12377.G1Affine{}
		return &PointBls12377G1{value.ScalarMultiplication(p.value, r.value)}
	} else {
		return nil
	}
}

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

func (p *PointBls12377G1) Set(x, y *big.Int) (Point, error) {
	if x.Cmp(core.Zero) == 0 &&
		y.Cmp(core.Zero) == 0 {
		return p.Identity(), nil
	}
	var data [96]byte
	x.FillBytes(data[:48])
	y.FillBytes(data[48:])
	value := &bls12377.G1Affine{}
	_, err := value.SetBytes(data[:])
	if err != nil {
		return nil, fmt.Errorf("invalid coordinates")
	}
	return &PointBls12377G1{value}, nil
}

func (p *PointBls12377G1) ToAffineCompressed() []byte {
	v := p.value.Bytes()
	return v[:]
}

func (p *PointBls12377G1) ToAffineUncompressed() []byte {
	v := p.value.RawBytes()
	return v[:]
}

func (p *PointBls12377G1) FromAffineCompressed(bytes []byte) (Point, error) {
	if len(bytes) != bls12377.SizeOfG1AffineCompressed {
		return nil, fmt.Errorf("invalid point")
	}
	value := &bls12377.G1Affine{}
	_, err := value.SetBytes(bytes)
	if err != nil {
		return nil, err
	}
	return &PointBls12377G1{value}, nil
}

func (p *PointBls12377G1) FromAffineUncompressed(bytes []byte) (Point, error) {
	if len(bytes) != bls12377.SizeOfG1AffineUncompressed {
		return nil, fmt.Errorf("invalid point")
	}
	value := &bls12377.G1Affine{}
	_, err := value.SetBytes(bytes)
	if err != nil {
		return nil, err
	}
	return &PointBls12377G1{value}, nil
}

func (p *PointBls12377G1) CurveName() string {
	return "BLS12377G1"
}

func (p *PointBls12377G1) SumOfProducts(points []Point, scalars []Scalar) Point {
	nScalars := make([]*big.Int, len(scalars))
	for i, sc := range scalars {
		s, ok := sc.(*ScalarBls12377)
		if !ok {
			return nil
		}
		nScalars[i] = s.value
	}
	return sumOfProductsPippenger(points, nScalars)
}

func (p *PointBls12377G1) OtherGroup() PairingPoint {
	return new(PointBls12377G2).Identity().(PairingPoint)
}

func (p *PointBls12377G1) Pairing(rhs PairingPoint) Scalar {
	pt, ok := rhs.(*PointBls12377G2)
	if !ok {
		return nil
	}
	if !p.value.IsInSubGroup() ||
		!pt.value.IsInSubGroup() {
		return nil
	}
	value := bls12377.GT{}
	if p.value.IsInfinity() || pt.value.IsInfinity() {
		return &ScalarBls12377Gt{&value}
	}
	value, err := bls12377.Pair([]bls12377.G1Affine{*p.value}, []bls12377.G2Affine{*pt.value})
	if err != nil {
		return nil
	}

	return &ScalarBls12377Gt{&value}
}

func (p *PointBls12377G1) MultiPairing(points ...PairingPoint) Scalar {
	return multiPairingBls12377(points...)
}

func (p *PointBls12377G1) X() *big.Int {
	b := p.value.RawBytes()
	return new(big.Int).SetBytes(b[:48])
}

func (p *PointBls12377G1) Y() *big.Int {
	b := p.value.RawBytes()
	return new(big.Int).SetBytes(b[48:])
}

func (p *PointBls12377G1) Modulus() *big.Int {
	return bls12377modulus
}

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

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

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

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

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

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

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

func (p *PointBls12377G2) Hash(bytes []byte) Point {
	var domain = []byte("BLS12377G2_XMD:SHA-256_SVDW_RO_")
	pt, err := bls12377.HashToCurveG2Svdw(bytes, domain)
	if err != nil {
		return nil
	}
	return &PointBls12377G2{value: &pt}
}

func (p *PointBls12377G2) Identity() Point {
	t := bls12377.G2Affine{}
	return &PointBls12377G2{
		value: t.Set(&g2Inf),
	}
}

func (p *PointBls12377G2) Generator() Point {
	t := bls12377.G2Affine{}
	_, _, _, g2Aff := bls12377.Generators()
	return &PointBls12377G2{
		value: t.Set(&g2Aff),
	}
}

func (p *PointBls12377G2) IsIdentity() bool {
	return p.value.IsInfinity()
}

func (p *PointBls12377G2) 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.Bytes()[0]>>5)&1 == 1
}

func (p *PointBls12377G2) IsOnCurve() bool {
	return p.value.IsOnCurve()
}

func (p *PointBls12377G2) Double() Point {
	t := &bls12377.G2Jac{}
	t.FromAffine(p.value)
	t.DoubleAssign()
	value := bls12377.G2Affine{}
	return &PointBls12377G2{value.FromJacobian(t)}
}

func (p *PointBls12377G2) Scalar() Scalar {
	return &ScalarBls12377{
		value: new(big.Int),
		point: new(PointBls12377G2),
	}
}

func (p *PointBls12377G2) Neg() Point {
	value := &bls12377.G2Affine{}
	value.Neg(p.value)
	return &PointBls12377G2{value}
}

func (p *PointBls12377G2) Add(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12377G2)
	if ok {
		value := &bls12377.G2Affine{}
		return &PointBls12377G2{value.Add(p.value, r.value)}
	} else {
		return nil
	}
}

func (p *PointBls12377G2) Sub(rhs Point) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*PointBls12377G2)
	if ok {
		value := &bls12377.G2Affine{}
		return &PointBls12377G2{value.Sub(p.value, r.value)}
	} else {
		return nil
	}
}

func (p *PointBls12377G2) Mul(rhs Scalar) Point {
	if rhs == nil {
		return nil
	}
	r, ok := rhs.(*ScalarBls12377)
	if ok {
		value := &bls12377.G2Affine{}
		return &PointBls12377G2{value.ScalarMultiplication(p.value, r.value)}
	} else {
		return nil
	}
}

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

func (p *PointBls12377G2) Set(x, y *big.Int) (Point, error) {
	if x.Cmp(core.Zero) == 0 &&
		y.Cmp(core.Zero) == 0 {
		return p.Identity(), nil
	}
	var data [192]byte
	x.FillBytes(data[:96])
	y.FillBytes(data[96:])
	value := &bls12377.G2Affine{}
	_, err := value.SetBytes(data[:])
	if err != nil {
		return nil, fmt.Errorf("invalid coordinates")
	}
	return &PointBls12377G2{value}, nil
}

func (p *PointBls12377G2) ToAffineCompressed() []byte {
	v := p.value.Bytes()
	return v[:]
}

func (p *PointBls12377G2) ToAffineUncompressed() []byte {
	v := p.value.RawBytes()
	return v[:]
}

func (p *PointBls12377G2) FromAffineCompressed(bytes []byte) (Point, error) {
	if len(bytes) != bls12377.SizeOfG2AffineCompressed {
		return nil, fmt.Errorf("invalid point")
	}
	value := &bls12377.G2Affine{}
	_, err := value.SetBytes(bytes)
	if err != nil {
		return nil, err
	}
	return &PointBls12377G2{value}, nil
}

func (p *PointBls12377G2) FromAffineUncompressed(bytes []byte) (Point, error) {
	if len(bytes) != bls12377.SizeOfG2AffineUncompressed {
		return nil, fmt.Errorf("invalid point")
	}
	value := &bls12377.G2Affine{}
	_, err := value.SetBytes(bytes)
	if err != nil {
		return nil, err
	}
	return &PointBls12377G2{value}, nil
}

func (p *PointBls12377G2) CurveName() string {
	return "BLS12377G2"
}

func (p *PointBls12377G2) SumOfProducts(points []Point, scalars []Scalar) Point {
	nScalars := make([]*big.Int, len(scalars))
	for i, sc := range scalars {
		s, ok := sc.(*ScalarBls12377)
		if !ok {
			return nil
		}
		nScalars[i] = s.value
	}
	return sumOfProductsPippenger(points, nScalars)
}

func (p *PointBls12377G2) OtherGroup() PairingPoint {
	return new(PointBls12377G1).Identity().(PairingPoint)
}

func (p *PointBls12377G2) Pairing(rhs PairingPoint) Scalar {
	pt, ok := rhs.(*PointBls12377G1)
	if !ok {
		return nil
	}
	if !p.value.IsInSubGroup() ||
		!pt.value.IsInSubGroup() {
		return nil
	}
	value := bls12377.GT{}
	if p.value.IsInfinity() || pt.value.IsInfinity() {
		return &ScalarBls12377Gt{&value}
	}
	value, err := bls12377.Pair([]bls12377.G1Affine{*pt.value}, []bls12377.G2Affine{*p.value})
	if err != nil {
		return nil
	}

	return &ScalarBls12377Gt{&value}
}

func (p *PointBls12377G2) MultiPairing(points ...PairingPoint) Scalar {
	return multiPairingBls12377(points...)
}

func (p *PointBls12377G2) X() *big.Int {
	b := p.value.RawBytes()
	return new(big.Int).SetBytes(b[:96])
}

func (p *PointBls12377G2) Y() *big.Int {
	b := p.value.RawBytes()
	return new(big.Int).SetBytes(b[96:])
}

func (p *PointBls12377G2) Modulus() *big.Int {
	return bls12377modulus
}

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

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

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

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

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

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

func multiPairingBls12377(points ...PairingPoint) Scalar {
	if len(points)%2 != 0 {
		return nil
	}
	g1Arr := make([]bls12377.G1Affine, 0, len(points)/2)
	g2Arr := make([]bls12377.G2Affine, 0, len(points)/2)
	valid := true
	for i := 0; i < len(points); i += 2 {
		pt1, ok := points[i].(*PointBls12377G1)
		valid = valid && ok
		pt2, ok := points[i+1].(*PointBls12377G2)
		valid = valid && ok
		if valid {
			valid = valid && pt1.value.IsInSubGroup()
			valid = valid && pt2.value.IsInSubGroup()
		}
		if valid {
			g1Arr = append(g1Arr, *pt1.value)
			g2Arr = append(g2Arr, *pt2.value)
		}
	}
	if !valid {
		return nil
	}

	value, err := bls12377.Pair(g1Arr, g2Arr)
	if err != nil {
		return nil
	}

	return &ScalarBls12377Gt{&value}
}

func (s *ScalarBls12377Gt) Random(reader io.Reader) Scalar {
	const width = 48
	offset := 0
	var data [bls12377.SizeOfGT]byte
	for i := 0; i < 12; i++ {
		tv, err := rand.Int(reader, bls12377modulus)
		if err != nil {
			return nil
		}
		tv.FillBytes(data[offset*width : (offset+1)*width])
		offset++
	}
	value := bls12377.GT{}
	err := value.SetBytes(data[:])
	if err != nil {
		return nil
	}
	return &ScalarBls12377Gt{&value}
}

func (s *ScalarBls12377Gt) 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 *ScalarBls12377Gt) Zero() Scalar {
	var t [bls12377.SizeOfGT]byte
	value := bls12377.GT{}
	err := value.SetBytes(t[:])
	if err != nil {
		return nil
	}
	return &ScalarBls12377Gt{&value}
}

func (s *ScalarBls12377Gt) One() Scalar {
	value := bls12377.GT{}
	return &ScalarBls12377Gt{value.SetOne()}
}

func (s *ScalarBls12377Gt) IsZero() bool {
	r := byte(0)
	b := s.value.Bytes()
	for _, i := range b {
		r |= i
	}
	return r == 0
}

func (s *ScalarBls12377Gt) IsOne() bool {
	o := bls12377.GT{}
	return s.value.Equal(o.SetOne())
}

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

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

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

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

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

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

func (s *ScalarBls12377Gt) IsOdd() bool {
	data := s.value.Bytes()
	return data[len(data)-1]&1 == 1
}

func (s *ScalarBls12377Gt) IsEven() bool {
	data := s.value.Bytes()
	return data[len(data)-1]&1 == 0
}

func (s *ScalarBls12377Gt) New(input int) Scalar {
	var data [576]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 := bls12377.GT{}
	err := value.SetBytes(data[:])
	if err != nil {
		return nil
	}
	return &ScalarBls12377Gt{&value}
}

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

func (s *ScalarBls12377Gt) Square() Scalar {
	value := bls12377.GT{}
	return &ScalarBls12377Gt{
		value.Square(s.value),
	}
}

func (s *ScalarBls12377Gt) Double() Scalar {
	value := &bls12377.GT{}
	return &ScalarBls12377Gt{
		value.Add(s.value, s.value),
	}
}

func (s *ScalarBls12377Gt) Invert() (Scalar, error) {
	value := &bls12377.GT{}
	return &ScalarBls12377Gt{
		value.Inverse(s.value),
	}, nil
}

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

func (s *ScalarBls12377Gt) Cube() Scalar {
	value := &bls12377.GT{}
	value.Square(s.value)
	value.Mul(value, s.value)
	return &ScalarBls12377Gt{
		value,
	}
}

func (s *ScalarBls12377Gt) Add(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377Gt)
	if ok {
		value := &bls12377.GT{}
		return &ScalarBls12377Gt{
			value.Add(s.value, r.value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377Gt) Sub(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377Gt)
	if ok {
		value := &bls12377.GT{}
		return &ScalarBls12377Gt{
			value.Sub(s.value, r.value),
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377Gt) Mul(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377Gt)
	if ok {
		value := &bls12377.GT{}
		return &ScalarBls12377Gt{
			value.Mul(s.value, r.value),
		}
	} else {
		return nil
	}
}

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

func (s *ScalarBls12377Gt) Div(rhs Scalar) Scalar {
	r, ok := rhs.(*ScalarBls12377Gt)
	if ok {
		value := &bls12377.GT{}
		value.Inverse(r.value)
		value.Mul(value, s.value)
		return &ScalarBls12377Gt{
			value,
		}
	} else {
		return nil
	}
}

func (s *ScalarBls12377Gt) Neg() Scalar {
	sValue := &bls12377.GT{}
	sValue.SetOne()
	value := &bls12377.GT{}
	value.SetOne()
	value.Sub(value, sValue)
	return &ScalarBls12377Gt{
		value.Sub(value, s.value),
	}
}

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

func (s *ScalarBls12377Gt) BigInt() *big.Int {
	b := s.value.Bytes()
	return new(big.Int).SetBytes(b[:])
}

func (s *ScalarBls12377Gt) Point() Point {
	p := &PointBls12377G1{}
	return p.Identity()
}

func (s *ScalarBls12377Gt) Bytes() []byte {
	b := s.value.Bytes()
	return b[:]
}

func (s *ScalarBls12377Gt) SetBytes(bytes []byte) (Scalar, error) {
	value := &bls12377.GT{}
	err := value.SetBytes(bytes)
	if err != nil {
		return nil, err
	}
	return &ScalarBls12377Gt{value}, nil
}

func (s *ScalarBls12377Gt) SetBytesWide(bytes []byte) (Scalar, error) {
	l := len(bytes)
	if l != 1152 {
		return nil, fmt.Errorf("invalid byte sequence")
	}
	value := &bls12377.GT{}
	err := value.SetBytes(bytes[:l/2])
	if err != nil {
		return nil, err
	}
	value2 := &bls12377.GT{}
	err = value2.SetBytes(bytes[l/2:])
	if err != nil {
		return nil, err
	}
	value.Add(value, value2)
	return &ScalarBls12377Gt{value}, nil
}

func (s *ScalarBls12377Gt) Clone() Scalar {
	value := &bls12377.GT{}
	return &ScalarBls12377Gt{
		value.Set(s.value),
	}
}
