package common

import (
	"testing"
)

func BenchmarkDeriveNoise2(b *testing.B) {
	var p Poly
	var seed [32]byte
	for i := 0; i < b.N; i++ {
		p.DeriveNoise2(seed[:], 37)
	}
}

func BenchmarkDeriveNoise3(b *testing.B) {
	var p Poly
	var seed [32]byte
	for i := 0; i < b.N; i++ {
		p.DeriveNoise3(seed[:], 37)
	}
}

func TestPolyDeriveNoise3Ref(t *testing.T) {
	var p Poly

	want := Poly{
		0, 0, 1, -1, 0, 2, 0, -1, -1, 3, 0, 1, -2, -2, 0, 1, -2,
		1, 0, -2, 3, 0, 0, 0, 1, 3, 1, 1, 2, 1, -1, -1, -1, 0, 1,
		0, 1, 0, 2, 0, 1, -2, 0, -1, -1, -2, 1, -1, -1, 2, -1, 1,
		1, 2, -3, -1, -1, 0, 0, 0, 0, 1, -1, -2, -2, 0, -2, 0, 0,
		0, 1, 0, -1, -1, 1, -2, 2, 0, 0, 2, -2, 0, 1, 0, 1, 1, 1,
		0, 1, -2, -1, -2, -1, 1, 0, 0, 0, 0, 0, 1, 0, -1, -1, 0,
		-1, 1, 0, 1, 0, -1, -1, 0, -2, 2, 0, -2, 1, -1, 0, 1, -1,
		-1, 2, 1, 0, 0, -2, -1, 2, 0, 0, 0, -1, -1, 3, 1, 0, 1, 0,
		1, 0, 2, 1, 0, 0, 1, 0, 1, 0, 0, -1, -1, -1, 0, 1, 3, 1,
		0, 1, 0, 1, -1, -1, -1, -1, 0, 0, -2, -1, -1, 2, 0, 1, 0,
		1, 0, 2, -2, 0, 1, 1, -3, -1, -2, -1, 0, 1, 0, 1, -2, 2,
		2, 1, 1, 0, -1, 0, -1, -1, 1, 0, -1, 2, 1, -1, 1, 2, -2,
		1, 2, 0, 1, 2, 1, 0, 0, 2, 1, 2, 1, 0, 2, 1, 0, 0, -1, -1,
		1, -1, 0, 1, -1, 2, 2, 0, 0, -1, 1, 1, 1, 1, 0, 0, -2, 0,
		-1, 1, 2, 0, 0, 1, 1, -1, 1, 0, 1,
	}

	var seed [32]byte

	for i := 0; i < 32; i++ {
		seed[i] = byte(i)
	}

	p.DeriveNoise3(seed[:], 37)

	if p != want {
		t.Fatal()
	}
}

func TestPolyDeriveNoise2Ref(t *testing.T) {
	var p Poly

	want := Poly{
		1, 0, 1, -1, -1, -2, -1, -1, 2, 0, -1, 0, 0, -1,
		1, 1, -1, 1, 0, 2, -2, 0, 1, 2, 0, 0, -1, 1, 0, -1,
		1, -1, 1, 2, 1, 1, 0, -1, 1, -1, -2, -1, 1, -1, -1,
		-1, 2, -1, -1, 0, 0, 1, 1, -1, 1, 1, 1, 1, -1, -2,
		0, 1, 0, 0, 2, 1, -1, 2, 0, 0, 1, 1, 0, -1, 0, 0,
		-1, -1, 2, 0, 1, -1, 2, -1, -1, -1, -1, 0, -2, 0,
		2, 1, 0, 0, 0, -1, 0, 0, 0, -1, -1, 0, -1, -1, 0,
		-1, 0, 0, -2, 1, 1, 0, 1, 0, 1, 0, 1, 1, -1, 2, 0,
		1, -1, 1, 2, 0, 0, 0, 0, -1, -1, -1, 0, 1, 0, -1,
		2, 0, 0, 1, 1, 1, 0, 1, -1, 1, 2, 1, 0, 2, -1, 1,
		-1, -2, -1, -2, -1, 1, 0, -2, -2, -1, 1, 0, 0, 0,
		0, 1, 0, 0, 0, 2, 2, 0, 1, 0, -1, -1, 0, 2, 0, 0,
		-2, 1, 0, 2, 1, -1, -2, 0, 0, -1, 1, 1, 0, 0, 2,
		0, 1, 1, -2, 1, -2, 1, 1, 0, 2, 0, -1, 0, -1, 0,
		1, 2, 0, 1, 0, -2, 1, -2, -2, 1, -1, 0, -1, 1, 1,
		0, 0, 0, 1, 0, -1, 1, 1, 0, 0, 0, 0, 1, 0, 1, -1,
		0, 1, -1, -1, 2, 0, 0, 1, -1, 0, 1, -1, 0,
	}

	var seed [32]byte

	for i := 0; i < 32; i++ {
		seed[i] = byte(i)
	}

	p.DeriveNoise2(seed[:], 37)

	if p != want {
		t.Fatal()
	}
}

