use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::int::Int;
use crate::mod_int::ModInt;
use crate::nat::Nat;
const BSGS_MAX_ORDER_BITS: u64 = 40;
const RHO_ATTEMPTS: u64 = 24;
#[inline]
fn reduce(value: &Int, modulus: &Int) -> Int {
value.rem_euclid(modulus)
}
#[inline]
fn mul_mod(a: &Int, b: &Int, modulus: &Int) -> Int {
a.mul(b).rem_euclid(modulus)
}
fn prepare(
base: &Int,
target: &Int,
modulus: &Int,
order: &Int,
) -> core::result::Result<(Int, Int, Int), Option<Int>> {
let m = modulus.abs();
if m <= Int::ONE {
return Err(Some(Int::ZERO));
}
if !order.is_positive() {
return Err(None);
}
let g = reduce(base, &m);
let h = reduce(target, &m);
if h.is_one() {
return Err(Some(Int::ZERO));
}
if g.is_zero() {
return Err(if h.is_zero() { Some(Int::ONE) } else { None });
}
Ok((g, h, m))
}
fn isqrt_ceil(order: &Nat) -> Nat {
let s = order.isqrt();
if &s.mul(&s) < order {
s.add(&Nat::one())
} else {
s
}
}
pub fn bsgs(base: &Int, target: &Int, modulus: &Int, order: &Int) -> Option<Int> {
let (g, h, m) = match prepare(base, target, modulus, order) {
Ok(v) => v,
Err(done) => return done,
};
let m_steps = isqrt_ceil(&order.magnitude());
let m_steps = m_steps.to_u64().expect("bsgs: order is too large");
let m_step_int = Int::from(m_steps);
let mut table: BTreeMap<Nat, u64> = BTreeMap::new();
let mut power = Int::ONE; for j in 0..m_steps {
table.entry(power.magnitude()).or_insert(j);
power = mul_mod(&power, &g, &m);
}
let g_inv = g.modinv(&m)?;
let factor = g_inv.modpow(&m_step_int, &m);
let mut gamma = h;
for i in 0..m_steps {
if let Some(&j) = table.get(&gamma.magnitude()) {
let x = Int::from(i).mul(&m_step_int).add(&Int::from(j));
if &x < order {
return Some(x);
}
}
gamma = mul_mod(&gamma, &factor, &m);
}
None
}
#[derive(Clone)]
struct Walk {
x: Int,
a: Int,
b: Int,
}
fn rho_step(w: &Walk, g: &Int, h: &Int, m: &Int, order: &Int) -> Walk {
let part = w.x.rem_euclid(&Int::from(3u64));
if part.is_zero() {
Walk {
x: mul_mod(&w.x, &w.x, m),
a: w.a.add(&w.a).rem_euclid(order),
b: w.b.add(&w.b).rem_euclid(order),
}
} else if part.is_one() {
Walk {
x: mul_mod(&w.x, g, m),
a: w.a.add(&Int::ONE).rem_euclid(order),
b: w.b.clone(),
}
} else {
Walk {
x: mul_mod(&w.x, h, m),
a: w.a.clone(),
b: w.b.add(&Int::ONE).rem_euclid(order),
}
}
}
fn solve_and_verify(coeff: &Int, rhs: &Int, order: &Int, g: &Int, h: &Int, m: &Int) -> Option<Int> {
let coeff = coeff.rem_euclid(order);
let rhs = rhs.rem_euclid(order);
if coeff.is_zero() {
return None;
}
let d = coeff.gcd(order);
if !d.divides(&rhs) {
return None;
}
let coeff_d = coeff.div_exact(&d);
let rhs_d = rhs.div_exact(&d);
let order_d = order.div_exact(&d);
let inv = coeff_d.modinv(&order_d)?;
let x0 = mul_mod(&rhs_d, &inv, &order_d);
let dd = d.to_u64().unwrap_or(u64::MAX);
let mut cand = x0;
for _ in 0..dd {
if &g.modpow(&cand, m) == h {
return Some(cand);
}
cand = cand.add(&order_d);
if &cand >= order {
break;
}
}
None
}
pub fn pollard_rho(base: &Int, target: &Int, modulus: &Int, order: &Int, seed: u64) -> Option<Int> {
let (g, h, m) = match prepare(base, target, modulus, order) {
Ok(v) => v,
Err(done) => return done,
};
let a0 = Int::from(seed).rem_euclid(order);
let b0 = Int::from(seed / 2 + 1).rem_euclid(order);
let start = Walk {
x: mul_mod(&g.modpow(&a0, &m), &h.modpow(&b0, &m), &m),
a: a0,
b: b0,
};
let steps = isqrt_ceil(&order.magnitude());
let cap = steps.to_u64().unwrap_or(u64::MAX).saturating_mul(8).max(64);
let mut tortoise = start.clone();
let mut hare = start;
for _ in 0..cap {
tortoise = rho_step(&tortoise, &g, &h, &m, order);
hare = rho_step(&rho_step(&hare, &g, &h, &m, order), &g, &h, &m, order);
if tortoise.x == hare.x {
let coeff = hare.b.sub(&tortoise.b);
let rhs = tortoise.a.sub(&hare.a);
return solve_and_verify(&coeff, &rhs, order, &g, &h, &m);
}
}
None
}
pub fn pohlig_hellman(base: &Int, target: &Int, modulus: &Int, order: &Int) -> Option<Int> {
let (g, h, m) = match prepare(base, target, modulus, order) {
Ok(v) => v,
Err(done) => return done,
};
let g_inv = g.modinv(&m)?;
let factors = order.factor_exponents();
let mut residues: Vec<Int> = Vec::with_capacity(factors.len());
let mut moduli: Vec<Int> = Vec::with_capacity(factors.len());
for (p, e) in &factors {
let gamma = g.modpow(&order.div_exact(p), &m);
let mut xi = Int::ZERO; let mut pj = Int::ONE; for j in 0..*e {
let stripped = mul_mod(&h, &g_inv.modpow(&xi, &m), &m);
let beta = stripped.modpow(&order.div_exact(&p.pow(j + 1)), &m);
let digit = discrete_log(&gamma, &beta, &m, p)?;
xi = xi.add(&digit.mul(&pj));
pj = pj.mul(p); }
residues.push(xi);
moduli.push(pj);
}
let x = Int::crt(&residues, &moduli)?;
if g.modpow(&x, &m) == h { Some(x) } else { None }
}
pub fn discrete_log(base: &Int, target: &Int, modulus: &Int, order: &Int) -> Option<Int> {
if order.is_positive() {
let factors = order.factor_exponents();
let composite = factors.len() > 1 || factors.first().is_some_and(|(_, e)| *e > 1);
if composite && let Some(x) = pohlig_hellman(base, target, modulus, order) {
return Some(x);
}
}
if order.is_positive() && order.magnitude().bit_len() <= BSGS_MAX_ORDER_BITS {
return bsgs(base, target, modulus, order);
}
for seed in 0..RHO_ATTEMPTS {
if let Some(x) = pollard_rho(base, target, modulus, order, seed) {
return Some(x);
}
}
bsgs(base, target, modulus, order)
}
impl ModInt {
pub fn discrete_log(&self, target: &ModInt, order: &Int) -> Option<Int> {
discrete_log(&self.to_int(), &target.to_int(), &self.modulus(), order)
}
}