// Copyright (c) 2020-2023 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package secp256k1

import (
	"fmt"
	"math/big"
	"math/rand"
	"reflect"
	"testing"
	"time"

	"lol.mleku.dev/chk"
	"next.orly.dev/pkg/encoders/hex"
	"next.orly.dev/pkg/utils"
)

// SetHex interprets the provided hex string as a 256-bit big-endian unsigned
// integer (meaning it is truncated to the first 32 bytes), reduces it modulo
// the group order and sets the scalar to the result.
//
// This is NOT constant time.
//
// The scalar is returned to support chaining.  This enables syntax like:
// s := new(ModNScalar).SetHex("0abc").Add(1) so that s = 0x0abc + 1
func (s *ModNScalar) SetHex(hexString string) *ModNScalar {
	if len(hexString)%2 != 0 {
		hexString = "0" + hexString
	}
	bytes, _ := hex.Dec(hexString)
	s.SetByteSlice(bytes)
	return s
}

// randModNScalar returns a mod N scalar created from a random value generated
// by the passed rng.
func randModNScalar(t *testing.T, rng *rand.Rand) *ModNScalar {
	t.Helper()
	var buf [32]byte
	if _, err := rng.Read(buf[:]); chk.T(err) {
		t.Fatalf("failed to read random: %v", err)
	}
	// Create and return a mod N scalar.
	var modNVal ModNScalar
	modNVal.SetBytes(&buf)
	return &modNVal
}

// randIntAndModNScalar returns a big integer and mod N scalar both created from
// the same random value generated by the passed rng.
func randIntAndModNScalar(t *testing.T, rng *rand.Rand) (
	*big.Int,
	*ModNScalar,
) {
	t.Helper()
	var buf [32]byte
	if _, err := rng.Read(buf[:]); chk.T(err) {
		t.Fatalf("failed to read random: %v", err)
	}
	// Create and return both a big integer and a mod N scalar.
	bigIntVal := new(big.Int).SetBytes(buf[:])
	bigIntVal.Mod(bigIntVal, curveParams.N)
	var modNVal ModNScalar
	modNVal.SetBytes(&buf)
	return bigIntVal, &modNVal
}

// TestModNScalarZero ensures that zeroing a scalar modulo the group order works
// as expected.
func TestModNScalarZero(t *testing.T) {
	var s ModNScalar
	s.SetHex("a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5")
	s.Zero()
	for idx, rawInt := range s.n {
		if rawInt != 0 {
			t.Errorf(
				"internal integer at index #%d is not zero - got %d", idx,
				rawInt,
			)
		}
	}
}

// TestModNScalarIsZero ensures that checking if a scalar is zero via IsZero and
// IsZeroBit works as expected.
func TestModNScalarIsZero(t *testing.T) {
	var s ModNScalar
	if !s.IsZero() {
		t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n)
	}
	if s.IsZeroBit() != 1 {
		t.Errorf("new scalar is not zero - got %v (rawints %x)", s, s.n)
	}
	s.SetInt(1)
	if s.IsZero() {
		t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n)
	}
	if s.IsZeroBit() == 1 {
		t.Errorf("claims zero for nonzero scalar - got %v (rawints %x)", s, s.n)
	}

	s.SetInt(0)
	if !s.IsZero() {
		t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
	}
	if s.IsZeroBit() != 1 {
		t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
	}
	s.SetInt(1)
	s.Zero()
	if !s.IsZero() {
		t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
	}
	if s.IsZeroBit() != 1 {
		t.Errorf("claims nonzero for zero scalar - got %v (rawints %x)", s, s.n)
	}
}

// TestModNScalarSetInt ensures that setting a scalar to various native integers
// works as expected.
func TestModNScalarSetInt(t *testing.T) {
	tests := []struct {
		name     string    // test description
		in       uint32    // test value
		expected [8]uint32 // expected raw ints
	}{
		{
			name:     "five",
			in:       5,
			expected: [8]uint32{5, 0, 0, 0, 0, 0, 0, 0},
		}, {
			name:     "group order word zero",
			in:       orderWordZero,
			expected: [8]uint32{orderWordZero, 0, 0, 0, 0, 0, 0, 0},
		}, {
			name:     "group order word zero + 1",
			in:       orderWordZero + 1,
			expected: [8]uint32{orderWordZero + 1, 0, 0, 0, 0, 0, 0, 0},
		}, {
			name:     "2^32 - 1",
			in:       4294967295,
			expected: [8]uint32{4294967295, 0, 0, 0, 0, 0, 0, 0},
		},
	}
	for _, test := range tests {
		s := new(ModNScalar).SetInt(test.in)
		if !reflect.DeepEqual(s.n, test.expected) {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, s.n,
				test.expected,
			)
			continue
		}
	}
}

