Skip to content

Commit

Permalink
Add todos for modint
Browse files Browse the repository at this point in the history
  • Loading branch information
northernorca committed Sep 24, 2024
1 parent ab9e4a1 commit 692d353
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 1 deletion.
11 changes: 11 additions & 0 deletions basm-std/src/math/dynamic_modint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,34 @@
pub struct Modulo(pub u64);

impl Modulo {
// NOTE: Unlike `static_modint`, the arguments of each function may be larger than or equal to
// `self.0`, the modulus.

// TODO: Investigate the plausibility (performance, overflow) of Lemire's algorithm
// https://snippets.kiwiyou.dev/cdiv
// https://lemire.me/blog/2019/02/20/more-fun-with-fast-remainders-when-the-divisor-is-a-constant/

pub fn new(modulo: u64) -> Self {
debug_assert!(modulo >= 2);
Self(modulo)
}

// TODO: Handle the case where `a + b` overflows
pub fn add(&self, a: u64, b: u64) -> u64 {
(a + b) % self.0
}

// TODO: Handle the case where `self.0 + a` overflows
pub fn sub(&self, a: u64, b: u64) -> u64 {
(self.0 + a - b) % self.0
}

// TODO: Handle the case where `a * b` overflows
pub fn mul(&self, a: u64, b: u64) -> u64 {
(a * b) % self.0
}

// TODO: Try removing `i128` cast
pub fn div(&self, a: u64, b: u64) -> u64 {
let inv = super::egcd(b as i128, self.0 as i128).1;
let inv = (self.0 as i128 + inv) as u64 % self.0;
Expand Down
13 changes: 12 additions & 1 deletion basm-std/src/math/static_modint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,33 @@ impl<const M: u64> From<ModInt<M>> for u64 {
}
}

// TODO: Handle the case for `self.0 + rhs.0` overflow
impl<const M: u64> Add for ModInt<M> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self((self.0 + rhs.0) % M)
}
}

// TODO: Same with `Add`
impl<const M: u64> AddAssign for ModInt<M> {
fn add_assign(&mut self, rhs: Self) {
self.0 = (self.0 + rhs.0) % M;
}
}

// TODO: Try removing `i128` cast
impl<const M: u64> Div for ModInt<M> {
type Output = Self;
/// May panic
/// May panic if M == 0
fn div(self, rhs: Self) -> Self::Output {
let inv = super::egcd(rhs.0 as i128, M as i128).1;
let inv = (M as i128 + inv) as u64 % M;
Self(self.0) * Self(inv)
}
}

// TODO: Same with `Div`
impl<const M: u64> DivAssign for ModInt<M> {
fn div_assign(&mut self, rhs: Self) {
let inv = super::egcd(rhs.0 as i128, M as i128).1;
Expand All @@ -55,33 +59,40 @@ impl<const M: u64> DivAssign for ModInt<M> {
}
}

// TODO: Handle the case for `self.0 * rhs.0` overflow
impl<const M: u64> Mul for ModInt<M> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self((self.0 * rhs.0) % M)
}
}

// TODO: Same with `Mul`
impl<const M: u64> MulAssign for ModInt<M> {
fn mul_assign(&mut self, rhs: Self) {
self.0 = (self.0 * rhs.0) % M;
}
}

// TODO: Check if there's a more performant way, doable without literal rem operation
impl<const M: u64> Neg for ModInt<M> {
type Output = Self;
fn neg(self) -> Self::Output {
Self((M - self.0) % M)
}
}

// TODO: This, or `self + (-rhs)`, which is more performant?
// TODO: Handle the case where `M + self.0` overflows
impl<const M: u64> Sub for ModInt<M> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self((M + self.0 - rhs.0) % M)
}
}

// TODO: This, or `self += -rhs`, which is more performant?
// TODO: Handle the case where `M + self.0` overflows
impl<const M: u64> SubAssign for ModInt<M> {
fn sub_assign(&mut self, rhs: Self) {
self.0 = (M + self.0 - rhs.0) % M;
Expand Down

0 comments on commit 692d353

Please sign in to comment.