package gmath

import (
	"crypto/rand"
	"math/big"

	"xdx.jelly/xgcl/grand"
)

const MRConst = 10

// IsPrimeMR tests if n is prime by the Miller-Rabin prime testing method.
func IsPrimeMR(n *big.Int) bool {
	return n.ProbablyPrime(MRConst)
}

// MillerRabin tests if n is prime by the Miller-Rabin prime testing method.
func MillerRabin(n *big.Int) bool {
	// n = 2^s * r
	n1 := new(big.Int)
	n1.Sub(n, BigInt1)
	r := new(big.Int).Set(n1)
	s := 0
	for r.Bit(0) == 0 {
		s++
		r = r.Rsh(r, 1)
	}

	for i := 0; i < MRConst; i++ {
		a, _ := rand.Int(grand.Reader, n1)

		//a = a^r mod n
		a.Exp(a, r, n)
		if a.Cmp(BigInt1) != 0 && a.Cmp(n1) != 0 {
			for j := 1; j < s && a.Cmp(n1) != 0; j++ {
				a.Mul(a, a)
				a.Mod(a, n)
				if a.Cmp(BigInt1) == 0 {
					return false
				}
			}
			if a.Cmp(n1) != 0 {
				return false
			}
		}
	}
	return true
}