// TestModNScalarSetBytes ensures that setting a scalar to a 256-bit big-endian
// unsigned integer via both the slice and array methods works as expected for
// edge cases.  Random cases are tested via the various other tests.
func TestModNScalarSetBytes(t *testing.T) {
	tests := []struct {
		name     string    // test description
		in       string    // hex encoded test value
		expected [8]uint32 // expected raw ints
		overflow bool      // expected overflow result
	}{
		{
			name:     "zero",
			in:       "00",
			expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0},
			overflow: false,
		}, {
			name:     "group order (aka 0)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			expected: [8]uint32{0, 0, 0, 0, 0, 0, 0, 0},
			overflow: true,
		}, {
			name: "group order - 1",
			in:   "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			expected: [8]uint32{
				0xd0364140, 0xbfd25e8c, 0xaf48a03b, 0xbaaedce6,
				0xfffffffe, 0xffffffff, 0xffffffff, 0xffffffff,
			},
			overflow: false,
		}, {
			name:     "group order + 1 (aka 1, overflow in word zero)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142",
			expected: [8]uint32{1, 0, 0, 0, 0, 0, 0, 0},
			overflow: true,
		}, {
			name:     "group order word zero",
			in:       "d0364141",
			expected: [8]uint32{0xd0364141, 0, 0, 0, 0, 0, 0, 0},
			overflow: false,
		}, {
			name:     "group order word zero and one",
			in:       "bfd25e8cd0364141",
			expected: [8]uint32{0xd0364141, 0xbfd25e8c, 0, 0, 0, 0, 0, 0},
			overflow: false,
		}, {
			name: "group order words zero, one, and two",
			in:   "af48a03bbfd25e8cd0364141",
			expected: [8]uint32{
				0xd0364141, 0xbfd25e8c, 0xaf48a03b, 0, 0, 0, 0, 0,
			},
			overflow: false,
		}, {
			name:     "overflow in word one",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141",
			expected: [8]uint32{0, 1, 0, 0, 0, 0, 0, 0},
			overflow: true,
		}, {
			name:     "overflow in word two",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141",
			expected: [8]uint32{0, 0, 1, 0, 0, 0, 0, 0},
			overflow: true,
		}, {
			name:     "overflow in word three",
			in:       "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141",
			expected: [8]uint32{0, 0, 0, 1, 0, 0, 0, 0},
			overflow: true,
		}, {
			name:     "overflow in word four",
			in:       "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141",
			expected: [8]uint32{0, 0, 0, 0, 1, 0, 0, 0},
			overflow: true,
		}, {
			name: "(group order - 1) * 2 NOT mod N, truncated >32 bytes",
			in:   "01fffffffffffffffffffffffffffffffd755db9cd5e9140777fa4bd19a06c8284",
			expected: [8]uint32{
				0x19a06c82, 0x777fa4bd, 0xcd5e9140, 0xfd755db9,
				0xffffffff, 0xffffffff, 0xffffffff, 0x01ffffff,
			},
			overflow: false,
		}, {
			name: "alternating bits",
			in:   "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: [8]uint32{
				0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
				0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5, 0xa5a5a5a5,
			},
			overflow: false,
		}, {
			name: "alternating bits 2",
			in:   "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: [8]uint32{
				0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
				0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a, 0x5a5a5a5a,
			},
			overflow: false,
		},
	}
	for _, test := range tests {
		inBytes := hexToBytes(test.in)

		// Ensure setting the bytes via the slice method works as expected.
		var s ModNScalar
		overflow := s.SetByteSlice(inBytes)
		if !reflect.DeepEqual(s.n, test.expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name, s.n,
				test.expected,
			)
			continue
		}
		// Ensure the setting the bytes via the slice method produces the
		// expected overflow result.
		if overflow != test.overflow {
			t.Errorf(
				"%s: unexpected overflow -- got: %v, want: %v", test.name,
				overflow, test.overflow,
			)
			continue
		}
		// Ensure setting the bytes via the array method works as expected.
		var s2 ModNScalar
		var b32 [32]byte
		truncatedInBytes := inBytes
		if len(truncatedInBytes) > 32 {
			truncatedInBytes = truncatedInBytes[:32]
		}
		copy(b32[32-len(truncatedInBytes):], truncatedInBytes)
		overflow = s2.SetBytes(&b32) != 0
		if !reflect.DeepEqual(s2.n, test.expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name,
				s2.n, test.expected,
			)
			continue
		}
		// Ensure the setting the bytes via the array method produces the
		// expected overflow result.
		if overflow != test.overflow {
			t.Errorf(
				"%s: unexpected overflow -- got: %v, want: %v", test.name,
				overflow, test.overflow,
			)
			continue
		}
	}
}

