func miller(q *twistPoint, p *curvePoint, pool *bnPool) *gfP12 {
	ret := newGFp12(pool)
	ret.SetOne()
	aAffine := newTwistPoint(pool)
	aAffine.Set(q)
	aAffine.MakeAffine(pool)
	bAffine := newCurvePoint(pool)
	bAffine.Set(p)
	bAffine.MakeAffine(pool)
	minusA := newTwistPoint(pool)
	minusA.Negative(aAffine, pool)
	r := newTwistPoint(pool)
	r.Set(aAffine)
	r2 := newGFp2(pool)
	r2.Square(aAffine.y, pool)
	for i := len(sixuPlus2NAF) - 1; i > 0; i-- {
		a, b, c, newR := lineFunctionDouble(r, bAffine, pool)
		if i != len(sixuPlus2NAF)-1 {
			ret.Square(ret, pool)
		}
		mulLine(ret, a, b, c, pool)
		a.Put(pool)
		b.Put(pool)
		c.Put(pool)
		r.Put(pool)
		r = newR
		switch sixuPlus2NAF[i-1] {
		case 1:
			a, b, c, newR = lineFunctionAdd(r, aAffine, bAffine, r2, pool)
		case -1:
			a, b, c, newR = lineFunctionAdd(r, minusA, bAffine, r2, pool)
		default:
			continue
		}
		mulLine(ret, a, b, c, pool)
		a.Put(pool)
		b.Put(pool)
		c.Put(pool)
		r.Put(pool)
		r = newR
	}
	q1 := newTwistPoint(pool)
	q1.x.Conjugate(aAffine.x)
	q1.x.Mul(q1.x, xiToPMinus1Over3, pool)
	q1.y.Conjugate(aAffine.y)
	q1.y.Mul(q1.y, xiToPMinus1Over2, pool)
	q1.z.SetOne()
	q1.t.SetOne()
	minusQ2 := newTwistPoint(pool)
	minusQ2.x.MulScalar(aAffine.x, xiToPSquaredMinus1Over3)
	minusQ2.y.Set(aAffine.y)
	minusQ2.z.SetOne()
	minusQ2.t.SetOne()
	r2.Square(q1.y, pool)
	a, b, c, newR := lineFunctionAdd(r, q1, bAffine, r2, pool)
	mulLine(ret, a, b, c, pool)
	a.Put(pool)
	b.Put(pool)
	c.Put(pool)
	r.Put(pool)
	r = newR
	r2.Square(minusQ2.y, pool)
	a, b, c, newR = lineFunctionAdd(r, minusQ2, bAffine, r2, pool)
	mulLine(ret, a, b, c, pool)
	a.Put(pool)
	b.Put(pool)
	c.Put(pool)
	r.Put(pool)
	r = newR
	aAffine.Put(pool)
	bAffine.Put(pool)
	minusA.Put(pool)
	r.Put(pool)
	r2.Put(pool)
	return ret
}
miller implements the Miller loop for calculating the Optimal Ate pairing.
See algorithm 1 from http://cryptojedi.org/papers/dclxvi-20100714.pdf
package bn256
func lineFunctionAdd(r, p *twistPoint, q *curvePoint, r2 *gfP2, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
	B := newGFp2(pool).Mul(p.x, r.t, pool)
	D := newGFp2(pool).Add(p.y, r.z)
	D.Square(D, pool)
	D.Sub(D, r2)
	D.Sub(D, r.t)
	D.Mul(D, r.t, pool)
	H := newGFp2(pool).Sub(B, r.x)
	I := newGFp2(pool).Square(H, pool)
	E := newGFp2(pool).Add(I, I)
	E.Add(E, E)
	J := newGFp2(pool).Mul(H, E, pool)
	L1 := newGFp2(pool).Sub(D, r.y)
	L1.Sub(L1, r.y)
	V := newGFp2(pool).Mul(r.x, E, pool)
	rOut = newTwistPoint(pool)
	rOut.x.Square(L1, pool)
	rOut.x.Sub(rOut.x, J)
	rOut.x.Sub(rOut.x, V)
	rOut.x.Sub(rOut.x, V)
	rOut.z.Add(r.z, H)
	rOut.z.Square(rOut.z, pool)
	rOut.z.Sub(rOut.z, r.t)
	rOut.z.Sub(rOut.z, I)
	t := newGFp2(pool).Sub(V, rOut.x)
	t.Mul(t, L1, pool)
	t2 := newGFp2(pool).Mul(r.y, J, pool)
	t2.Add(t2, t2)
	rOut.y.Sub(t, t2)
	rOut.t.Square(rOut.z, pool)
	t.Add(p.y, rOut.z)
	t.Square(t, pool)
	t.Sub(t, r2)
	t.Sub(t, rOut.t)
	t2.Mul(L1, p.x, pool)
	t2.Add(t2, t2)
	a = newGFp2(pool)
	a.Sub(t2, t)
	c = newGFp2(pool)
	c.MulScalar(rOut.z, q.y)
	c.Add(c, c)
	b = newGFp2(pool)
	b.SetZero()
	b.Sub(b, L1)
	b.MulScalar(b, q.x)
	b.Add(b, b)
	B.Put(pool)
	D.Put(pool)
	H.Put(pool)
	I.Put(pool)
	E.Put(pool)
	J.Put(pool)
	L1.Put(pool)
	V.Put(pool)
	t.Put(pool)
	t2.Put(pool)
	return
}
func lineFunctionDouble(r *twistPoint, q *curvePoint, pool *bnPool) (a, b, c *gfP2, rOut *twistPoint) {
	A := newGFp2(pool).Square(r.x, pool)
	B := newGFp2(pool).Square(r.y, pool)
	C_ := newGFp2(pool).Square(B, pool)
	D := newGFp2(pool).Add(r.x, B)
	D.Square(D, pool)
	D.Sub(D, A)
	D.Sub(D, C_)
	D.Add(D, D)
	E := newGFp2(pool).Add(A, A)
	E.Add(E, A)
	G := newGFp2(pool).Square(E, pool)
	rOut = newTwistPoint(pool)
	rOut.x.Sub(G, D)
	rOut.x.Sub(rOut.x, D)
	rOut.z.Add(r.y, r.z)
	rOut.z.Square(rOut.z, pool)
	rOut.z.Sub(rOut.z, B)
	rOut.z.Sub(rOut.z, r.t)
	rOut.y.Sub(D, rOut.x)
	rOut.y.Mul(rOut.y, E, pool)
	t := newGFp2(pool).Add(C_, C_)
	t.Add(t, t)
	t.Add(t, t)
	rOut.y.Sub(rOut.y, t)
	rOut.t.Square(rOut.z, pool)
	t.Mul(E, r.t, pool)
	t.Add(t, t)
	b = newGFp2(pool)
	b.SetZero()
	b.Sub(b, t)
	b.MulScalar(b, q.x)
	a = newGFp2(pool)
	a.Add(r.x, E)
	a.Square(a, pool)
	a.Sub(a, A)
	a.Sub(a, G)
	t.Add(B, B)
	t.Add(t, t)
	a.Sub(a, t)
	c = newGFp2(pool)
	c.Mul(rOut.z, r.t, pool)
	c.Add(c, c)
	c.MulScalar(c, q.y)
	A.Put(pool)
	B.Put(pool)
	C_.Put(pool)
	D.Put(pool)
	E.Put(pool)
	G.Put(pool)
	t.Put(pool)
	return
}
func mulLine(ret *gfP12, a, b, c *gfP2, pool *bnPool) {
	a2 := newGFp6(pool)
	a2.x.SetZero()
	a2.y.Set(a)
	a2.z.Set(b)
	a2.Mul(a2, ret.x, pool)
	t3 := newGFp6(pool).MulScalar(ret.y, c, pool)
	t := newGFp2(pool)
	t.Add(b, c)
	t2 := newGFp6(pool)
	t2.x.SetZero()
	t2.y.Set(a)
	t2.z.Set(t)
	ret.x.Add(ret.x, ret.y)
	ret.y.Set(t3)
	ret.x.Mul(ret.x, t2, pool)
	ret.x.Sub(ret.x, a2)
	ret.x.Sub(ret.x, ret.y)
	a2.MulTau(a2, pool)
	ret.y.Add(ret.y, a2)
	a2.Put(pool)
	t3.Put(pool)
	t2.Put(pool)
	t.Put(pool)
}
var sixuPlus2NAF = []int8{0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
	0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
	1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
	1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1}