func TestPolyDeriveUniformRef(t *testing.T) {
	var p Poly

	// Generated by reference implementation.
	want := Poly{
		797, 993, 161, 6, 2608, 2385, 2096, 2661, 1676, 247, 2440,
		342, 634, 194, 1570, 2848, 986, 684, 3148, 3208, 2018, 351,
		2288, 612, 1394, 170, 1521, 3119, 58, 596, 2093, 1549, 409,
		2156, 1934, 1730, 1324, 388, 446, 418, 1719, 2202, 1812,
		98, 1019, 2369, 214, 2699, 28, 1523, 2824, 273, 402, 2899,
		246, 210, 1288, 863, 2708, 177, 3076, 349, 44, 949, 854,
		1371, 957, 292, 2502, 1617, 1501, 254, 7, 1761, 2581, 2206,
		2655, 1211, 629, 1274, 2358, 816, 2766, 2115, 2985, 1006,
		2433, 856, 2596, 3192, 1, 1378, 2345, 707, 1891, 1669, 536,
		1221, 710, 2511, 120, 1176, 322, 1897, 2309, 595, 2950,
		1171, 801, 1848, 695, 2912, 1396, 1931, 1775, 2904, 893,
		2507, 1810, 2873, 253, 1529, 1047, 2615, 1687, 831, 1414,
		965, 3169, 1887, 753, 3246, 1937, 115, 2953, 586, 545, 1621,
		1667, 3187, 1654, 1988, 1857, 512, 1239, 1219, 898, 3106,
		391, 1331, 2228, 3169, 586, 2412, 845, 768, 156, 662, 478,
		1693, 2632, 573, 2434, 1671, 173, 969, 364, 1663, 2701,
		2169, 813, 1000, 1471, 720, 2431, 2530, 3161, 733, 1691,
		527, 2634, 335, 26, 2377, 1707, 767, 3020, 950, 502, 426,
		1138, 3208, 2607, 2389, 44, 1358, 1392, 2334, 875, 2097,
		173, 1697, 2578, 942, 1817, 974, 1165, 2853, 1958, 2973,
		3282, 271, 1236, 1677, 2230, 673, 1554, 96, 242, 1729, 2518,
		1884, 2272, 71, 1382, 924, 1807, 1610, 456, 1148, 2479,
		2152, 238, 2208, 2329, 713, 1175, 1196, 757, 1078, 3190,
		3169, 708, 3117, 154, 1751, 3225, 1364, 154, 23, 2842, 1105,
		1419, 79, 5, 2013,
	}

	var seed [32]byte

	for i := 0; i < 32; i++ {
		seed[i] = byte(i)
	}

	p.DeriveUniform(&seed, 1, 0)
	p.Detangle()
	p.Normalize()

	if p != want {
		t.Fatalf("%v\n%v", p, want)
	}
}

func BenchmarkPolyDeriveUniform(b *testing.B) {
	var p Poly
	var seed [32]byte
	for i := 0; i < b.N; i++ {
		p.DeriveUniform(&seed, 0, 0)
	}
}

func BenchmarkPolyDeriveUniformX4(b *testing.B) {
	if !DeriveX4Available {
		b.SkipNow()
	}

	var p [4]Poly
	var seed [32]byte
	for i := 0; i < b.N; i++ {
		PolyDeriveUniformX4(
			[4]*Poly{&p[0], &p[1], &p[2], &p[3]},
			&seed,
			[4]uint8{0, 1, 2, 3},
			[4]uint8{4, 5, 6, 7},
		)
	}
}

func TestPolyDeriveUniformX4(t *testing.T) {
	if !DeriveX4Available {
		t.SkipNow()
	}

	var p2 Poly
	var p [4]Poly
	var seed [32]byte

	for i := 0; i < 32; i++ {
		seed[i] = byte(i)
	}

	PolyDeriveUniformX4(
		[4]*Poly{&p[0], &p[1], &p[2], &p[3]},
		&seed,
		[4]uint8{0, 1, 2, 3},
		[4]uint8{4, 5, 6, 7},
	)

	for i := 0; i < 4; i++ {
		p2.DeriveUniform(&seed, uint8(i), uint8(i+4))
		if p2 != p[i] {
			t.Fatalf("%d\n%v\n%v", i, p2, p[i])
		}
	}
}
