use crate::data::primes::PRIMELIST;
use crate::traits::NumberTheory;
use crate::Mpz;
use crate::NTResult;
use crate::arithmetic::sliceops::mod_slice;
use crate::arithmetic::sliceops::sub_slice;
impl Mpz {
pub(crate) fn sprp(&self, base: Self) -> bool {
let mut p_minus = self.clone();
let one = Mpz::one();
sub_slice(&mut p_minus.limbs[..], &one.limbs[..]);
let zeroes = p_minus.trailing_zeros() as usize;
let d = p_minus.shr(zeroes);
let mut x = base.u_mod_pow(&d, self);
if x == Mpz::one() || x == p_minus {
return true;
}
for _ in 1..zeroes{
x = x.u_quadratic_residue(self);
if x == p_minus {
return true;
}
}
false
}
#[cfg(not(feature = "parallel"))]
pub fn sprp_check(&self, n: usize) -> bool {
if self.len() < 2 {
return self.to_u64().unwrap().is_prime();
}
if self.is_fermat() {
return false;
}
for _ in 0..n {
let z = Mpz::rand((self.bit_length()-1) as usize);
if !self.sprp(z) {
return false;
} }
true
}
#[cfg(feature = "parallel")] pub fn sprp_check(&self, k: usize) -> bool {
if self.len() < 2 {
return self.to_u64().unwrap().is_prime();
}
if self.is_fermat() {
return false;
}
let single = |x: Mpz, n: usize| {
for _ in 0..n {
let z = Mpz::rand(x.bit_length() as usize-1);
if !x.sprp(z) {
return false;
}
}
return true;
};
let threadcount = usize::from(std::thread::available_parallelism().unwrap()) - 2;
let q = self.clone();
let mut threadvec = vec![];
let mut xclone = vec![];
for _ in 0..threadcount {
xclone.push(self.clone())
}
for i in xclone {
threadvec.push(std::thread::spawn(move || single(i, k / threadcount)));
}
let tally = single(q, k / threadcount);
for j in threadvec {
if !j.join().unwrap() {
return false;
}
}
return tally;
}
pub(crate) fn form_check(&self) -> bool {
let sqrt = self.sqrt().0;
if sqrt.sqr() == self.clone() {
return false;
}
!detect_pseudo_mpz(self)
}
pub(crate) fn trial_div(&self) -> bool {
if self.is_even() {
return false;
}
let rem = mod_slice(&self.limbs[..], 16294579238595022365);
for i in PRIMELIST[1..16usize].iter() {
if rem % *i as u64 == 0 {
return false;
}
}
let mut supremum: usize = 2048;
if self.len() < 40 {
supremum = self.len() * 50
}
for i in PRIMELIST[17..supremum].iter() {
if self.congruence_u64(*i as u64, 0) {
return false;
}
}
return true;
}
pub(crate) fn _trial_list(&self, primes: &[u64]) -> bool{
for i in primes{
if self.congruence_u64(*i,0){
return false
}
}
return true
}
pub(crate) fn weighted_sprp(&self) -> bool {
const CHECK_LUT: [u8; 5] = [8, 6, 5, 4, 2]; let mut check: usize = 1;
if self.len() < 8 {
check = CHECK_LUT[self.len() - 3] as usize;
}
self.sprp_check(check)
}
pub(crate) fn llt(&self, p: u64) -> bool {
let mut s = Mpz::from_u64(4);
for _ in 0..(p - 2) {
s = s.ref_product(&s);
s.normalize();
sub_slice(&mut s.limbs[..], &[2]);
s = s.ref_euclidean(self).1;
}
s.normalize();
if s == Mpz::zero() {
return true;
}
false
}
pub fn is_sophie(&self) -> Option<Self> {
if self.is_prime() {
let mut safe = self.shl(1);
let p = safe.clone();
let two = Mpz::two();
safe.successor();
if two.exp_residue(&p, &safe) == Mpz::one() {
return Some(safe);
}
}
None
}
pub(crate) fn jacobi_check_mpz(&self) -> bool {
let mut witness = 3;
loop {
if fast_jacobi_mpz(self, witness) == -1 {
break;
}
witness += 1;
}
let witty = Mpz::from_u64(witness);
self.is_sprp(&witty)
}
pub fn psp(k: usize) -> NTResult<Self>{
let corrector = 1 + (k&1)*2 ;
if k < 14{
return NTResult::DNE
}
if k > (1<<32){
return NTResult::CompExceeded
}
loop {
let len = (k-corrector)/2;
let mut x = Mpz::rand(len);
if corrector == 3{
if !x.check_bit(len-2){
x.flip_bit(len-2);
}
}
if corrector == 1{
if x.check_bit(len-2){
x.flip_bit(len-2);
}
if x.check_bit(len-3){
x.flip_bit(len-3);
}
}
let lhs = x.shl(1).ref_addition(&Mpz::one());
let rhs = x.shl(2).ref_addition(&Mpz::one());
if lhs.is_prime() & rhs.is_prime(){
let product = lhs.ref_product(&rhs);
assert_eq!(product.bit_length(),k as u64);
return NTResult::Eval(product)
}
}
}
pub fn fermat(&self, base: &Self) -> bool{
base.exp_residue(&self.ref_subtraction(&Mpz::one()),self).is_one()
}
pub fn miller(&self) -> bool{
let sup = (self.ln()*self.ln()*2.0) as u64;
match self.to_u128(){
Some(x) => {
for i in 2..sup{
if !x.is_sprp(&(i as u128)){
return false
}
}
}
None => {
for i in 2..sup{
if !self.is_sprp(&Mpz::from_u64(i)){
return false
}
}
}
}
return true
}
}
pub(crate) fn fast_jacobi_mpz(x: &Mpz, p: u64) -> i8 {
let k = x.word_div(p).1;
k.jacobi(&p)
}
pub(crate) fn detect_pseudo_mpz(x: &Mpz) -> bool {
let mut xminus = x.abs();
let copy = xminus.clone();
let eightprod = copy.scale_add(8,1);
let eightsqrt = eightprod.sqrt().0;
if eightsqrt.sqr() == eightprod{
return true
}
let threeprod = copy.scale_add(3,1);
let threesqrt = threeprod.sqrt().0;
if threesqrt.sqr() == threeprod{
return true
}
xminus.inv_successor();
for i in 1..16 {
let sq = xminus.word_div(2 * i + 1).0.sqrt().0; let lhs = sq.ref_product(&sq).ref_addition(&sq);
let lhs2 = lhs.ref_product(&Mpz::from_u64(2 * i + 1));
if lhs.ref_addition(&lhs2).ref_addition(&Mpz::one()) == copy {
return true;
}
}
return false;
}