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

// A dynamic lookup table for variable-base, constant-time scalar muls.
type projLookupTable struct {
	points [8]projCached
}

// A precomputed lookup table for fixed-base, constant-time scalar muls.
type affineLookupTable struct {
	points [8]affineCached
}

// A dynamic lookup table for variable-base, variable-time scalar muls.
type nafLookupTable5 struct {
	points [8]projCached
}

// A precomputed lookup table for fixed-base, variable-time scalar muls.
type nafLookupTable8 struct {
	points [64]affineCached
}

// Constructors.

// Builds a lookup table at runtime. Fast.
func ( *projLookupTable) ( *Point) {
	// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
	// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
	.points[0].FromP3()
	 := Point{}
	 := projP1xP1{}
	for  := 0;  < 7; ++ {
		// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
		// This is needlessly complicated because the API has explicit
		// recievers instead of creating stack objects and relying on RVO
		.points[+1].FromP3(.fromP1xP1(.Add(, &.points[])))
	}
}

// This is not optimised for speed; fixed-base tables should be precomputed.
func ( *affineLookupTable) ( *Point) {
	// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
	// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
	.points[0].FromP3()
	 := Point{}
	 := projP1xP1{}
	for  := 0;  < 7; ++ {
		// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
		.points[+1].FromP3(.fromP1xP1(.AddAffine(, &.points[])))
	}
}

// Builds a lookup table at runtime. Fast.
func ( *nafLookupTable5) ( *Point) {
	// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
	// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
	.points[0].FromP3()
	 := Point{}
	.Add(, )
	 := Point{}
	 := projP1xP1{}
	for  := 0;  < 7; ++ {
		.points[+1].FromP3(.fromP1xP1(.Add(&, &.points[])))
	}
}

// This is not optimised for speed; fixed-base tables should be precomputed.
func ( *nafLookupTable8) ( *Point) {
	.points[0].FromP3()
	 := Point{}
	.Add(, )
	 := Point{}
	 := projP1xP1{}
	for  := 0;  < 63; ++ {
		.points[+1].FromP3(.fromP1xP1(.AddAffine(&, &.points[])))
	}
}

// Selectors.

// Set dest to x*Q, where -8 <= x <= 8, in constant time.
func ( *projLookupTable) ( *projCached,  int8) {
	// Compute xabs = |x|
	 :=  >> 7
	 := uint8(( + ) ^ )

	.Zero()
	for  := 1;  <= 8; ++ {
		// Set dest = j*Q if |x| = j
		 := subtle.ConstantTimeByteEq(, uint8())
		.Select(&.points[-1], , )
	}
	// Now dest = |x|*Q, conditionally negate to get x*Q
	.CondNeg(int( & 1))
}

// Set dest to x*Q, where -8 <= x <= 8, in constant time.
func ( *affineLookupTable) ( *affineCached,  int8) {
	// Compute xabs = |x|
	 :=  >> 7
	 := uint8(( + ) ^ )

	.Zero()
	for  := 1;  <= 8; ++ {
		// Set dest = j*Q if |x| = j
		 := subtle.ConstantTimeByteEq(, uint8())
		.Select(&.points[-1], , )
	}
	// Now dest = |x|*Q, conditionally negate to get x*Q
	.CondNeg(int( & 1))
}

// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
func ( *nafLookupTable5) ( *projCached,  int8) {
	* = .points[/2]
}

// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
func ( *nafLookupTable8) ( *affineCached,  int8) {
	* = .points[/2]
}