// TestModNScalarBytes ensures that retrieving the bytes for a 256-bit
// big-endian unsigned integer via the various methods works as expected for
// edge cases.  Random cases are tested via the various other tests.
func TestModNScalarBytes(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded test value
		expected string // expected hex encoded bytes
		overflow bool   // expected overflow result
	}{
		{
			name:     "zero",
			in:       "0",
			expected: "0000000000000000000000000000000000000000000000000000000000000000",
		}, {
			name:     "group order (aka 0)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			expected: "0000000000000000000000000000000000000000000000000000000000000000",
		}, {
			name:     "group order - 1",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
		}, {
			name:     "group order + 1 (aka 1, overflow in word zero)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142",
			expected: "0000000000000000000000000000000000000000000000000000000000000001",
		}, {
			name:     "group order word zero",
			in:       "d0364141",
			expected: "00000000000000000000000000000000000000000000000000000000d0364141",
		}, {
			name:     "group order word zero and one",
			in:       "bfd25e8cd0364141",
			expected: "000000000000000000000000000000000000000000000000bfd25e8cd0364141",
		}, {
			name:     "group order words zero, one, and two",
			in:       "af48a03bbfd25e8cd0364141",
			expected: "0000000000000000000000000000000000000000af48a03bbfd25e8cd0364141",
		}, {
			name:     "overflow in word one",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8dd0364141",
			expected: "0000000000000000000000000000000000000000000000000000000100000000",
		}, {
			name:     "overflow in word two",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03cbfd25e8cd0364141",
			expected: "0000000000000000000000000000000000000000000000010000000000000000",
		}, {
			name:     "overflow in word three",
			in:       "fffffffffffffffffffffffffffffffebaaedce7af48a03bbfd25e8cd0364141",
			expected: "0000000000000000000000000000000000000001000000000000000000000000",
		}, {
			name:     "overflow in word four",
			in:       "ffffffffffffffffffffffffffffffffbaaedce6af48a03bbfd25e8cd0364141",
			expected: "0000000000000000000000000000000100000000000000000000000000000000",
		}, {
			name:     "alternating bits",
			in:       "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
		}, {
			name:     "alternating bits 2",
			in:       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
		},
	}
	for _, test := range tests {
		s := new(ModNScalar).SetHex(test.in)
		expected := hexToBytes(test.expected)
		// Ensure getting the bytes works as expected.
		gotBytes := s.Bytes()
		if !utils.FastEqual(gotBytes[:], expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name,
				gotBytes, expected,
			)
			continue
		}
		// Ensure getting the bytes directly into an array works as expected.
		var b32 [32]byte
		s.PutBytes(&b32)
		if !utils.FastEqual(b32[:], expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name,
				b32, expected,
			)
			continue
		}
		// Ensure getting the bytes directly into a slice works as expected.
		var buffer [64]byte
		s.PutBytesUnchecked(buffer[:])
		if !utils.FastEqual(buffer[:32], expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name,
				buffer[:32], expected,
			)
			continue
		}
	}
}

// TestModNScalarIsOdd ensures that checking if a scalar is odd works as
// expected.
func TestModNScalarIsOdd(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded value
		expected bool   // expected oddness
	}{
		{
			name:     "zero",
			in:       "0",
			expected: false,
		}, {
			name:     "one",
			in:       "1",
			expected: true,
		}, {
			name:     "two",
			in:       "2",
			expected: false,
		}, {
			name:     "2^32 - 1",
			in:       "ffffffff",
			expected: true,
		}, {
			name:     "2^64 - 2",
			in:       "fffffffffffffffe",
			expected: false,
		}, {
			name:     "group order (aka 0)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			expected: false,
		}, {
			name:     "group order + 1 (aka 1)",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142",
			expected: true,
		},
	}
	for _, test := range tests {
		result := new(ModNScalar).SetHex(test.in).IsOdd()
		if result != test.expected {
			t.Errorf(
				"%s: wrong result -- got: %v, want: %v", test.name,
				result, test.expected,
			)
			continue
		}
	}
}

// TestModNScalarEquals ensures that checking two scalars for equality works as
// expected for edge cases.
func TestModNScalarEquals(t *testing.T) {
	tests := []struct {
		name     string // test description
		in1      string // hex encoded value
		in2      string // hex encoded value
		expected bool   // expected equality
	}{
		{
			name:     "0 == 0?",
			in1:      "0",
			in2:      "0",
			expected: true,
		}, {
			name:     "0 == 1?",
			in1:      "0",
			in2:      "1",
			expected: false,
		}, {
			name:     "1 == 0?",
			in1:      "1",
			in2:      "0",
			expected: false,
		}, {
			name:     "2^32 - 1 == 2^32 - 1?",
			in1:      "ffffffff",
			in2:      "ffffffff",
			expected: true,
		}, {
			name:     "2^64 - 1 == 2^64 - 2?",
			in1:      "ffffffffffffffff",
			in2:      "fffffffffffffffe",
			expected: false,
		}, {
			name:     "0 == group order?",
			in1:      "0",
			in2:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			expected: true,
		}, {
			name:     "1 == group order + 1?",
			in1:      "1",
			in2:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142",
			expected: true,
		},
	}
	for _, test := range tests {
		s1 := new(ModNScalar).SetHex(test.in1)
		s2 := new(ModNScalar).SetHex(test.in2)
		result := s1.Equals(s2)
		if result != test.expected {
			t.Errorf(
				"%s: wrong result -- got: %v, want: %v", test.name, result,
				test.expected,
			)
			continue
		}
	}
}

// TestModNScalarEqualsRandom ensures that scalars for random values works as
// expected.
func TestModNScalarEqualsRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Ensure a randomly-generated scalar equals itself.
		s := randModNScalar(t, rng)
		if !s.Equals(s) {
			t.Fatalf("failed equality check\nscalar in: %v", s)
		}
		// Flip a random bit in a random word and ensure it's no longer equal.
		randomWord := rng.Int31n(int32(len(s.n)))
		randomBit := uint32(1 << uint32(rng.Int31n(32)))
		s2 := new(ModNScalar).Set(s)
		s2.n[randomWord] ^= randomBit
		if s2.Equals(s) {
			t.Fatalf("failed inequality check\nscalar in: %v", s2)
		}
	}
}

