package bn256

import (
	"fmt"
	"testing"
	"time"

	"bytes"
	"crypto/rand"
)

// // return x,y
// func coordinates(e *G1) (*big.Int, *big.Int) {
// 	e.p.MakeAffine()
// 	temp := &gfP{}
// 	b := make([]byte, 32)
// 	montDecode(temp, &e.p.x)
// 	temp.Marshal(b)
// 	x := new(big.Int).SetBytes(b)
// 	montDecode(temp, &e.p.y)
// 	temp.Marshal(b)
// 	y := new(big.Int).SetBytes(b)
// 	return x, y
// }

func TestG1(t *testing.T) {
	k, Ga, err := RandomG1(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(G1).ScalarBaseMult(k)
	mb := Gb.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestG1UnmarshalCompressed(t *testing.T) {
	for i := 0; i < 10000; i++ {
		_, e1, err := RandomG1(rand.Reader)
		if err != nil {
			t.Fatal(err)
		}
		b := e1.Marshal()
		e2, err := (&G1{}).UnmarshalCompressed(b[:32], b[len(b)-1]&1)
		if err != nil {
			t.Fatal(err)
		}
		if !e1.p.Equal(&e2.p) {
			t.Fatal()
		}
	}
}

func TestG2UnmarshalCompressed(t *testing.T) {
	for i := 0; i < 10000; i++ {
		_, e1, err := RandomG2(rand.Reader)
		if err != nil {
			t.Fatal(err)
		}
		b := e1.Marshal()
		e2, err := (&G2{}).UnmarshalCompressed(b[:32], b[32:64], b[len(b)-1]&1)
		if err != nil {
			t.Fatal(err)
		}
		if !e1.p.Equal(&e2.p) {
			t.Fatal()
		}
	}
}

func TestG2FromX(t *testing.T) {
	n := newGFp(2)
	gfpNeg(n, n)
	for i := 0; i < 4; i++ {
		fmt.Printf("%x\n", n[i])
	}
}

func TestG1Marshal(t *testing.T) {
	_, Ga, err := RandomG1(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(G1)
	_, err = Gb.Unmarshal(ma)
	if err != nil {
		t.Fatal(err)
	}
	mb := Gb.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestG2(t *testing.T) {
	k, Ga, err := RandomG2(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(G2).ScalarBaseMult(k)
	mb := Gb.Marshal()
	if !bytes.Equal(ma, mb) {
		t.Fatal()
	}
}

func TestG2Marshal(t *testing.T) {
	_, Ga, err := RandomG2(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(G2)
	_, err = Gb.Unmarshal(ma)
	if err != nil {
		t.Fatal(err)
	}
	mb := Gb.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestGT(t *testing.T) {
	k, Ga, err := RandomGT(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(GT).ScalarBaseMult(k)
	mb := Gb.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal()
	}
}

func TestGTMarshal(t *testing.T) {
	_, Ga, err := RandomGT(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	Gb := new(GT)
	_, err = Gb.Unmarshal(ma)
	if err != nil {
		t.Fatal(err)
	}
	mb := Gb.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestBilinearity(t *testing.T) {
	for i := 0; i < 2; i++ {
		a, p1, _ := RandomG1(rand.Reader)
		b, p2, _ := RandomG2(rand.Reader)
		e1 := Pair(p1, p2)

		e2 := Pair(&G1{*curveGen}, &G2{*twistGen})
		e2.ScalarMult(e2, a)
		e2.ScalarMult(e2, b)

		if e1.p != e2.p {
			t.Log(e1)
			t.Log(e2)
			t.Fatalf("bad pairing result: %s", e1)
		}
	}
}

func TestTripartiteDiffieHellman(t *testing.T) {
	a, _ := rand.Int(rand.Reader, N)
	b, _ := rand.Int(rand.Reader, N)
	c, _ := rand.Int(rand.Reader, N)

	pa, pb, pc := new(G1), new(G1), new(G1)
	qa, qb, qc := new(G2), new(G2), new(G2)

	_, _ = pa.Unmarshal(new(G1).ScalarBaseMult(a).Marshal())
	_, _ = qa.Unmarshal(new(G2).ScalarBaseMult(a).Marshal())
	_, _ = pb.Unmarshal(new(G1).ScalarBaseMult(b).Marshal())
	_, _ = qb.Unmarshal(new(G2).ScalarBaseMult(b).Marshal())
	_, _ = pc.Unmarshal(new(G1).ScalarBaseMult(c).Marshal())
	_, _ = qc.Unmarshal(new(G2).ScalarBaseMult(c).Marshal())

	k1 := Pair(pb, qc)
	k1.ScalarMult(k1, a)
	k1Bytes := k1.Marshal()

	k2 := Pair(pc, qa)
	k2.ScalarMult(k2, b)
	k2Bytes := k2.Marshal()

	k3 := Pair(pa, qb)
	k3.ScalarMult(k3, c)
	k3Bytes := k3.Marshal()

	if !bytes.Equal(k1Bytes, k2Bytes) || !bytes.Equal(k2Bytes, k3Bytes) {
		t.Errorf("keys didn't agree")
	}
}

func TestSelfAddG1(t *testing.T) {
	_, Ga, err := RandomG1(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	Gb := &G1{*curveGen}
	Gb.p.Double(&Ga.p)
	mb := Gb.Marshal()

	Ga.Add(Ga, Ga)
	ma := Ga.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestSelfAddG2(t *testing.T) {
	_, Ga, err := RandomG2(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}

	Gb := &G2{*twistGen}
	Gb.p.Double(&Ga.p)
	mb := Gb.Marshal()

	Ga.Add(Ga, Ga)
	ma := Ga.Marshal()

	if !bytes.Equal(ma, mb) {
		t.Fatal("bytes are different")
	}
}

func TestDirtyUnmarshal(t *testing.T) {
	_, Ga, err := RandomG2(rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	ma := Ga.Marshal()

	if _, err := Ga.Unmarshal(ma); err != nil {
		t.Fatal(err)
	}
}

func TestPairing(T *testing.T) {
	begin := time.Now()
	total := 1

	for i := 0; i < total; i++ {
		e := Pair(&G1{*curveGen}, &G2{*twistGen})
		// fmt.Println("e:", gfP12Decode(e.p))
		_ = e
	}
	elaspe := time.Since(begin)
	fmt.Println("time: ", elaspe.Milliseconds(), "ms")
	fmt.Println(float64(total) / float64(elaspe.Milliseconds()) * 1000)
}
