package bn256

import (
	"crypto/rand"
	"fmt"
	"testing"
	"time"
)

func TestLatticeReduceCurve(t *testing.T) {
	for {
		k, _ := rand.Int(rand.Reader, N)
		//for i:=255;i>100;i-- {
		//	k.SetBit(k, i, 0)
		//}
		ks := curveLattice.decompose(k)
		fmt.Println("============================================")
		fmt.Println("k = ", k.Text(16), k.BitLen(), k.Sign())
		fmt.Println("k1 = ", ks[0].Text(16), ks[0].BitLen(), ks[0].Sign())
		fmt.Println("k2 = ", ks[1].Text(16), ks[1].BitLen(), ks[1].Sign())
		if ks[0].BitLen() > 130 || ks[1].BitLen() > 130 {
			t.Fatal("reduction too large")
		} else if ks[0].Sign() < 0 || ks[1].Sign() < 0 {
			t.Fatal("reduction must be positive")
		}
	}
}

func TestLatticeReduceTarget(t *testing.T) {
	k, _ := rand.Int(rand.Reader, N)
	ks := targetLattice.decompose(k)

	if ks[0].BitLen() > 66 || ks[1].BitLen() > 66 || ks[2].BitLen() > 66 || ks[3].BitLen() > 66 {
		t.Fatal("reduction too large")
	} else if ks[0].Sign() < 0 || ks[1].Sign() < 0 || ks[2].Sign() < 0 || ks[3].Sign() < 0 {
		t.Fatal("reduction must be positive")
	}
}

func TestLatticeCurveMul(t *testing.T) {
	useLattice = false
	pt := &curvePoint{}
	t.Log("G   =", curveGen)
	l2 := bigFromBase10("82434016654578246432914077779442682275270229881604616279947255993657999048255")
	l := bigFromBase10("11916685325803286854045818138240527491926474132365765087461")
	pt.Mul(curveGen, l)
	t.Log("[l]G=", pt)
	pt.Add(curveGen, pt)
	t.Log("[l+1]G=", pt)
	pt.Neg(pt)
	t.Log("-[l+1]G=", pt)
	pt.Mul(curveGen, l2)
	t.Log("[l2]G=", pt)

	pt.Set(curveGen)
	gfpMul(&pt.x, &curveGen.x, xiTo2PMinus2Over3)
	t.Log("phiG=", pt)
	t.Log("phi(G) is on curve:", pt.IsOnCurve())

}

func TestLatticeTwistCurveMul(t *testing.T) {
	useLattice = false
	pt := &twistPoint{}
	t.Log("G   =", twistGen)
	l2 := bigFromBase10("82434016654578246432914077779442682275270229881604616279947255993657999048255")
	l := bigFromBase10("11916685325803286854045818138240527491926474132365765087461")
	pt.Mul(twistGen, l)
	t.Log("[l]G=", pt)
	pt.Add(twistGen, pt)
	t.Log("[l+1]G=", pt)
	pt.Neg(pt)
	t.Log("-[l+1]G=", pt)
	pt.Mul(twistGen, l2)
	t.Log("[l2]G=", pt)

	pt.Set(twistGen)
	//gfpMul(&pt.x, &twistGen.x, xiTo2PMinus2Over3)
	pt.x.MulScalar(&pt.x, xiTo2PSquaredMinus2Over3)
	t.Log("phiG=", pt)
	t.Log("phi(G) is on curve:", pt.IsOnCurve())

}

func TestTemp(t *testing.T) {
	useLattice = true
	l := bigFromBase10("11916685325803286854045818138240527491926474132365765087461")
	// a := bigFromBase10("18601171214415468628822298024872005604767796808132779597987639723831549415194")
	a := bigFromBase10("186011712144154686288222980248720056047677968081327795979876397238315495")
	//a = new(big.Int).Sub(Order,gmath.BigInt2)
	//multiScalar := curveLattice.Multi(a)
	decomp := curveLattice.decompose(a)
	fmt.Println(decomp)
	s := decomp[1]
	s.Mul(s, l)
	s.Add(s, decomp[0])
	s.Mod(s, N)
	fmt.Println(s)
	fmt.Println(a)

	pt1 := &curvePoint{}
	pt1.Mul(curveGen, a)
	fmt.Println(pt1)

	useLattice = false
	pt2 := &curvePoint{}
	pt2.Mul(curveGen, a)
	fmt.Println(pt2)

}

func TestTemp1(t *testing.T) {
	count := 0
	for {
		a, _ := rand.Int(rand.Reader, N)
		useLattice = true
		pt1 := &curvePoint{}
		pt1.Mul(curveGen, a)

		useLattice = false
		pt2 := &curvePoint{}
		pt2.Mul(curveGen, a)

		pt1.MakeAffine()
		pt2.MakeAffine()
		if *pt1 != *pt2 {
			fmt.Println(pt1)
			fmt.Println(pt2)
			t.Fail()
			return
		}
		count++
		if count%10000 == 0 {
			fmt.Println(count, "pass")
		}
	}
}

func TestGfP12Lattice(t *testing.T) {
	k, _ := rand.Int(rand.Reader, N)
	e := &gfP12{}
	f := &gfP12{}
	e.Exp(gfP12Gen, k)
	f.latticeExp(gfP12Gen, k)
	if *e != *f {
		t.Log(e)
		t.Log(f)
		t.Fatalf("bad lattice exponitial:")

	}

}

func TestGfP12LatticePairint(t *testing.T) {
	useLattice = true
	fmt.Println("Test bilinear")

	k1, p1, _ := RandomG1(rand.Reader)
	k2, p2, _ := RandomG2(rand.Reader)
	e1 := Pair(p1, p2)

	e2 := &GT{*gfP12Gen}
	e2.ScalarBaseMult(k1)
	e2.ScalarMult(e2, k2)

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

func BenchmarkLatticeGFP12(b *testing.B) {
	k, _ := rand.Int(rand.Reader, N)
	e := &gfP12{}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		e.latticeExp(gfP12Gen, k)
	}
}
func BenchmarkNormalLatticeGFP12(b *testing.B) {
	k, _ := rand.Int(rand.Reader, N)
	e := &gfP12{}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		e.Exp(gfP12Gen, k)
	}
}
func TestGfP12LatticeSpeed(t *testing.T) {
	k, _ := rand.Int(rand.Reader, N)
	e := &gfP12{}
	f := &gfP12{}
	total := 1000
	{
		//800
		begin := time.Now()
		for i := 0; i < total; i++ {
			e.Exp(gfP12Gen, k)
		}
		elaspe := time.Since(begin)
		fmt.Println("time: ", elaspe.Milliseconds(), "ms")
		fmt.Println(float64(total) / float64(elaspe.Milliseconds()) * 1000)
	}
	{
		// 1600 次/秒
		begin := time.Now()

		for i := 0; i < total; i++ {
			f.latticeExp(gfP12Gen, k)
		}
		elaspe := time.Since(begin)
		fmt.Println("time: ", elaspe.Milliseconds(), "ms")
		fmt.Println(float64(total) / float64(elaspe.Milliseconds()) * 1000)
	}
}
