// Copyright (c) 2017 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 edwards25519

import (
	
	
)

// Point types.

type projP1xP1 struct {
	X, Y, Z, T field.Element
}

type projP2 struct {
	X, Y, Z field.Element
}

// Point represents a point on the edwards25519 curve.
//
// This type works similarly to math/big.Int, and all arguments and receivers
// are allowed to alias.
//
// The zero value is NOT valid, and it may be used only as a receiver.
type Point struct {
	// Make the type not comparable (i.e. used with == or as a map key), as
	// equivalent points can be represented by different Go values.
	_ incomparable

	// The point is internally represented in extended coordinates (X, Y, Z, T)
	// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
	x, y, z, t field.Element
}

type incomparable [0]func()

func checkInitialized( ...*Point) {
	for ,  := range  {
		if .x == (field.Element{}) && .y == (field.Element{}) {
			panic("edwards25519: use of uninitialized Point")
		}
	}
}

type projCached struct {
	YplusX, YminusX, Z, T2d field.Element
}

type affineCached struct {
	YplusX, YminusX, T2d field.Element
}

// Constructors.

func ( *projP2) () *projP2 {
	.X.Zero()
	.Y.One()
	.Z.One()
	return 
}

// identity is the point at infinity.
var identity, _ = new(Point).SetBytes([]byte{
	1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})

// NewIdentityPoint returns a new Point set to the identity.
func () *Point {
	return new(Point).Set(identity)
}

// generator is the canonical curve basepoint. See TestGenerator for the
// correspondence of this encoding with the values in RFC 8032.
var generator, _ = new(Point).SetBytes([]byte{
	0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
	0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
	0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
	0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})

// NewGeneratorPoint returns a new Point set to the canonical generator.
func () *Point {
	return new(Point).Set(generator)
}

func ( *projCached) () *projCached {
	.YplusX.One()
	.YminusX.One()
	.Z.One()
	.T2d.Zero()
	return 
}

func ( *affineCached) () *affineCached {
	.YplusX.One()
	.YminusX.One()
	.T2d.Zero()
	return 
}

// Assignments.

// Set sets v = u, and returns v.
func ( *Point) ( *Point) *Point {
	* = *
	return 
}

// Encoding.

// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
// Section 5.1.2.
func ( *Point) () []byte {
	// This function is outlined to make the allocations inline in the caller
	// rather than happen on the heap.
	var  [32]byte
	return .bytes(&)
}

func ( *Point) ( *[32]byte) []byte {
	checkInitialized()

	var , ,  field.Element
	.Invert(&.z)       // zInv = 1 / Z
	.Multiply(&.x, &) // x = X / Z
	.Multiply(&.y, &) // y = Y / Z

	 := copyFieldElement(, &)
	[31] |= byte(.IsNegative() << 7)
	return 
}

var feOne = new(field.Element).One()

// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not
// represent a valid point on the curve, SetBytes returns nil and an error and
// the receiver is unchanged. Otherwise, SetBytes returns v.
//
// Note that SetBytes accepts all non-canonical encodings of valid points.
// That is, it follows decoding rules that match most implementations in
// the ecosystem rather than RFC 8032.
func ( *Point) ( []byte) (*Point, error) {
	// Specifically, the non-canonical encodings that are accepted are
	//   1) the ones where the field element is not reduced (see the
	//      (*field.Element).SetBytes docs) and
	//   2) the ones where the x-coordinate is zero and the sign bit is set.
	//
	// Read more at https://hdevalence.ca/blog/2020-10-04-its-25519am,
	// specifically the "Canonical A, R" section.

	,  := new(field.Element).SetBytes()
	if  != nil {
		return nil, errors.New("edwards25519: invalid point encoding length")
	}

	// -x² + y² = 1 + dx²y²
	// x² + dx²y² = x²(dy² + 1) = y² - 1
	// x² = (y² - 1) / (dy² + 1)

	// u = y² - 1
	 := new(field.Element).Square()
	 := new(field.Element).Subtract(, feOne)

	// v = dy² + 1
	 := new(field.Element).Multiply(, d)
	 = .Add(, feOne)

	// x = +√(u/v)
	,  := new(field.Element).SqrtRatio(, )
	if  == 0 {
		return nil, errors.New("edwards25519: invalid point encoding")
	}

	// Select the negative square root if the sign bit is set.
	 := new(field.Element).Negate()
	 = .Select(, , int([31]>>7))

	.x.Set()
	.y.Set()
	.z.One()
	.t.Multiply(, ) // xy = T / Z

	return , nil
}

func copyFieldElement( *[32]byte,  *field.Element) []byte {
	copy([:], .Bytes())
	return [:]
}

// Conversions.

func ( *projP2) ( *projP1xP1) *projP2 {
	.X.Multiply(&.X, &.T)
	.Y.Multiply(&.Y, &.Z)
	.Z.Multiply(&.Z, &.T)
	return 
}

func ( *projP2) ( *Point) *projP2 {
	.X.Set(&.x)
	.Y.Set(&.y)
	.Z.Set(&.z)
	return 
}

func ( *Point) ( *projP1xP1) *Point {
	.x.Multiply(&.X, &.T)
	.y.Multiply(&.Y, &.Z)
	.z.Multiply(&.Z, &.T)
	.t.Multiply(&.X, &.Y)
	return 
}

func ( *Point) ( *projP2) *Point {
	.x.Multiply(&.X, &.Z)
	.y.Multiply(&.Y, &.Z)
	.z.Square(&.Z)
	.t.Multiply(&.X, &.Y)
	return 
}

// d is a constant in the curve equation.
var d, _ = new(field.Element).SetBytes([]byte{
	0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
	0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
	0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
	0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52})