// TestModNScalarAdd ensures that adding two scalars works as expected for edge
// cases.
func TestModNScalarAdd(t *testing.T) {
	tests := []struct {
		name     string // test description
		in1      string // first hex encoded test value
		in2      string // second hex encoded test value
		expected string // expected hex encoded bytes
	}{
		{
			name:     "zero + one",
			in1:      "0",
			in2:      "1",
			expected: "1",
		}, {
			name:     "one + zero",
			in1:      "1",
			in2:      "0",
			expected: "1",
		}, {
			name:     "group order (aka 0) + 1 (gets reduced, no overflow)",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			in2:      "1",
			expected: "1",
		}, {
			name:     "group order - 1 + 1 (aka 0, overflow to prime)",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			in2:      "1",
			expected: "0",
		}, {
			name:     "group order - 1 + 2 (aka 1, overflow in word zero)",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			in2:      "2",
			expected: "1",
		}, {
			name:     "overflow in word one",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141",
			in2:      "100000001",
			expected: "1",
		}, {
			name:     "overflow in word two",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141",
			in2:      "10000000000000001",
			expected: "1",
		}, {
			name:     "overflow in word three",
			in1:      "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141",
			in2:      "1000000000000000000000001",
			expected: "1",
		}, {
			name:     "overflow in word four",
			in1:      "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141",
			in2:      "100000000000000000000000000000001",
			expected: "1",
		}, {
			name:     "overflow in word five",
			in1:      "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141",
			in2:      "10000000000000000000000000000000000000001",
			expected: "1",
		}, {
			name:     "overflow in word six",
			in1:      "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			in2:      "1000000000000000000000000000000000000000000000001",
			expected: "1",
		}, {
			name:     "overflow in word seven",
			in1:      "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			in2:      "100000000000000000000000000000000000000000000000000000001",
			expected: "1",
		}, {
			name:     "alternating bits",
			in1:      "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			in2:      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "14551231950b75fc4402da1732fc9bebe",
		}, {
			name:     "alternating bits 2",
			in1:      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			in2:      "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "14551231950b75fc4402da1732fc9bebe",
		},
	}
	for _, test := range tests {
		// Parse test hex.
		s1 := new(ModNScalar).SetHex(test.in1)
		s2 := new(ModNScalar).SetHex(test.in2)
		expected := new(ModNScalar).SetHex(test.expected)

		// Ensure the result has the expected value.
		s1.Add(s2)
		if !s1.Equals(expected) {
			t.Errorf(
				"%s: unexpected result\ngot: %x\nwant: %x", test.name,
				s1, expected,
			)
			continue
		}
	}
}

// TestModNScalarAddRandom ensures that adding two scalars together for random
// values works as expected by also performing the same operation with big ints
// and comparing the results.
func TestModNScalarAddRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Generate two big integer and mod n scalar pairs.
		bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng)
		bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng)

		// Calculate the sum of the values using big ints.
		bigIntResult := new(big.Int).Add(bigIntVal1, bigIntVal2)
		bigIntResult.Mod(bigIntResult, curveParams.N)
		// Calculate the sum of the values using mod n scalars.
		modNValResult := new(ModNScalar).Add2(modNVal1, modNVal2)
		// Ensure they match.
		bigIntResultHex := fmt.Sprintf("%064x", bigIntResult)
		modNResultHex := fmt.Sprintf("%v", modNValResult)
		if bigIntResultHex != modNResultHex {
			t.Fatalf(
				"mismatched add\nbig int in 1: %x\nbig int in 2: %x\n"+
					"scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+
					"result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2,
				bigIntResult, modNValResult,
			)
		}
	}
}

// TestAccumulator96Add ensures that the internal 96-bit accumulator used by
// multiplication works as expected for overflow edge cases including overflow.
func TestAccumulator96Add(t *testing.T) {
	tests := []struct {
		name     string        // test description
		start    accumulator96 // starting value of accumulator
		in       uint64        // value to add to accumulator
		expected accumulator96 // expected value of accumulator after addition
	}{
		{
			name:     "0 + 0 = 0",
			start:    accumulator96{[3]uint32{0, 0, 0}},
			in:       0,
			expected: accumulator96{[3]uint32{0, 0, 0}},
		}, {
			name:     "overflow in word zero",
			start:    accumulator96{[3]uint32{0xffffffff, 0, 0}},
			in:       1,
			expected: accumulator96{[3]uint32{0, 1, 0}},
		}, {
			name:     "overflow in word one",
			start:    accumulator96{[3]uint32{0, 0xffffffff, 0}},
			in:       0x100000000,
			expected: accumulator96{[3]uint32{0, 0, 1}},
		}, {
			name:     "overflow in words one and two",
			start:    accumulator96{[3]uint32{0xffffffff, 0xffffffff, 0}},
			in:       1,
			expected: accumulator96{[3]uint32{0, 0, 1}},
		}, {
			// Start accumulator at 129127208455837319175 which is the result of
			// 4294967295 * 4294967295 accumulated seven times.
			name:     "max result from eight adds of max uint32 multiplications",
			start:    accumulator96{[3]uint32{7, 4294967282, 6}},
			in:       18446744065119617025,
			expected: accumulator96{[3]uint32{8, 4294967280, 7}},
		},
	}
	for _, test := range tests {
		acc := test.start
		acc.Add(test.in)
		if acc.n != test.expected.n {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, acc.n,
				test.expected.n,
			)
		}
	}
}

