use crate::{
arch::word::Word,
ibig::IBig,
modular::modulo::{Modulo, ModuloLarge, ModuloRepr, ModuloSmall, ModuloSmallRaw},
ops::RemEuclid,
ubig::UBig,
};
use core::{
convert::TryInto,
ops::{Div, DivAssign},
};
impl<'a> Modulo<'a> {
pub fn inverse(&self) -> Option<Modulo<'a>> {
match self.repr() {
ModuloRepr::Small(self_small) => self_small.inverse().map(Into::into),
ModuloRepr::Large(self_large) => self_large.inverse().map(Into::into),
}
}
}
impl<'a> Div<Modulo<'a>> for Modulo<'a> {
type Output = Modulo<'a>;
#[inline]
fn div(self, rhs: Modulo<'a>) -> Modulo<'a> {
(&self).div(&rhs)
}
}
impl<'a> Div<&Modulo<'a>> for Modulo<'a> {
type Output = Modulo<'a>;
#[inline]
fn div(self, rhs: &Modulo<'a>) -> Modulo<'a> {
(&self).div(rhs)
}
}
impl<'a> Div<Modulo<'a>> for &Modulo<'a> {
type Output = Modulo<'a>;
#[inline]
fn div(self, rhs: Modulo<'a>) -> Modulo<'a> {
self.div(&rhs)
}
}
impl<'a> Div<&Modulo<'a>> for &Modulo<'a> {
type Output = Modulo<'a>;
#[inline]
fn div(self, rhs: &Modulo<'a>) -> Modulo<'a> {
#[allow(clippy::suspicious_arithmetic_impl)]
match rhs.inverse() {
None => panic!("Division by a non-invertible Modulo"),
Some(inv_rhs) => self * inv_rhs,
}
}
}
impl<'a> DivAssign<Modulo<'a>> for Modulo<'a> {
#[inline]
fn div_assign(&mut self, rhs: Modulo<'a>) {
self.div_assign(&rhs)
}
}
impl<'a> DivAssign<&Modulo<'a>> for Modulo<'a> {
#[inline]
fn div_assign(&mut self, rhs: &Modulo<'a>) {
*self = (&*self).div(rhs)
}
}
impl<'a> ModuloSmall<'a> {
fn inverse(&self) -> Option<ModuloSmall<'a>> {
let a = self.residue();
let b = self.ring().modulus();
let (gcd, x, _) = UBig::from(a).extended_gcd(&UBig::from(b));
if gcd == UBig::from_word(1) {
let res: Word = x.rem_euclid(IBig::from(b)).try_into().unwrap();
Some(ModuloSmall::new(
ModuloSmallRaw::from_word(res, self.ring()),
self.ring(),
))
} else {
None
}
}
}
impl<'a> ModuloLarge<'a> {
fn inverse(&self) -> Option<ModuloLarge<'a>> {
let a = self.residue();
let b = self.ring().modulus();
let (gcd, x, _) = a.extended_gcd(&b);
if gcd == UBig::from_word(1) {
let res: UBig = x.rem_euclid(IBig::from(b)).try_into().unwrap();
Some(ModuloLarge::from_ubig(res, self.ring()))
} else {
None
}
}
}