// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package elliptic

import (
	
	
)

type p521Curve struct {
	*CurveParams
}

var p521 p521Curve
var p521Params *CurveParams

func initP521() {
	// See FIPS 186-3, section D.2.5
	p521.CurveParams = &CurveParams{Name: "P-521"}
	p521.P, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", 10)
	p521.N, _ = new(big.Int).SetString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449", 10)
	p521.B, _ = new(big.Int).SetString("051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00", 16)
	p521.Gx, _ = new(big.Int).SetString("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16)
	p521.Gy, _ = new(big.Int).SetString("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16)
	p521.BitSize = 521
}

func ( p521Curve) () *CurveParams {
	return .CurveParams
}

func ( p521Curve) (,  *big.Int) bool {
	 := bigIntToFiatP521()
	 := bigIntToFiatP521()
	 := bigIntToFiatP521(.B) // TODO: precompute this value.

	// x³ - 3x + b.
	 := new(fiat.P521Element).Square()
	.Mul(, )

	 := new(fiat.P521Element).Add(, )
	.Add(, )

	.Sub(, )
	.Add(, )

	// y² = x³ - 3x + b
	 := new(fiat.P521Element).Square()

	return .Equal() == 1
}

type p521Point struct {
	x, y, z *fiat.P521Element
}

func fiatP521ToBigInt( *fiat.P521Element) *big.Int {
	 := .Bytes()
	for  := range [:len()/2] {
		[], [len()--1] = [len()--1], []
	}
	return new(big.Int).SetBytes()
}

// affineFromJacobian brings a point in Jacobian coordinates back to affine
// coordinates, with (0, 0) representing infinity by convention. It also goes
// back to big.Int values to match the exposed API.
func ( p521Curve) ( *p521Point) (,  *big.Int) {
	if .z.IsZero() == 1 {
		return new(big.Int), new(big.Int)
	}

	 := new(fiat.P521Element).Invert(.z)
	 := new(fiat.P521Element).Mul(, )

	 := new(fiat.P521Element).Mul(.x, )
	.Mul(, )
	 := new(fiat.P521Element).Mul(.y, )

	return fiatP521ToBigInt(), fiatP521ToBigInt()
}

func bigIntToFiatP521( *big.Int) *fiat.P521Element {
	 := new(big.Int).Mod(, p521.P).FillBytes(make([]byte, 66))
	for  := range [:len()/2] {
		[], [len()--1] = [len()--1], []
	}
	,  := new(fiat.P521Element).SetBytes()
	if  != nil {
		// The input is reduced modulo P and encoded in a fixed size bytes
		// slice, this should be impossible.
		panic("internal error: bigIntToFiatP521")
	}
	return 
}

// jacobianFromAffine converts (x, y) affine coordinates into (x, y, z) Jacobian
// coordinates. It also converts from big.Int to fiat, which is necessarily a
// messy and variable-time operation, which we can't avoid due to the exposed API.
func ( p521Curve) (,  *big.Int) *p521Point {
	// (0, 0) is by convention the point at infinity, which can't be represented
	// in affine coordinates, but is (0, 0, 0) in Jacobian.
	if .Sign() == 0 && .Sign() == 0 {
		return &p521Point{
			x: new(fiat.P521Element),
			y: new(fiat.P521Element),
			z: new(fiat.P521Element),
		}
	}
	return &p521Point{
		x: bigIntToFiatP521(),
		y: bigIntToFiatP521(),
		z: new(fiat.P521Element).One(),
	}
}

func ( p521Curve) (, , ,  *big.Int) (*big.Int, *big.Int) {
	 := .jacobianFromAffine(, )
	 := .jacobianFromAffine(, )
	return .affineFromJacobian(.addJacobian(, ))
}

// addJacobian sets q = p1 + p2, and returns q. The points may overlap.
func ( *p521Point) (,  *p521Point) *p521Point {
	// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
	 := .z.IsZero()
	 := .z.IsZero()

	 := new(fiat.P521Element).Square(.z)
	 := new(fiat.P521Element).Square(.z)

	 := new(fiat.P521Element).Mul(.x, )
	 := new(fiat.P521Element).Mul(.x, )
	 := new(fiat.P521Element).Sub(, )
	 := .IsZero() == 1
	 := new(fiat.P521Element).Add(, )
	.Square()
	 := new(fiat.P521Element).Mul(, )

	 := new(fiat.P521Element).Mul(.y, .z)
	.Mul(, )
	 := new(fiat.P521Element).Mul(.y, .z)
	.Mul(, )
	 := new(fiat.P521Element).Sub(, )
	 := .IsZero() == 1
	if  &&  &&  == 0 &&  == 0 {
		return .doubleJacobian()
	}
	.Add(, )
	 := new(fiat.P521Element).Mul(, )

	 := new(fiat.P521Element).Set()
	.Square()
	.Sub(, )
	.Sub(, )
	.Sub(, )

	 := new(fiat.P521Element).Set()
	.Sub(, )
	.Mul(, )
	.Mul(, )
	.Add(, )
	.Sub(, )

	 := new(fiat.P521Element).Add(.z, .z)
	.Square()
	.Sub(, )
	.Sub(, )
	.Mul(, )

	.Select(.x, , )
	.Select(.x, , )
	.Select(.y, , )
	.Select(.y, , )
	.Select(.z, , )
	.Select(.z, , )

	.x.Set()
	.y.Set()
	.z.Set()
	return 
}

func ( p521Curve) (,  *big.Int) (*big.Int, *big.Int) {
	 := .jacobianFromAffine(, )
	return .affineFromJacobian(.doubleJacobian())
}

// doubleJacobian sets q = p + p, and returns q. The points may overlap.
func ( *p521Point) ( *p521Point) *p521Point {
	// https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
	 := new(fiat.P521Element).Square(.z)
	 := new(fiat.P521Element).Square(.y)
	 := new(fiat.P521Element).Sub(.x, )
	 := new(fiat.P521Element).Add(.x, )
	.Mul(, )
	.Set()
	.Add(, )
	.Add(, )

	 := .Mul(.x, )

	.x.Square()
	 := new(fiat.P521Element).Add(, )
	.Add(, )
	.Add(, )
	.x.Sub(.x, )

	.z.Add(.y, .z)
	.z.Square(.z)
	.z.Sub(.z, )
	.z.Sub(.z, )

	.Add(, )
	.Add(, )
	.Sub(, .x)
	.y.Mul(, )

	.Square()
	.Add(, )
	.Add(, )
	.Add(, )

	.y.Sub(.y, )

	return 
}

func ( p521Curve) (,  *big.Int,  []byte) (*big.Int, *big.Int) {
	 := .jacobianFromAffine(, )
	,  := &p521Point{
		x: new(fiat.P521Element),
		y: new(fiat.P521Element),
		z: new(fiat.P521Element),
	}, &p521Point{
		x: new(fiat.P521Element),
		y: new(fiat.P521Element),
		z: new(fiat.P521Element),
	}

	for ,  := range  {
		for  := 0;  < 8; ++ {
			.doubleJacobian()
			 := ( >> (7 - )) & 1
			.addJacobian(, )
			.x.Select(.x, .x, int())
			.y.Select(.y, .y, int())
			.z.Select(.z, .z, int())
		}
	}

	return .affineFromJacobian()
}

func ( p521Curve) ( []byte) (*big.Int, *big.Int) {
	return .ScalarMult(.Gx, .Gy, )
}