// TestModNScalarMul ensures that multiplying two scalars together works as
// expected for edge cases.
func TestModNScalarMul(t *testing.T) {
	tests := []struct {
		name     string // test description
		in1      string // first hex encoded value
		in2      string // second hex encoded value to multiply with
		expected string // expected hex encoded value
	}{
		{
			name:     "zero * zero",
			in1:      "0",
			in2:      "0",
			expected: "0",
		}, {
			name:     "one * zero",
			in1:      "1",
			in2:      "0",
			expected: "0",
		}, {
			name:     "one * one",
			in1:      "1",
			in2:      "1",
			expected: "1",
		}, {
			name:     "(group order-1) * 2",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			in2:      "2",
			expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f",
		}, {
			name:     "(group order-1) * (group order-1)",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			in2:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			expected: "1",
		}, {
			name:     "slightly over group order",
			in1:      "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1",
			in2:      "2",
			expected: "1",
		}, {
			name:     "group order (aka 0) * 3",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
			in2:      "3",
			expected: "0",
		}, {
			name:     "overflow in word eight",
			in1:      "100000000000000000000000000000000",
			in2:      "100000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf",
		}, {
			name:     "overflow in word nine",
			in1:      "1000000000000000000000000000000000000",
			in2:      "1000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf00000000",
		}, {
			name:     "overflow in word ten",
			in1:      "10000000000000000000000000000000000000000",
			in2:      "10000000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf0000000000000000",
		}, {
			name:     "overflow in word eleven",
			in1:      "100000000000000000000000000000000000000000000",
			in2:      "100000000000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000",
		}, {
			name:     "overflow in word twelve",
			in1:      "1000000000000000000000000000000000000000000000000",
			in2:      "1000000000000000000000000000000000000000000000000",
			expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf",
		}, {
			name:     "overflow in word thirteen",
			in1:      "10000000000000000000000000000000000000000000000000000",
			in2:      "10000000000000000000000000000000000000000000000000000",
			expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7",
		}, {
			name:     "overflow in word fourteen",
			in1:      "100000000000000000000000000000000000000000000000000000000",
			in2:      "100000000000000000000000000000000000000000000000000000000",
			expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c",
		}, {
			name:     "overflow in word fifteen",
			in1:      "1000000000000000000000000000000000000000000000000000000000000",
			in2:      "1000000000000000000000000000000000000000000000000000000000000",
			expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd",
		}, {
			name:     "double overflow in internal accumulator",
			in1:      "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			in2:      "55555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c2",
			expected: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa9d1c9e899ca306ad27fe1945de0242b7f",
		}, {
			name:     "alternating bits",
			in1:      "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			in2:      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838",
		}, {
			name:     "alternating bits 2",
			in1:      "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			in2:      "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "88edea3d29272800e7988455cfdf19b039dbfbb1c93b5b44a48c2ba462316838",
		},
	}
	for _, test := range tests {
		v1 := new(ModNScalar).SetHex(test.in1)
		v2 := new(ModNScalar).SetHex(test.in2)
		expected := new(ModNScalar).SetHex(test.expected)
		// Ensure multiplying two other values produces the expected result.
		result := new(ModNScalar).Mul2(v1, v2)
		if !result.Equals(expected) {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, result,
				expected,
			)
			continue
		}
		// Ensure self multiplying with another value also produces the expected
		// result.
		result2 := new(ModNScalar).Set(v1).Mul(v2)
		if !result2.Equals(expected) {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, result2,
				expected,
			)
			continue
		}
	}
}

// TestModNScalarMulRandom ensures that multiplying two scalars together for
// random values works as expected by also performing the same operation with
// big ints and comparing the results.
func TestModNScalarMulRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Generate two big integer and mod n scalar pairs.
		bigIntVal1, modNVal1 := randIntAndModNScalar(t, rng)
		bigIntVal2, modNVal2 := randIntAndModNScalar(t, rng)
		// Calculate the square of the value using big ints.
		bigIntResult := new(big.Int).Mul(bigIntVal1, bigIntVal2)
		bigIntResult.Mod(bigIntResult, curveParams.N)
		// Calculate the square of the value using mod n scalar.
		modNValResult := new(ModNScalar).Mul2(modNVal1, modNVal2)
		// Ensure they match.
		bigIntResultHex := fmt.Sprintf("%064x", bigIntResult)
		modNResultHex := fmt.Sprintf("%v", modNValResult)
		if bigIntResultHex != modNResultHex {
			t.Fatalf(
				"mismatched mul\nbig int in 1: %x\nbig int in 2: %x\n"+
					"scalar in 1: %v\nscalar in 2: %v\nbig int result: %x\nscalar "+
					"result %v", bigIntVal1, bigIntVal2, modNVal1, modNVal2,
				bigIntResult, modNValResult,
			)
		}
	}
}

