use core::{
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
str::FromStr,
};
macro_rules! generate {
($name: ident, $base:ty, $wide:ty, $signed_wide: ty, $num_traits: ident, $test_name: ident) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name<const M: $base>($base);
impl<const M: $base> From<$base> for $name<M> {
fn from(x: $base) -> Self {
Self(x % M)
}
}
#[auto_impl_ops::auto_ops]
impl<const M: $base> AddAssign<&$name<M>> for $name<M> {
fn add_assign(&mut self, other: &Self) {
let t = self.0 + other.0;
self.0 = if t >= M { t - M } else { t };
}
}
#[auto_impl_ops::auto_ops]
impl<const M: $base> SubAssign<&$name<M>> for $name<M> {
fn sub_assign(&mut self, other: &Self) {
let t = self.0 + (M - other.0);
self.0 = if t >= M { t - M } else { t };
}
}
impl<const M: $base> Neg for $name<M> {
type Output = Self;
fn neg(self) -> Self::Output {
Self(M - self.0)
}
}
impl<const M: $base> Neg for &$name<M> {
type Output = $name<M>;
fn neg(self) -> Self::Output {
$name(M - self.0)
}
}
#[auto_impl_ops::auto_ops]
impl<const M: $base> MulAssign<&$name<M>> for $name<M> {
fn mul_assign(&mut self, other: &Self) {
self.0 = ((self.0 as $wide) * (other.0 as $wide) % (M as $wide)) as $base;
}
}
#[auto_impl_ops::auto_ops]
impl<const M: $base> DivAssign<&$name<M>> for $name<M> {
fn div_assign(&mut self, other: &Self) {
let t = ring_algorithm::modulo_division(
self.0 as $signed_wide,
other.0 as $signed_wide,
M as $signed_wide,
)
.expect("Can't divide");
let t = if t < 0 { t + M as $signed_wide } else { t };
self.0 = t as $base;
}
}
impl<const M: $base> FromStr for $name<M> {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut v = s.parse::<i128>()?;
v %= M as i128;
if v < 0 {
v += M as i128;
}
Ok(Self(v as $base))
}
}
impl<const M: $base> std::fmt::Display for $name<M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{}", self.0)
}
}
#[cfg(feature = "num-traits")]
mod $num_traits {
use crate::$name;
use num_traits::{ConstOne, ConstZero, One, Zero};
impl<const M: $base> Zero for $name<M> {
fn zero() -> Self {
Self::ZERO
}
fn is_zero(&self) -> bool {
*self == Self::ZERO
}
}
impl<const M: $base> ConstZero for $name<M> {
const ZERO: Self = Self(0);
}
impl<const M: $base> One for $name<M> {
fn one() -> Self {
Self::ONE
}
}
impl<const M: $base> ConstOne for $name<M> {
const ONE: Self = Self(1);
}
}
#[cfg(test)]
mod $test_name {
use super::*;
#[test]
fn add() {
let a = $name::<6>(2);
let b = $name::<6>(3);
let c = $name::<6>(4);
let d = $name::<6>(5);
let e = $name::<6>(1);
let f = $name::<6>(0);
assert_eq!(a + b, d);
assert_eq!(b + c, e);
assert_eq!(b + b, f);
}
#[test]
fn sub() {
let a = $name::<6>(3);
let b = $name::<6>(2);
let c = $name::<6>(4);
let d = $name::<6>(1);
let e = $name::<6>(4);
let f = $name::<6>(0);
assert_eq!(a - b, d);
assert_eq!(b - c, e);
assert_eq!(a - a, f);
}
#[test]
fn mul() {
let a = $name::<6>(2);
let b = $name::<6>(5);
let c = $name::<6>(3);
let d = $name::<6>(4);
let e = $name::<6>(3);
let f = $name::<6>(0);
assert_eq!(a * b, d);
assert_eq!(b * c, e);
assert_eq!(a * c, f);
}
#[test]
fn div() {
let a = $name::<5>(2);
let b = $name::<5>(3);
let c = $name::<5>(4);
let d = $name::<5>(4);
let e = $name::<5>(2);
let f = $name::<5>(3);
assert_eq!(a / b, d);
assert_eq!(b / c, e);
assert_eq!(a / c, f);
}
}
};
}
generate!(ConstModulo8, u8, u16, i16, impl_num_traits8, test8);
generate!(ConstModulo16, u16, u32, i32, impl_num_traits16, test16);
generate!(ConstModulo32, u32, u64, i64, impl_num_traits32, test32);
generate!(ConstModulo64, u64, u128, i128, impl_num_traits64, test64);