func (p *G2Jac) WindowedMultiExp(curve *Curve, points []G2Jac, scalars []fr.Element) *G2Jac {
	var lock sync.Mutex
	parallel.Execute(0, len(points), func(start, end int) {
		var t G2Jac
		t.multiExp(curve, points[start:end], scalars[start:end])
		lock.Lock()
		p.Add(curve, &t)
		lock.Unlock()
	}, false)
	return p
}
WindowedMultiExp set p = scalars[0]*points[0] + ... + scalars[n]*points[n]
assume: scalars in non-Montgomery form!
assume: len(points)==len(scalars)>0, len(scalars[i]) equal for all i
algorithm: a special case of Pippenger described by Bootle:
https://jbootle.github.io/Misc/pippenger.pdf
uses all availables runtime.NumCPU()
package bn256
import (
	"runtime"
	"sync"
	"github.com/consensys/gurvy/bn256/fr"
	"github.com/consensys/gurvy/utils/debug"
	"github.com/consensys/gurvy/utils/parallel"
)
type G2Jac struct {
	X, Y, Z e2
}
type G2Affine struct {
	X, Y e2
}
type g2JacExtended struct {
	X, Y, ZZ, ZZZ e2
}
func (p *g2JacExtended) SetInfinity() *g2JacExtended {
	p.X.SetOne()
	p.Y.SetOne()
	p.ZZ.SetZero()
	p.ZZZ.SetZero()
	return p
}
func (p *g2JacExtended) ToAffine(Q *G2Affine) *G2Affine {
	Q.X.Inverse(&p.ZZ).MulAssign(&p.X)
	Q.Y.Inverse(&p.ZZZ).MulAssign(&p.Y)
	return Q
}
func (p *g2JacExtended) ToJac(Q *G2Jac) *G2Jac {
	Q.X.Mul(&p.ZZ, &p.X).MulAssign(&p.ZZ)
	Q.Y.Mul(&p.ZZZ, &p.Y).MulAssign(&p.ZZZ)
	Q.Z.Set(&p.ZZZ)
	return Q
}
func (p *g2JacExtended) mAdd(a *G2Affine) *g2JacExtended {
	if a.X.IsZero() && a.Y.IsZero() {
		return p
	}
	if p.ZZ.IsZero() {
		p.X = a.X
		p.Y = a.Y
		p.ZZ.SetOne()
		p.ZZZ.SetOne()
		return p
	}
	var U2, S2, P, R, PP, PPP, Q, Q2, RR, X3, Y3 e2
	U2.Mul(&a.X, &p.ZZ)
	S2.Mul(&a.Y, &p.ZZZ)
	if U2.Equal(&p.X) && S2.Equal(&p.Y) {
		return p.double(a)
	}
	P.Sub(&U2, &p.X)
	R.Sub(&S2, &p.Y)
	PP.Square(&P)
	PPP.Mul(&P, &PP)
	Q.Mul(&p.X, &PP)
	RR.Square(&R)
	X3.Sub(&RR, &PPP)
	Q2.AddAssign(&Q).AddAssign(&Q)
	p.X.Sub(&X3, &Q2)
	Y3.Sub(&Q, &p.X).MulAssign(&R)
	R.Mul(&p.Y, &PPP)
	p.Y.Sub(&Y3, &R)
	p.ZZ.MulAssign(&PP)
	p.ZZZ.MulAssign(&PPP)
	return p
}
func (p *g2JacExtended) double(q *G2Affine) *g2JacExtended {
	var U, S, M, _M, Y3 e2
	U.Double(&q.Y)
	p.ZZ.Square(&U)
	p.ZZZ.Mul(&U, &p.ZZ)
	S.Mul(&q.X, &p.ZZ)
	_M.Square(&q.X)
	M.Double(&_M).
		AddAssign(&_M)  
	p.X.Square(&M).
		SubAssign(&S).
		SubAssign(&S)
	Y3.Sub(&S, &p.X).MulAssign(&M)
	U.Mul(&p.ZZZ, &q.Y)
	p.Y.Sub(&Y3, &U)
	return p
}
func (p *G2Jac) Set(a *G2Jac) *G2Jac {
	p.X.Set(&a.X)
	p.Y.Set(&a.Y)
	p.Z.Set(&a.Z)
	return p
}
func (p *G2Jac) Equal(a *G2Jac) bool {
	if p.Z.IsZero() && a.Z.IsZero() {
		return true
	}
	_p := G2Affine{}
	p.ToAffineFromJac(&_p)
	_a := G2Affine{}
	a.ToAffineFromJac(&_a)
	return _p.X.Equal(&_a.X) && _p.Y.Equal(&_a.Y)
}
func (p *G2Affine) Equal(a *G2Affine) bool {
	return p.X.Equal(&a.X) && p.Y.Equal(&a.Y)
}
func (p *G2Jac) Clone() *G2Jac {
	return &G2Jac{
		p.X, p.Y, p.Z,
	}
}
func (p *G2Jac) Neg(a *G2Jac) *G2Jac {
	p.Set(a)
	p.Y.Neg(&a.Y)
	return p
}
func (p *G2Affine) Neg(a *G2Affine) *G2Affine {
	p.X.Set(&a.X)
	p.Y.Neg(&a.Y)
	return p
}
func (p *G2Jac) Sub(curve *Curve, a G2Jac) *G2Jac {
	a.Y.Neg(&a.Y)
	p.Add(curve, &a)
	return p
}
func (p *G2Jac) ToAffineFromJac(res *G2Affine) *G2Affine {
	var bufs [3]e2
	if p.Z.IsZero() {
		res.X.SetZero()
		res.Y.SetZero()
		return res
	}
	bufs[0].Inverse(&p.Z)
	bufs[2].Square(&bufs[0])
	bufs[1].Mul(&bufs[2], &bufs[0])
	res.Y.Mul(&p.Y, &bufs[1])
	res.X.Mul(&p.X, &bufs[2])
	return res
}
func (p *G2Jac) ToProjFromJac() *G2Jac {
	var buf e2
	buf.Square(&p.Z)
	p.X.Mul(&p.X, &p.Z)
	p.Z.Mul(&p.Z, &buf)
	return p
}
func (p *G2Jac) String(curve *Curve) string {
	if p.Z.IsZero() {
		return "O"
	}
	_p := G2Affine{}
	p.ToAffineFromJac(&_p)
	_p.X.FromMont()
	_p.Y.FromMont()
	return "E([" + _p.X.String() + "," + _p.Y.String() + "]),"
}
func (p *G2Affine) ToJacobian(Q *G2Jac) *G2Jac {
	if p.X.IsZero() && p.Y.IsZero() {
		Q.Z.SetZero()
		Q.X.SetOne()
		Q.Y.SetOne()
		return Q
	}
	Q.Z.SetOne()
	Q.X.Set(&p.X)
	Q.Y.Set(&p.Y)
	return Q
}
func (p *G2Affine) String(curve *Curve) string {
	var x, y e2
	x.Set(&p.X)
	y.Set(&p.Y)
	return "E([" + x.String() + "," + y.String() + "]),"
}
func (p *G2Affine) IsInfinity() bool {
	return p.X.IsZero() && p.Y.IsZero()
}
func (p *G2Jac) Add(curve *Curve, a *G2Jac) *G2Jac {
	if p.Z.IsZero() {
		p.Set(a)
		return p
	}
	if a.Z.IsZero() {
		return p
	}
	var Z1Z1, Z2Z2, U1, U2, S1, S2, H, I, J, r, V e2
	Z1Z1.Square(&a.Z)
	Z2Z2.Square(&p.Z)
	U1.Mul(&a.X, &Z2Z2)
	U2.Mul(&p.X, &Z1Z1)
	S1.Mul(&a.Y, &p.Z).
		MulAssign(&Z2Z2)
	S2.Mul(&p.Y, &a.Z).
		MulAssign(&Z1Z1)
	if U1.Equal(&U2) && S1.Equal(&S2) {
		return p.Double()
	}
	H.Sub(&U2, &U1)
	I.Double(&H).
		Square(&I)
	J.Mul(&H, &I)
	r.Sub(&S2, &S1).Double(&r)
	V.Mul(&U1, &I)
	p.X.Square(&r).
		SubAssign(&J).
		SubAssign(&V).
		SubAssign(&V)
	p.Y.Sub(&V, &p.X).
		MulAssign(&r)
	S1.MulAssign(&J).Double(&S1)
	p.Y.SubAssign(&S1)
	p.Z.AddAssign(&a.Z)
	p.Z.Square(&p.Z).
		SubAssign(&Z1Z1).
		SubAssign(&Z2Z2).
		MulAssign(&H)
	return p
}
func (p *G2Jac) AddMixed(a *G2Affine) *G2Jac {
	if a.X.IsZero() && a.Y.IsZero() {
		return p
	}
	if p.Z.IsZero() {
		p.X = a.X
		p.Y = a.Y
		p.Z.SetOne()
		return p
	}
	var Z1Z1, U2, S2, H, HH, I, J, r, V e2
	Z1Z1.Square(&p.Z)
	U2.Mul(&a.X, &Z1Z1)
	S2.Mul(&a.Y, &p.Z).
		MulAssign(&Z1Z1)
	if U2.Equal(&p.X) && S2.Equal(&p.Y) {
		return p.Double()
	}
	H.Sub(&U2, &p.X)
	HH.Square(&H)
	I.Double(&HH).Double(&I)
	J.Mul(&H, &I)
	r.Sub(&S2, &p.Y).Double(&r)
	V.Mul(&p.X, &I)
	p.X.Square(&r).
		SubAssign(&J).
		SubAssign(&V).
		SubAssign(&V)
	J.MulAssign(&p.Y).Double(&J)
	p.Y.Sub(&V, &p.X).
		MulAssign(&r)
	p.Y.SubAssign(&J)
	p.Z.AddAssign(&H)
	p.Z.Square(&p.Z).
		SubAssign(&Z1Z1).
		SubAssign(&HH)
	return p
}
func (p *G2Jac) Double() *G2Jac {
	var XX, YY, YYYY, ZZ, S, M, T e2
	XX.Square(&p.X)
	YY.Square(&p.Y)
	YYYY.Square(&YY)
	ZZ.Square(&p.Z)
	S.Add(&p.X, &YY)
	S.Square(&S).
		SubAssign(&XX).
		SubAssign(&YYYY).
		Double(&S)
	M.Double(&XX).AddAssign(&XX)
	p.Z.AddAssign(&p.Y).
		Square(&p.Z).
		SubAssign(&YY).
		SubAssign(&ZZ)
	T.Square(&M)
	p.X = T
	T.Double(&S)
	p.X.SubAssign(&T)
	p.Y.Sub(&S, &p.X).
		MulAssign(&M)
	YYYY.Double(&YYYY).Double(&YYYY).Double(&YYYY)
	p.Y.SubAssign(&YYYY)
	return p
}
func (p *G2Jac) ScalarMul(curve *Curve, a *G2Jac, scalar fr.Element) *G2Jac {
	const s = 4
	const b = s
	const TSize = (1 << b) - 1
	var T [TSize]G2Jac
	computeT := func(T []G2Jac, t0 *G2Jac) {
		T[0].Set(t0)
		for j := 1; j < (1<<b)-1; j = j + 2 {
			T[j].Set(&T[j/2]).Double()
			T[j+1].Set(&T[(j+1)/2]).Add(curve, &T[j/2])
		}
	}
	return p.pippenger(curve, []G2Jac{*a}, []fr.Element{scalar}, s, b, T[:], computeT)
}
func (p *G2Jac) ScalarMulByGen(curve *Curve, scalar fr.Element) *G2Jac {
	computeT := func(T []G2Jac, t0 *G2Jac) {}
	return p.pippenger(curve, []G2Jac{curve.g2Gen}, []fr.Element{scalar}, sGen, bGen, curve.tGenG2[:], computeT)
}