#![cfg_attr(feature = "__internal_inject_debug", recursion_limit = "8")]
mod sealed {
pub trait SizedExt: std::marker::Sized + std::fmt::Debug + std::fmt::Display {}
impl<T> SizedExt for T where T: std::marker::Sized + std::fmt::Debug + std::fmt::Display {}
#[cfg(not(feature = "__internal_inject_debug"))]
pub use std::marker::Sized;
#[cfg(feature = "__internal_inject_debug")]
pub use SizedExt as Sized;
}
mod ring_traits;
#[cfg(test)]
mod test;
pub use ring_traits::{EuclideanRingOperation, RingNormalize, RingOperation};
pub fn times<T>(a: T, mut p: u64) -> T
where
T: sealed::Sized + num_traits::Zero + for<'x> std::ops::AddAssign<&'x T>,
for<'x> &'x T: std::ops::Add<Output = T>,
{
let mut x = T::zero();
let mut y = a;
loop {
if p % 2 == 1 {
x += &y;
}
p /= 2;
if p == 0 {
break;
}
y = &y + &y;
}
x
}
pub fn power<T>(a: T, mut p: u64) -> T
where
T: sealed::Sized + num_traits::One + for<'x> std::ops::MulAssign<&'x T>,
for<'x> &'x T: std::ops::Mul<Output = T>,
{
let mut x = T::one();
let mut y = a;
loop {
if p % 2 == 1 {
x *= &y;
}
p /= 2;
if p == 0 {
break;
}
y = &y * &y;
}
x
}
pub fn gcd<T>(mut x: T, mut y: T) -> T
where
T: sealed::Sized + num_traits::Zero,
for<'x> &'x T: std::ops::Rem<Output = T>,
{
while !y.is_zero() {
let r = &x % &y;
x = y;
y = r;
}
x
}
pub fn is_coprime<T>(x: T, y: T) -> bool
where
T: sealed::Sized + Eq + num_traits::Zero + num_traits::One + RingNormalize,
for<'x> &'x T: std::ops::Rem<Output = T>,
{
gcd::<T>(x, y).into_normalize().is_one()
}
pub fn extended_euclidian_algorithm<T>(x: T, y: T) -> (T, T, T)
where
T: sealed::Sized + num_traits::Zero + num_traits::One,
for<'x> &'x T: EuclideanRingOperation<T>,
{
let mut old = (x, T::one(), T::zero());
let mut now = (y, T::zero(), T::one());
while !now.0.is_zero() {
let q = &old.0 / &now.0;
let new = (
&old.0 - &(&q * &now.0),
&old.1 - &(&q * &now.1),
&old.2 - &(&q * &now.2),
);
old = now;
now = new;
}
old
}
pub fn normalized_extended_euclidian_algorithm<T>(x: T, y: T) -> (T, T, T)
where
T: sealed::Sized + num_traits::Zero + num_traits::One + RingNormalize,
for<'x> &'x T: EuclideanRingOperation<T>,
{
let lc_x = x.leading_unit();
let lc_y = y.leading_unit();
let mut old = (x.into_normalize(), &T::one() / &lc_x, T::zero());
let mut now = (y.into_normalize(), T::zero(), &T::one() / &lc_y);
while !now.0.is_zero() {
let q = &old.0 / &now.0;
let r = &old.0 % &now.0;
let lc_r = r.leading_unit();
let new = (
r.into_normalize(),
&(&old.1 - &(&q * &now.1)) / &lc_r,
&(&old.2 - &(&q * &now.2)) / &lc_r,
);
old = now;
now = new;
}
old
}
pub fn modulo_inverse<T>(a: T, m: T) -> Option<T>
where
T: sealed::Sized + Eq + num_traits::Zero + num_traits::One + RingNormalize,
for<'x> &'x T: EuclideanRingOperation<T>,
{
let (gcd, inv_a, _) = normalized_extended_euclidian_algorithm::<T>(a, m);
if gcd.is_one() {
Some(inv_a)
} else {
None
}
}
pub fn modulo_division<T>(a: T, b: T, m: T) -> Option<T>
where
T: sealed::Sized + Clone + Eq + num_traits::Zero + num_traits::One + RingNormalize,
for<'x> &'x T: EuclideanRingOperation<T>,
{
let (gcd, inv_b, _) = normalized_extended_euclidian_algorithm::<T>(b, m.clone());
if (&a % &gcd).is_zero() {
Some(&(&a / &gcd * inv_b) % &m)
} else {
None
}
}
pub fn chinese_remainder_theorem<T>(u: &[T], m: &[T]) -> Option<T>
where
T: sealed::Sized + Clone + Eq + num_traits::Zero + num_traits::One + RingNormalize,
for<'x> &'x T: EuclideanRingOperation<T>,
{
if u.len() != m.len() {
return None;
}
let mut v = Vec::with_capacity(u.len());
for (i, (u_i, m_i)) in u.iter().zip(m.iter()).enumerate() {
let coef_i = modulo_inverse::<T>(
m[0..i].iter().fold(T::one(), |p, v| &(&p * v) % m_i),
m_i.clone(),
)?;
let t = v
.iter()
.zip(m.iter())
.rev()
.fold(T::zero(), |t, (v_j, m_j)| &(&(m_j * &t) + v_j) % m_i);
v.push(&(&(u_i - &t) * &coef_i) % m_i);
}
let mut ret = v.pop().unwrap();
for (v_i, m_i) in v.iter().zip(m.iter()).rev() {
ret = &(&ret * m_i) + v_i;
}
Some(ret)
}