use batch_impl::batch_trait;
use crate::modn::ModN;
use crate::op::{Additive, Multiplicative};
use crate::tower::{
AbelianGroup, CommutativeRing, DivisionRing, EuclideanDomain, Field, FiniteField, FreeModule,
Group, IntegralDomain, Loop, Magma, Module, Monoid, PrincipalIdealDomain, Quasigroup, Ring,
Semigroup, Semiring, UniqueFactorizationDomain, VectorSpace,
};
batch_trait! {
@am=Additive, Multiplicative;
@pmod=<const P: usize> ModN<P>;
Magma: @trait[
{
fn combine(&self, rhs: &Self) -> Self { ModN::new(self.value().wrapping_add(rhs.value())) }
},
<Multiplicative>{
fn combine(&self, rhs: &Self) -> Self { ModN::new(self.value().wrapping_mul(rhs.value())) }
}]@pmod;
Semigroup: @trait[<Additive>,<Multiplicative>] @pmod;
Monoid: [
{
fn identity() -> Self { ModN::new(0) }
},
@trait <Multiplicative> {
fn identity() -> Self { ModN::new(1) }
}]@pmod;
Quasigroup: @pmod;
Loop: @pmod;
Group: @pmod{
fn inverse(&self) -> Self { ModN::new(P.wrapping_sub(self.value()).wrapping_rem(P)) }
};
AbelianGroup: @pmod;
Semiring: @pmod;
Ring: @pmod;
CommutativeRing: @pmod;
Field: @pmod;
FiniteField: @pmod{
fn characteristic() -> u64 { P as u64 }
fn order() -> u64 { P as u64 }
};
IntegralDomain: @pmod;
UniqueFactorizationDomain: @pmod;
PrincipalIdealDomain: @pmod;
Module: @trait<@am,Scalar=Self> @pmod{
fn scale(s: &Self::Scalar, v: Self) -> Self { ModN::new(s.value().wrapping_mul(v.value())) }
};
VectorSpace: @trait<@am> @pmod
where Self::Scalar: Field<@am>;
FreeModule: @trait<@am> @pmod{
fn rank() -> usize { 1 }
fn basis_element(_i: usize) -> Self { <Self as Monoid<Multiplicative>>::identity() }
fn coordinate(&self, _i: usize) -> Self::Scalar { *self }
};
DivisionRing: @pmod{
fn inv(&self) -> Self {
let (mut old_r, mut r) = (self.value() as i128, P as i128);
let (mut old_s, mut s) = (1i128, 0i128);
while r != 0 {
let q = old_r / r;
(old_r, r) = (r, old_r - q * r);
(old_s, s) = (s, old_s - q * s);
}
ModN::new(old_s.rem_euclid(P as i128) as usize)
}
};
EuclideanDomain: @trait<@am> @pmod impl{@trait<>}{
fn quot_rem(&self, divisor: &Self) -> (Self, Self) {
let inv = <Self as DivisionRing<>>::inv(divisor);
(<Self as Magma<Multiplicative>>::combine(self, &inv), ModN::new(0))
}
fn euclidean_norm(&self) -> u128 { 0 }
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tower::{FiniteField, Group, Magma, PrincipalIdealDomain};
fn add<const P: usize>(a: ModN<P>, b: ModN<P>) -> ModN<P> {
<ModN<P> as Magma<Additive>>::combine(&a, &b)
}
fn mul<const P: usize>(a: ModN<P>, b: ModN<P>) -> ModN<P> {
<ModN<P> as Magma<Multiplicative>>::combine(&a, &b)
}
#[test]
fn residues_arithmetic() {
assert_eq!(add(ModN::<7>::new(3), ModN::new(5)), ModN::new(1));
assert_eq!(mul(ModN::<7>::new(3), ModN::new(5)), ModN::new(1));
let inv = <ModN<7> as Group<Additive>>::inverse(&ModN::new(3));
assert_eq!(inv, ModN::new(4));
let five_inv = <ModN<7> as DivisionRing<Additive, Multiplicative>>::inv(&ModN::new(5));
assert_eq!(five_inv, ModN::new(3));
assert_eq!(mul(ModN::<7>::new(5), five_inv), ModN::new(1));
}
#[test]
fn modn_is_a_finite_field() {
assert_eq!(<ModN<7> as FiniteField<Additive, Multiplicative>>::characteristic(), 7);
assert_eq!(<ModN<7> as FiniteField<Additive, Multiplicative>>::order(), 7);
let inv = <ModN<7> as DivisionRing<Additive, Multiplicative>>::inv(&ModN::new(2));
assert_eq!(inv, ModN::new(4));
}
#[test]
fn modn_is_a_principal_ideal_domain() {
fn assert_pid<const P: usize>()
where
ModN<P>: PrincipalIdealDomain<Additive, Multiplicative>,
{
}
assert_pid::<7>();
let a = ModN::<7>::new(5);
let b = ModN::new(3);
let (q, r) = <ModN<7> as EuclideanDomain<Additive, Multiplicative>>::quot_rem(&a, &b);
assert_eq!(mul(q, b), a);
assert_eq!(r, ModN::new(0));
}
}