/*
------------------------------------------------------------------------------------
 Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0 OR ISC
------------------------------------------------------------------------------------
*/

// Note: The function elliptic.UnmarshalCompressed is available as of go1.15

package main

import (
	"crypto/elliptic"
	"fmt"
	"math/big"
)

const num_Points = 8

func printPadded(key string, n, max *big.Int) {
	padded := make([]byte, len(max.Bytes()))
	b := n.Bytes()
	copy(padded[len(padded)-len(b):], b)
	fmt.Printf("%s = %x\n", key, padded)
}

func makeCompressedPoint(curve elliptic.Curve, x *big.Int, y_bit uint8) (x_out, y_out *big.Int) {
	byteLen := (curve.Params().BitSize + 7) / 8
	compressed := make([]byte, 1+byteLen)
	x.FillBytes(compressed[1:])
	compressed[0] = 2 + byte(y_bit)
	x_out, y_out = elliptic.UnmarshalCompressed(curve, compressed)
	if x_out != nil {
		return x_out, y_out
	}
	// Otherwise, this x is not on the curve
	return nil, nil
}

func printPoints(name string, curve elliptic.Curve) {
	x := new(big.Int)
	y_b := uint8(0)
	for i := 1; i <= num_Points; i++ {
		x.SetInt64(int64(i))
		for y_b = 0; y_b <= 1; y_b++ {
			x_o, y_o := makeCompressedPoint(curve, x, y_b)
			if x_o != nil {
				fmt.Printf("Curve = %s\n", name)
				fmt.Printf("# x = %d\n", i)
				printPadded("X", x_o, curve.Params().P)
				x_o = x_o.Add(x_o, curve.Params().P)
				printPadded("XplusP", x_o, curve.Params().P)
				printPadded("Y", y_o, curve.Params().P)
				fmt.Printf("\n")
			}
		}
	}
}

func main() {
	fmt.Printf(`# This file contains points with very small x for various curves,
# such that x+p can still fit in the same bitlength as p.
# This is true for P-224, P-256 and P-384. For P-521, other than x=0, i.e. starting at x=1,
# the bitlength increases by 1, but the byte length remains the same.
# This file is generated by make_large_x_coordinate.go

`)
	printPoints("P-224", elliptic.P224())
	printPoints("P-256", elliptic.P256())
	printPoints("P-384", elliptic.P384())
	printPoints("P-521", elliptic.P521())
}
