use crypto_bigint::{
modular::{MontyForm, MontyParams},
Concat, Uint,
};
pub fn euler_totient<const PRIME_LIMBS: usize, const OUTPUT_LIMBS: usize>(
p: &Uint<PRIME_LIMBS>,
q: &Uint<PRIME_LIMBS>,
) -> Uint<OUTPUT_LIMBS>
where
Uint<PRIME_LIMBS>: Concat<Output = Uint<OUTPUT_LIMBS>>,
{
p.wrapping_sub(&Uint::<PRIME_LIMBS>::ONE)
.widening_mul(&q.wrapping_sub(&Uint::<PRIME_LIMBS>::ONE))
}
pub fn crt_combine<const LIMBS: usize, const WIDE_LIMBS: usize>(
r_p: &Uint<LIMBS>,
r_q: &Uint<LIMBS>,
p_inv: MontyForm<LIMBS>,
p: &Uint<LIMBS>,
q_mtg: MontyParams<LIMBS>,
) -> Uint<WIDE_LIMBS>
where
Uint<LIMBS>: Concat<Output = Uint<WIDE_LIMBS>>,
{
let diff = MontyForm::new(r_q, q_mtg) - MontyForm::new(r_p, q_mtg);
let t = (diff * p_inv).retrieve();
t.widening_mul(p) + r_p.resize()
}
pub fn is_3_mod_4<const LIMBS: usize>(p: &Uint<LIMBS>) -> bool {
p.as_limbs()[0].0 & 3 == 3
}
pub fn is_3_mod_8<const LIMBS: usize>(p: &Uint<LIMBS>) -> bool {
p.as_limbs()[0].0 & 7 == 3
}
pub fn is_5_mod_8<const LIMBS: usize>(p: &Uint<LIMBS>) -> bool {
p.as_limbs()[0].0 & 7 == 5
}
#[cfg(test)]
mod tests {
use super::*;
use crypto_bigint::{Random, RandomMod, U128, U256, U64};
use crypto_primes::generate_prime_with_rng;
use rand_core::OsRng;
#[test]
fn check_mods() {
let mut rng = OsRng::default();
for _ in 0..1000 {
let a = U64::random(&mut rng);
let a_4 = a.rem(&U64::from(4u32).to_nz().unwrap());
let a_8 = a.rem(&U64::from(8u32).to_nz().unwrap());
if a_4 == U64::from(3u32) {
assert!(is_3_mod_4(&a));
}
if a_8 == U64::from(3u32) {
assert!(is_3_mod_8(&a));
}
if a_8 == U64::from(5u32) {
assert!(is_5_mod_8(&a));
}
}
}
#[test]
fn crt() {
let mut rng = OsRng::default();
macro_rules! check {
( $prime_type:ident, $product_type:ident ) => {
let p = generate_prime_with_rng::<$prime_type>(&mut rng, $prime_type::BITS);
let q = generate_prime_with_rng::<$prime_type>(&mut rng, $prime_type::BITS);
let n = p.widening_mul(&q);
let q_mtg = MontyParams::new(q.to_odd().unwrap());
let p_inv = MontyForm::new(&p, q_mtg).inv().unwrap();
for _ in 0..100 {
let a = $product_type::random_mod(&mut rng, &n.to_nz().unwrap());
let a_p: $prime_type = a.rem(&p.resize().to_nz().unwrap()).resize();
let a_q: $prime_type = a.rem(&q.resize().to_nz().unwrap()).resize();
assert_eq!(
a,
crt_combine::<{ $prime_type::LIMBS }, { $product_type::LIMBS }>(
&a_p, &a_q, p_inv, &p, q_mtg
)
);
}
};
}
check!(U64, U128);
check!(U128, U256);
}
}