func Attack(ks []*keys.RSA) error {
	var (
		k     = ks[0]
		res   = new(fmp.Fmpz)
		state = new(fmp.FlintRandT)
		ch    = make(chan bool)
		ctx   = context.Background()
	)
	ctx, cancel := context.WithTimeout(ctx, timeout)
	defer cancel()
	if k.Verbose {
		log.Printf("%s attempt beginning with timeout %v", name, timeout)
	}
	go ecm(ch, k.Key.N, res, state)
	select {
	case <-ch:
		k.PackGivenP(res)
		return nil
	case <-ctx.Done():
		return fmt.Errorf("%s failed - no factors found", name)
	}
}
Attack implements the ECM factorization method.
package ecm
import (
	"context"
	"fmt"
	"log"
	"time"
	"github.com/jbarham/primegen"
	"github.com/sourcekris/goRsaTool/keys"
	"github.com/sourcekris/goRsaTool/ln"
	fmp "github.com/sourcekris/goflint"
)
var timeout = time.Minute * 5
const name = "ecm factorization"
type point struct {
	x, y *fmp.Fmpz
}
func (p point) String() string {
	return fmt.Sprintf("point{%v, %v}", p.x, p.y)
}
func (p point) Zero() bool {
	return p.x.IsZero() && p.y.IsZero()
}
func (p point) Check(n, a *fmp.Fmpz) bool {
	if p.Zero() {
		return true
	}
	lhs := new(fmp.Fmpz).ExpXI(p.y, 2)
	rhs := new(fmp.Fmpz).ExpXI(p.x, 2).AddZ(a).MulZ(p.x).AddZ(ln.BigOne)
	return new(fmp.Fmpz).Sub(lhs, rhs).ModZ(n).IsZero()
}
func (p point) Equals(q point) bool {
	return p.x.Equals(q.x) && p.y.Equals(q.y)
}
func (p point) Add(ch chan bool, q point, n, a, res *fmp.Fmpz) point {
	if p.Zero() {
		return q
	}
	if q.Zero() {
		return p
	}
	var num, denom *fmp.Fmpz
	if !p.x.Equals(q.x) {
		num = new(fmp.Fmpz).Sub(p.y, q.y)
		denom = new(fmp.Fmpz).Sub(p.x, q.x)
	} else if p.y.Equals(q.y) && !p.y.IsZero() {
		num = new(fmp.Fmpz).ExpXI(p.x, 2).MulZ(ln.BigThree).AddZ(a)
		denom = new(fmp.Fmpz).Mul(p.y, ln.BigTwo)
	} else {
		return point{fmp.NewFmpz(0), fmp.NewFmpz(0)}
	}
	denom.ModZ(n)
	f := new(fmp.Fmpz).GCD(denom, n)
	if !f.Equals(ln.BigOne) {
		res.Set(f)
		ch <- true
	}
	s := new(fmp.Fmpz).Mul(num, new(fmp.Fmpz).ModInverse(denom, n)).ModZ(n)
	rx := new(fmp.Fmpz).ExpXI(s, 2).SubZ(p.x).SubZ(q.x).ModZ(n)
	ry := new(fmp.Fmpz).Mul(s, new(fmp.Fmpz).Sub(p.x, rx)).SubZ(p.y).ModZ(n)
	return point{rx, ry}
}
func (p point) Mul(ch chan bool, k uint64, n, a, res *fmp.Fmpz) point {
	q := point{fmp.NewFmpz(0), fmp.NewFmpz(0)}
	for ; k > 1; k >>= 1 {
		if k&1 != 0 {
			q = q.Add(ch, p, n, a, res)
		}
		p = p.Add(ch, p, n, a, res)
	}
	return q.Add(ch, p, n, a, res)
}
func ecm(ch chan bool, n, res *fmp.Fmpz, state *fmp.FlintRandT) {
	pg := primegen.New()
	for {
		a := ln.GetRand(state, n)
		if new(fmp.Fmpz).ExpXI(a, 3).MulI(4).AddI(27).ModZ(n).IsZero() {
			continue
		}
		p := point{fmp.NewFmpz(0), fmp.NewFmpz(1)}
		for {
			p = p.Mul(ch, pg.Next(), n, a, res)
			if p.Zero() {
				break
			}
		}
	}
}