// TestModNScalarSquare ensures that squaring scalars works as expected for edge
// cases.
func TestModNScalarSquare(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded test value
		expected string // expected hex encoded value
	}{
		{
			name:     "zero",
			in:       "0",
			expected: "0",
		}, {
			name:     "one",
			in:       "1",
			expected: "1",
		}, {
			name:     "over group order",
			in:       "0000000000000000000000000000000100000000000000000000000000000000",
			expected: "000000000000000000000000000000014551231950b75fc4402da1732fc9bebf",
		}, {
			name:     "group order - 1",
			in:       "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
			expected: "0000000000000000000000000000000000000000000000000000000000000001",
		}, {
			name:     "overflow in word eight",
			in:       "100000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf",
		}, {
			name:     "overflow in word nine",
			in:       "1000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf00000000",
		}, {
			name:     "overflow in word ten",
			in:       "10000000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf0000000000000000",
		}, {
			name:     "overflow in word eleven",
			in:       "100000000000000000000000000000000000000000000",
			expected: "14551231950b75fc4402da1732fc9bebf000000000000000000000000",
		}, {
			name:     "overflow in word twelve",
			in:       "1000000000000000000000000000000000000000000000000",
			expected: "4551231950b75fc4402da1732fc9bec04551231950b75fc4402da1732fc9bebf",
		}, {
			name:     "overflow in word thirteen",
			in:       "10000000000000000000000000000000000000000000000000000",
			expected: "50b75fc4402da1732fc9bec09d671cd51b343a1b66926b57d2a4c1c61536bda7",
		}, {
			name:     "overflow in word fourteen",
			in:       "100000000000000000000000000000000000000000000000000000000",
			expected: "402da1732fc9bec09d671cd581c69bc59509b0b074ec0aea8f564d667ec7eb3c",
		}, {
			name:     "overflow in word fifteen",
			in:       "1000000000000000000000000000000000000000000000000000000000000",
			expected: "2fc9bec09d671cd581c69bc5e697f5e41f12c33a0a7b6f4e3302b92ea029cecd",
		}, {
			name:     "alternating bits",
			in:       "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "fb0982c5761d1eac534247f2a7c3af186a134d709b977ca88300faad5eafe9bc",
		}, {
			name:     "alternating bits 2",
			in:       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "9081c595b95b2d17c424a546144b25488104c5889d914635bc9d1a51859e1c19",
		},
	}
	for _, test := range tests {
		v := new(ModNScalar).SetHex(test.in)
		expected := new(ModNScalar).SetHex(test.expected)
		// Ensure squaring another value produces the expected result.
		result := new(ModNScalar).SquareVal(v)
		if !result.Equals(expected) {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, result,
				expected,
			)
			continue
		}
		// Ensure self squaring also produces the expected result.
		result2 := new(ModNScalar).Set(v).Square()
		if !result2.Equals(expected) {
			t.Errorf(
				"%s: wrong result\ngot: %v\nwant: %v", test.name, result2,
				expected,
			)
			continue
		}
	}
}

// TestModNScalarSquareRandom ensures that squaring scalars for random values
// works as expected by also performing the same operation with big ints and
// comparing the results.
func TestModNScalarSquareRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Generate big integer and mod n scalar with the same random value.
		bigIntVal, modNVal := randIntAndModNScalar(t, rng)
		// Calculate the square of the value using big ints.
		bigIntResult := new(big.Int).Mul(bigIntVal, bigIntVal)
		bigIntResult.Mod(bigIntResult, curveParams.N)
		// Calculate the square of the value using mod n scalar.
		modNValResult := new(ModNScalar).SquareVal(modNVal)
		// Ensure they match.
		bigIntResultHex := fmt.Sprintf("%064x", bigIntResult)
		modNResultHex := fmt.Sprintf("%v", modNValResult)
		if bigIntResultHex != modNResultHex {
			t.Fatalf(
				"mismatched square\nbig int in: %x\nscalar in: %v\n"+
					"big int result: %x\nscalar result %v", bigIntVal, modNVal,
				bigIntResult, modNValResult,
			)
		}
	}
}