var d2 = new(field.Element).Add(d, d)

func ( *projCached) ( *Point) *projCached {
	.YplusX.Add(&.y, &.x)
	.YminusX.Subtract(&.y, &.x)
	.Z.Set(&.z)
	.T2d.Multiply(&.t, d2)
	return 
}

func ( *affineCached) ( *Point) *affineCached {
	.YplusX.Add(&.y, &.x)
	.YminusX.Subtract(&.y, &.x)
	.T2d.Multiply(&.t, d2)

	var  field.Element
	.Invert(&.z)
	.YplusX.Multiply(&.YplusX, &)
	.YminusX.Multiply(&.YminusX, &)
	.T2d.Multiply(&.T2d, &)
	return 
}

// (Re)addition and subtraction.

// Add sets v = p + q, and returns v.
func ( *Point) (,  *Point) *Point {
	checkInitialized(, )
	 := new(projCached).FromP3()
	 := new(projP1xP1).Add(, )
	return .fromP1xP1()
}

// Subtract sets v = p - q, and returns v.
func ( *Point) (,  *Point) *Point {
	checkInitialized(, )
	 := new(projCached).FromP3()
	 := new(projP1xP1).Sub(, )
	return .fromP1xP1()
}

func ( *projP1xP1) ( *Point,  *projCached) *projP1xP1 {
	var , , , , ,  field.Element

	.Add(&.y, &.x)
	.Subtract(&.y, &.x)

	.Multiply(&, &.YplusX)
	.Multiply(&, &.YminusX)
	.Multiply(&.t, &.T2d)
	.Multiply(&.z, &.Z)

	.Add(&, &)

	.X.Subtract(&, &)
	.Y.Add(&, &)
	.Z.Add(&, &)
	.T.Subtract(&, &)
	return 
}

func ( *projP1xP1) ( *Point,  *projCached) *projP1xP1 {
	var , , , , ,  field.Element

	.Add(&.y, &.x)
	.Subtract(&.y, &.x)

	.Multiply(&, &.YminusX) // flipped sign
	.Multiply(&, &.YplusX) // flipped sign
	.Multiply(&.t, &.T2d)
	.Multiply(&.z, &.Z)

	.Add(&, &)

	.X.Subtract(&, &)
	.Y.Add(&, &)
	.Z.Subtract(&, &) // flipped sign
	.T.Add(&, &)      // flipped sign
	return 
}

func ( *projP1xP1) ( *Point,  *affineCached) *projP1xP1 {
	var , , , , ,  field.Element

	.Add(&.y, &.x)
	.Subtract(&.y, &.x)

	.Multiply(&, &.YplusX)
	.Multiply(&, &.YminusX)
	.Multiply(&.t, &.T2d)

	.Add(&.z, &.z)

	.X.Subtract(&, &)
	.Y.Add(&, &)
	.Z.Add(&, &)
	.T.Subtract(&, &)
	return 
}

func ( *projP1xP1) ( *Point,  *affineCached) *projP1xP1 {
	var , , , , ,  field.Element

	.Add(&.y, &.x)
	.Subtract(&.y, &.x)

	.Multiply(&, &.YminusX) // flipped sign
	.Multiply(&, &.YplusX) // flipped sign
	.Multiply(&.t, &.T2d)

	.Add(&.z, &.z)

	.X.Subtract(&, &)
	.Y.Add(&, &)
	.Z.Subtract(&, &) // flipped sign
	.T.Add(&, &)      // flipped sign
	return 
}

// Doubling.

func ( *projP1xP1) ( *projP2) *projP1xP1 {
	var , , ,  field.Element

	.Square(&.X)
	.Square(&.Y)
	.Square(&.Z)
	.Add(&, &)
	.Add(&.X, &.Y)
	.Square(&)

	.Y.Add(&, &)
	.Z.Subtract(&, &)

	.X.Subtract(&, &.Y)
	.T.Subtract(&, &.Z)
	return 
}

// Negation.

// Negate sets v = -p, and returns v.
func ( *Point) ( *Point) *Point {
	checkInitialized()
	.x.Negate(&.x)
	.y.Set(&.y)
	.z.Set(&.z)
	.t.Negate(&.t)
	return 
}

// Equal returns 1 if v is equivalent to u, and 0 otherwise.
func ( *Point) ( *Point) int {
	checkInitialized(, )

	var , , ,  field.Element
	.Multiply(&.x, &.z)
	.Multiply(&.x, &.z)
	.Multiply(&.y, &.z)
	.Multiply(&.y, &.z)

	return .Equal(&) & .Equal(&)
}

// Constant-time operations

// Select sets v to a if cond == 1 and to b if cond == 0.
func ( *projCached) (,  *projCached,  int) *projCached {
	.YplusX.Select(&.YplusX, &.YplusX, )
	.YminusX.Select(&.YminusX, &.YminusX, )
	.Z.Select(&.Z, &.Z, )
	.T2d.Select(&.T2d, &.T2d, )
	return 
}

// Select sets v to a if cond == 1 and to b if cond == 0.
func ( *affineCached) (,  *affineCached,  int) *affineCached {
	.YplusX.Select(&.YplusX, &.YplusX, )
	.YminusX.Select(&.YminusX, &.YminusX, )
	.T2d.Select(&.T2d, &.T2d, )
	return 
}

// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
func ( *projCached) ( int) *projCached {
	.YplusX.Swap(&.YminusX, )
	.T2d.Select(new(field.Element).Negate(&.T2d), &.T2d, )
	return 
}

// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
func ( *affineCached) ( int) *affineCached {
	.YplusX.Swap(&.YminusX, )
	.T2d.Select(new(field.Element).Negate(&.T2d), &.T2d, )
	return 
}