#![no_std]
use core::ops::{Add, AddAssign, BitAnd, Mul, Neg, Rem, ShrAssign, Sub, SubAssign};
pub mod montgomery;
fn reduce<T>(mut a: T, modulo: &T) -> T
where
T: Ord + for<'x> AddAssign<&'x T> + for<'x> SubAssign<&'x T>,
for<'x> &'x T: Neg<Output = T>,
{
if &a >= modulo {
a -= modulo;
} else if a <= -modulo {
a += modulo
}
a
}
pub fn add_mod<T>(a: &T, b: &T, modulo: &T) -> T
where
T: Ord + for<'x> AddAssign<&'x T> + for<'x> SubAssign<&'x T>,
for<'x> &'x T: Add<Output = T> + Neg<Output = T>,
{
let c = a + b;
reduce(c, modulo)
}
pub fn sub_mod<T>(a: &T, b: &T, modulo: &T) -> T
where
T: Ord + for<'x> AddAssign<&'x T> + for<'x> SubAssign<&'x T>,
for<'x> &'x T: Sub<Output = T> + Neg<Output = T>,
{
let c = a - b;
reduce(c, modulo)
}
pub fn mul_mod<T>(a: &T, b: &T, modulo: &T) -> T
where
for<'x> &'x T: Mul<Output = T> + Rem<Output = T>,
{
&(a * b) % modulo
}
pub fn pow_mod<T, U>(a: T, mut b: U, modulo: &T) -> T
where
T: From<u8>,
for<'x> &'x T: Mul<Output = T> + Rem<Output = T>,
U: Ord + ShrAssign<u8> + From<u8>,
for<'x> &'x U: BitAnd<Output = U>,
{
let c0 = U::from(0);
let c1 = U::from(1);
let mut x = a;
let mut y = T::from(1);
while b > c0 {
if &b & &c1 != c0 {
y = mul_mod(&x, &y, modulo);
}
x = mul_mod(&x, &x, modulo);
b >>= 1;
}
y
}
pub fn mul_pow_mod<T, U>(a: T, base: T, mut power: U, modulo: &T) -> T
where
for<'x> &'x T: Mul<Output = T> + Rem<Output = T>,
U: Ord + ShrAssign<u8> + From<u8>,
for<'x> &'x U: BitAnd<Output = U>,
{
let c0 = U::from(0);
let c1 = U::from(1);
let mut x = base;
let mut y = a;
while power > c0 {
if &power & &c1 != c0 {
y = mul_mod(&x, &y, modulo);
}
x = mul_mod(&x, &x, modulo);
power >>= 1;
}
y
}