// TestModNScalarNegate ensures that negating scalars works as expected for edge
// cases.
func TestModNScalarNegate(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded test value
		expected string // hex encoded expected result
	}{
		{
			name:     "zero",
			in:       "0",
			expected: "0",
		}, {
			name:     "one",
			in:       "1",
			expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
		}, {
			name:     "negation in word one",
			in:       "0000000000000000000000000000000000000000000000000000000100000000",
			expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8bd0364141",
		}, {
			name:     "negation in word two",
			in:       "0000000000000000000000000000000000000000000000010000000000000000",
			expected: "fffffffffffffffffffffffffffffffebaaedce6af48a03abfd25e8cd0364141",
		}, {
			name:     "negation in word three",
			in:       "0000000000000000000000000000000000000001000000000000000000000000",
			expected: "fffffffffffffffffffffffffffffffebaaedce5af48a03bbfd25e8cd0364141",
		}, {
			name:     "negation in word four",
			in:       "0000000000000000000000000000000100000000000000000000000000000000",
			expected: "fffffffffffffffffffffffffffffffdbaaedce6af48a03bbfd25e8cd0364141",
		}, {
			name:     "negation in word five",
			in:       "0000000000000000000000010000000000000000000000000000000000000000",
			expected: "fffffffffffffffffffffffefffffffebaaedce6af48a03bbfd25e8cd0364141",
		}, {
			name:     "negation in word six",
			in:       "0000000000000001000000000000000000000000000000000000000000000000",
			expected: "fffffffffffffffefffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
		}, {
			name:     "negation in word seven",
			in:       "0000000100000000000000000000000000000000000000000000000000000000",
			expected: "fffffffefffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
		}, {
			name:     "alternating bits",
			in:       "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a591509374109a2fa961a2cb8e72a909b9c",
		}, {
			name:     "alternating bits 2",
			in:       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a46054828c54ee45e16578043275dbe6e7",
		},
	}
	for _, test := range tests {
		s := new(ModNScalar).SetHex(test.in)
		expected := new(ModNScalar).SetHex(test.expected)
		// Ensure negating another value produces the expected result.
		result := new(ModNScalar).NegateVal(s)
		if !result.Equals(expected) {
			t.Errorf(
				"%s: unexpected result -- got: %v, want: %v", test.name,
				result, expected,
			)
			continue
		}
		// Ensure self negating also produces the expected result.
		result2 := new(ModNScalar).Set(s).Negate()
		if !result2.Equals(expected) {
			t.Errorf(
				"%s: unexpected result -- got: %v, want: %v", test.name,
				result2, expected,
			)
			continue
		}
	}
}

// TestModNScalarNegateRandom ensures that negating scalars for random values
// works as expected by also performing the same operation with big ints and
// comparing the results.
func TestModNScalarNegateRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Generate big integer and mod n scalar with the same random value.
		bigIntVal, modNVal := randIntAndModNScalar(t, rng)
		// Calculate the negation of the value using big ints.
		bigIntResult := new(big.Int).Neg(bigIntVal)
		bigIntResult.Mod(bigIntResult, curveParams.N)
		// Calculate the negation of the value using mod n scalar.
		modNValResult := new(ModNScalar).NegateVal(modNVal)
		// Ensure they match.
		bigIntResultHex := fmt.Sprintf("%064x", bigIntResult)
		modNResultHex := fmt.Sprintf("%v", modNValResult)
		if bigIntResultHex != modNResultHex {
			t.Fatalf(
				"mismatched negate\nbig int in: %x\nscalar in: %v\n"+
					"big int result: %x\nscalar result %v", bigIntVal, modNVal,
				bigIntResult, modNValResult,
			)
		}
	}
}

// TestModNScalarInverseNonConst ensures that calculating the multiplicative
// inverse of scalars in *non-constant* time works as expected for edge cases.
func TestModNScalarInverseNonConst(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded test value
		expected string // hex encoded expected result
	}{
		{
			name:     "zero",
			in:       "0",
			expected: "0",
		}, {
			name:     "one",
			in:       "1",
			expected: "1",
		}, {
			name:     "inverse carry in word one",
			in:       "0000000000000000000000000000000000000000000000000000000100000000",
			expected: "5588b13effffffffffffffffffffffff934e5b00ca8417bf50177f7ba415411a",
		}, {
			name:     "inverse carry in word two",
			in:       "0000000000000000000000000000000000000000000000010000000000000000",
			expected: "4b0dff665588b13effffffffffffffffa09f710af01555259d4ad302583de6dc",
		}, {
			name:     "inverse carry in word three",
			in:       "0000000000000000000000000000000000000001000000000000000000000000",
			expected: "34b9ec244b0dff665588b13effffffffbcff4127932a971a78274c9d74176b38",
		}, {
			name:     "inverse carry in word four",
			in:       "0000000000000000000000000000000100000000000000000000000000000000",
			expected: "50a51ac834b9ec244b0dff665588b13e9984d5b3cf80ef0fd6a23766a3ee9f22",
		}, {
			name:     "inverse carry in word five",
			in:       "0000000000000000000000010000000000000000000000000000000000000000",
			expected: "27cfab5e50a51ac834b9ec244b0dff6622f16e85b683d5a059bcd5a3b29d9dff",
		}, {
			name:     "inverse carry in word six",
			in:       "0000000000000001000000000000000000000000000000000000000000000000",
			expected: "897f30c127cfab5e50a51ac834b9ec239c53f268b4700c14f19b9499ac58d8ad",
		}, {
			name:     "inverse carry in word seven",
			in:       "0000000100000000000000000000000000000000000000000000000000000000",
			expected: "6494ef93897f30c127cfab5e50a51ac7b4e8f713e0cddd182234e907286ae6b3",
		}, {
			name:     "alternating bits",
			in:       "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
			expected: "cb6086e560b8597a85c934e46f5b6e8a445bf3f0a88e4160d7fa8d83fd10338d",
		}, {
			name:     "alternating bits 2",
			in:       "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
			expected: "9f864ca486a74eb5f546364d76d24aa93716dc78f84847aa6c1c09fca2707d77",
		},
	}
	for _, test := range tests {
		s := new(ModNScalar).SetHex(test.in)
		expected := new(ModNScalar).SetHex(test.expected)
		// Ensure calculating the multiplicative inverse of another value
		// produces the expected result.
		result := new(ModNScalar).InverseValNonConst(s)
		if !result.Equals(expected) {
			t.Errorf(
				"%s: unexpected result -- got: %v, want: %v", test.name,
				result, expected,
			)
			continue
		}
		// Ensure calculating the multiplicative inverse in place also produces
		// the expected result.
		result2 := new(ModNScalar).Set(s).InverseNonConst()
		if !result2.Equals(expected) {
			t.Errorf(
				"%s: unexpected result -- got: %v, want: %v", test.name,
				result2, expected,
			)
			continue
		}
	}
}

