use core::default::Default;
use std::ops::{BitAnd, BitOr, Mul};
use either::Either;
use num_integer::{Integer, Roots};
use num_traits::Pow;
pub trait BitTest {
fn bits(&self) -> usize;
fn bit(&self, position: usize) -> bool;
fn trailing_zeros(&self) -> usize;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Primality {
Yes,
No,
Probable(f32),
}
impl Primality {
#[inline(always)]
#[must_use]
pub fn probably(self) -> bool {
!matches!(self, Primality::No)
}
}
impl BitAnd<Primality> for Primality {
type Output = Primality;
fn bitand(self, rhs: Primality) -> Self::Output {
match self {
Primality::No => Primality::No,
Primality::Yes => rhs,
Primality::Probable(p) => match rhs {
Primality::No => Primality::No,
Primality::Yes => Primality::Probable(p),
Primality::Probable(p2) => {
let combined = p.mul(p2);
Primality::Probable(combined)
}
},
}
}
}
impl BitOr<Primality> for Primality {
type Output = Primality;
fn bitor(self, rhs: Primality) -> Self::Output {
match self {
Primality::No => rhs,
Primality::Yes => Primality::Yes,
Primality::Probable(p) => match rhs {
Primality::No => Primality::Probable(p),
Primality::Yes => Primality::Yes,
Primality::Probable(p2) => Primality::Probable(1. - (1. - p) * (1. - p2)),
},
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct PrimalityTestConfig {
pub sprp_trials: usize,
pub sprp_random_trials: usize,
pub slprp_test: bool,
pub eslprp_test: bool,
}
impl Default for PrimalityTestConfig {
fn default() -> Self {
Self {
sprp_trials: 2, sprp_random_trials: 3, slprp_test: false,
eslprp_test: false,
}
}
}
impl PrimalityTestConfig {
#[must_use]
pub fn strict() -> Self {
let mut config = Self::bpsw();
config.sprp_random_trials = 1;
config
}
#[must_use]
pub fn bpsw() -> Self {
Self {
sprp_trials: 1,
sprp_random_trials: 0,
slprp_test: true,
eslprp_test: false,
}
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub struct FactorizationConfig {
pub primality_config: PrimalityTestConfig,
pub td_limit: Option<u64>,
pub rho_trials: usize,
}
impl Default for FactorizationConfig {
fn default() -> Self {
const THRESHOLD_DEFAULT_TD: u64 = 1 << 14;
Self {
primality_config: PrimalityTestConfig::default(),
td_limit: Some(THRESHOLD_DEFAULT_TD),
rho_trials: 4,
}
}
}
impl FactorizationConfig {
#[must_use]
pub fn strict() -> Self {
Self {
primality_config: PrimalityTestConfig::strict(),
..Self::default()
}
}
}
pub trait ExactRoots: Roots + Pow<u32, Output = Self> + Clone {
fn nth_root_exact(&self, n: u32) -> Option<Self> {
let r = self.nth_root(n);
if &r.clone().pow(n) == self {
Some(r)
} else {
None
}
}
fn sqrt_exact(&self) -> Option<Self> {
self.nth_root_exact(2)
}
fn cbrt_exact(&self) -> Option<Self> {
self.nth_root_exact(3)
}
fn is_nth_power(&self, n: u32) -> bool {
self.nth_root_exact(n).is_some()
}
fn is_square(&self) -> bool {
self.sqrt_exact().is_some()
}
fn is_cubic(&self) -> bool {
self.cbrt_exact().is_some()
}
}
pub trait PrimeBuffer<'a> {
type PrimeIter: Iterator<Item = &'a u64>;
fn iter(&'a self) -> Self::PrimeIter;
fn reserve(&mut self, limit: u64);
fn bound(&self) -> u64;
fn contains(&self, num: u64) -> bool;
fn clear(&mut self);
}
pub trait PrimalityUtils: Integer + Clone {
fn is_prp(&self, base: Self) -> bool;
fn is_sprp(&self, base: Self) -> bool;
fn test_sprp(&self, base: Self) -> Either<bool, Self>;
fn is_lprp(&self, p: Option<usize>, q: Option<isize>) -> bool;
fn is_slprp(&self, p: Option<usize>, q: Option<isize>) -> bool;
fn is_eslprp(&self, p: Option<usize>) -> bool;
}
pub trait RandPrime<T> {
fn gen_prime(&mut self, bit_size: usize, config: Option<PrimalityTestConfig>) -> T;
fn gen_prime_exact(&mut self, bit_size: usize, config: Option<PrimalityTestConfig>) -> T;
fn gen_safe_prime(&mut self, bit_size: usize) -> T;
fn gen_safe_prime_exact(&mut self, bit_size: usize) -> T;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primality_probably() {
assert!(Primality::Yes.probably());
assert!(!Primality::No.probably());
assert!(Primality::Probable(0.9).probably());
}
#[test]
fn primality_bitand_no_lhs() {
assert_eq!(Primality::No & Primality::No, Primality::No);
assert_eq!(Primality::No & Primality::Yes, Primality::No);
assert_eq!(Primality::No & Primality::Probable(0.9), Primality::No);
}
#[test]
fn primality_bitand_yes_lhs() {
assert_eq!(Primality::Yes & Primality::No, Primality::No);
assert_eq!(Primality::Yes & Primality::Yes, Primality::Yes);
assert_eq!(
Primality::Yes & Primality::Probable(0.8),
Primality::Probable(0.8)
);
}
#[test]
fn primality_bitand_probable_lhs() {
assert_eq!(Primality::Probable(0.9) & Primality::No, Primality::No);
assert_eq!(
Primality::Probable(0.9) & Primality::Yes,
Primality::Probable(0.9)
);
let result = Primality::Probable(0.8) & Primality::Probable(0.9);
match result {
Primality::Probable(p) => assert!((p - 0.72).abs() < 1e-6),
_ => panic!("expected Probable"),
}
}
#[test]
fn primality_bitor_no_lhs() {
assert_eq!(Primality::No | Primality::No, Primality::No);
assert_eq!(Primality::No | Primality::Yes, Primality::Yes);
assert_eq!(
Primality::No | Primality::Probable(0.8),
Primality::Probable(0.8)
);
}
#[test]
fn primality_bitor_yes_lhs() {
assert_eq!(Primality::Yes | Primality::No, Primality::Yes);
assert_eq!(Primality::Yes | Primality::Yes, Primality::Yes);
assert_eq!(Primality::Yes | Primality::Probable(0.8), Primality::Yes);
}
#[test]
fn primality_bitor_probable_lhs() {
assert_eq!(
Primality::Probable(0.9) | Primality::No,
Primality::Probable(0.9)
);
assert_eq!(Primality::Probable(0.9) | Primality::Yes, Primality::Yes);
let result = Primality::Probable(0.8) | Primality::Probable(0.9);
match result {
Primality::Probable(p) => assert!((p - 0.98).abs() < 1e-6),
_ => panic!("expected Probable"),
}
}
#[test]
fn primality_test_config_default() {
let c = PrimalityTestConfig::default();
assert_eq!(c.sprp_trials, 2);
assert_eq!(c.sprp_random_trials, 3);
assert!(!c.slprp_test);
assert!(!c.eslprp_test);
}
#[test]
fn primality_test_config_bpsw() {
let c = PrimalityTestConfig::bpsw();
assert_eq!(c.sprp_trials, 1);
assert_eq!(c.sprp_random_trials, 0);
assert!(c.slprp_test);
assert!(!c.eslprp_test);
}
#[test]
fn primality_test_config_strict() {
let c = PrimalityTestConfig::strict();
assert_eq!(c.sprp_trials, 1);
assert_eq!(c.sprp_random_trials, 1);
assert!(c.slprp_test);
assert!(!c.eslprp_test);
}
#[test]
fn factorization_config_default() {
let c = FactorizationConfig::default();
assert_eq!(c.td_limit, Some(1 << 14));
assert_eq!(c.rho_trials, 4);
}
#[test]
fn factorization_config_strict() {
let c = FactorizationConfig::strict();
assert_eq!(c.td_limit, Some(1 << 14));
assert_eq!(c.rho_trials, 4);
assert_eq!(c.primality_config.sprp_trials, 1);
assert_eq!(c.primality_config.sprp_random_trials, 1);
assert!(c.primality_config.slprp_test);
}
#[test]
fn exact_roots_is_square() {
assert!(16u64.is_square());
assert!(!15u64.is_square());
assert!(1u64.is_square());
assert!(100u64.is_square());
assert!(!99u64.is_square());
}
#[test]
fn exact_roots_is_cubic() {
assert!(27u64.is_cubic());
assert!(!26u64.is_cubic());
assert!(1u64.is_cubic());
assert!(64u64.is_cubic());
}
#[test]
fn exact_roots_is_nth_power() {
assert!(256u64.is_nth_power(4));
assert!(!255u64.is_nth_power(4));
}
#[test]
fn exact_roots_sqrt_exact() {
assert_eq!(49u64.sqrt_exact(), Some(7));
assert_eq!(50u64.sqrt_exact(), None);
}
#[test]
fn exact_roots_cbrt_exact() {
assert_eq!(125u64.cbrt_exact(), Some(5));
assert_eq!(126u64.cbrt_exact(), None);
}
}