// TestModNScalarInverseNonConstRandom ensures that calculating the
// multiplicative inverse of scalars in *non-constant* time for random values
// works as expected by also performing the same operation with big ints and
// comparing the results.
func TestModNScalarInverseNonConstRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	for i := 0; i < 100; i++ {
		// Generate big integer and mod n scalar with the same random value.
		bigIntVal, modNVal := randIntAndModNScalar(t, rng)
		// Calculate the inverse of the value using big ints.
		bigIntResult := new(big.Int).ModInverse(bigIntVal, curveParams.N)
		// Calculate the inverse of the value using a mod n scalar.
		modNValResult := new(ModNScalar).InverseValNonConst(modNVal)
		// Ensure they match.
		bigIntResultHex := fmt.Sprintf("%064x", bigIntResult)
		modNResultHex := fmt.Sprintf("%v", modNValResult)
		if bigIntResultHex != modNResultHex {
			t.Fatalf(
				"mismatched inverse\nbig int in: %x\nscalar in: %v\n"+
					"big int result: %x\nscalar result %v", bigIntVal, modNVal,
				bigIntResult, modNValResult,
			)
		}
	}
}

// TestModNScalarIsOverHalfOrder ensures that scalars report whether or not they
// exceeed the half order works as expected for edge cases.
func TestModNScalarIsOverHalfOrder(t *testing.T) {
	tests := []struct {
		name     string // test description
		in       string // hex encoded test value
		expected bool   // expected result
	}{
		{
			name:     "zero",
			in:       "0",
			expected: false,
		}, {
			name:     "one",
			in:       "1",
			expected: false,
		}, {
			name:     "group half order - 1",
			in:       "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b209f",
			expected: false,
		}, {
			name:     "group half order",
			in:       "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0",
			expected: false,
		}, {
			name:     "group half order + 1",
			in:       "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1",
			expected: true,
		}, {
			name:     "over half order word one",
			in:       "7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f47681b20a0",
			expected: true,
		}, {
			name:     "over half order word two",
			in:       "7fffffffffffffffffffffffffffffff5d576e7357a4501edfe92f46681b20a0",
			expected: true,
		}, {
			name:     "over half order word three",
			in:       "7fffffffffffffffffffffffffffffff5d576e7457a4501ddfe92f46681b20a0",
			expected: true,
		}, {
			name:     "over half order word seven",
			in:       "8fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0",
			expected: true,
		},
	}
	for _, test := range tests {
		result := new(ModNScalar).SetHex(test.in).IsOverHalfOrder()
		if result != test.expected {
			t.Errorf(
				"%s: unexpected result -- got: %v, want: %v", test.name,
				result, test.expected,
			)
			continue
		}
	}
}

// TestModNScalarIsOverHalfOrderRandom ensures that scalars report whether or
// not they exceeed the half order for random values works as expected by also
// performing the same operation with big ints and comparing the results.
func TestModNScalarIsOverHalfOrderRandom(t *testing.T) {
	// Use a unique random seed each test instance and log it if the tests fail.
	seed := time.Now().Unix()
	rng := rand.New(rand.NewSource(seed))
	defer func(t *testing.T, seed int64) {
		if t.Failed() {
			t.Logf("random seed: %d", seed)
		}
	}(t, seed)
	bigHalfOrder := new(big.Int).Rsh(curveParams.N, 1)
	for i := 0; i < 100; i++ {
		// Generate big integer and mod n scalar with the same random value.
		bigIntVal, modNVal := randIntAndModNScalar(t, rng)
		// Determine the value exceeds the half order using big ints.
		bigIntResult := bigIntVal.Cmp(bigHalfOrder) > 0
		// Determine the value exceeds the half order using a mod n scalar.
		modNValResult := modNVal.IsOverHalfOrder()
		// Ensure they match.
		if bigIntResult != modNValResult {
			t.Fatalf(
				"mismatched is over half order\nbig int in: %x\nscalar "+
					"in: %v\nbig int result: %v\nscalar result %v", bigIntVal,
				modNVal, bigIntResult, modNValResult,
			)
		}
	}
}
