use crate::operations::*;
use crate::sets::Set;
pub trait AdditiveMagma: Set + ClosedAdd + ClosedAddAssign {}
pub trait MultiplicativeMagma: Set + ClosedMul + ClosedMulAssign {}
pub trait AdditiveSemigroup: AdditiveMagma + AssociativeAddition {}
pub trait MultiplicativeSemigroup: MultiplicativeMagma + AssociativeMultiplication {}
pub trait AdditiveMonoid: AdditiveSemigroup + ClosedZero {}
pub trait MultiplicativeMonoid: MultiplicativeSemigroup + ClosedOne {}
pub trait AdditiveGroup: AdditiveMonoid + ClosedNeg + ClosedSub + ClosedSubAssign {}
pub trait MultiplicativeGroup:
MultiplicativeMonoid + ClosedInv + ClosedDiv + ClosedDivAssign
{
}
pub trait AdditiveAbelianGroup: AdditiveGroup + CommutativeAddition {}
pub trait MultiplicativeAbelianGroup: MultiplicativeGroup + CommutativeMultiplication {}
impl<T: Set + ClosedAdd + ClosedAddAssign> AdditiveMagma for T {}
impl<T: Set + ClosedMul + ClosedMulAssign> MultiplicativeMagma for T {}
impl<T: AdditiveMagma + AssociativeAddition> AdditiveSemigroup for T {}
impl<T: MultiplicativeMagma + AssociativeMultiplication> MultiplicativeSemigroup for T {}
impl<T: AdditiveSemigroup + ClosedZero> AdditiveMonoid for T {}
impl<T: MultiplicativeSemigroup + ClosedOne> MultiplicativeMonoid for T {}
impl<T: AdditiveMonoid + ClosedNeg + ClosedSub + ClosedSubAssign> AdditiveGroup for T {}
impl<T: MultiplicativeMonoid + ClosedInv + ClosedDiv + ClosedDivAssign> MultiplicativeGroup for T {}
impl<T: AdditiveGroup + CommutativeAddition> AdditiveAbelianGroup for T {}
impl<T: MultiplicativeGroup + CommutativeMultiplication> MultiplicativeAbelianGroup for T {}
#[cfg(test)]
mod tests {
use super::*;
use crate::operations::{
AssociativeAddition, AssociativeMultiplication, CommutativeAddition,
CommutativeMultiplication,
};
use num_traits::{Inv, One, Zero};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
#[derive(Debug, Clone, Copy, PartialEq)]
struct IntegerMod7(i32);
impl IntegerMod7 {
fn new(value: i32) -> Self {
let normalized = value.rem_euclid(7);
Self(normalized)
}
}
impl Add for IntegerMod7 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.0 + rhs.0)
}
}
impl AddAssign for IntegerMod7 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for IntegerMod7 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.0 - rhs.0)
}
}
impl SubAssign for IntegerMod7 {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Neg for IntegerMod7 {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new(-self.0)
}
}
impl Zero for IntegerMod7 {
fn zero() -> Self {
Self(0)
}
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl Mul for IntegerMod7 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.0 * rhs.0)
}
}
impl MulAssign for IntegerMod7 {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl One for IntegerMod7 {
fn one() -> Self {
Self(1)
}
}
impl Inv for IntegerMod7 {
type Output = Self;
fn inv(self) -> Self::Output {
match self.0 {
0 => panic!("Multiplicative inverse of 0 is undefined in Z/7Z"),
1 => Self(1), 2 => Self(4), 3 => Self(5), 4 => Self(2), 5 => Self(3), 6 => Self(6), _ => unreachable!("All values should be normalized to 0-6"),
}
}
}
impl Div for IntegerMod7 {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
if rhs.0 == 0 {
panic!("Division by zero in Z/7Z");
}
self * rhs.inv()
}
}
impl DivAssign for IntegerMod7 {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl CommutativeAddition for IntegerMod7 {}
impl AssociativeAddition for IntegerMod7 {}
impl CommutativeMultiplication for IntegerMod7 {}
impl AssociativeMultiplication for IntegerMod7 {}
#[derive(Debug, Clone, Copy, PartialEq)]
enum Quaternion {
One, I, J, K, NegOne, NegI, NegJ, NegK, }
impl Mul for Quaternion {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
use Quaternion::*;
match (self, rhs) {
(One, x) | (x, One) => x,
(NegOne, x) => match x {
One => NegOne,
I => NegI,
J => NegJ,
K => NegK,
NegOne => One,
NegI => I,
NegJ => J,
NegK => K,
},
(x, NegOne) => match x {
One => NegOne,
I => NegI,
J => NegJ,
K => NegK,
NegOne => One,
NegI => I,
NegJ => J,
NegK => K,
},
(I, NegI) | (NegI, I) => One,
(J, NegJ) | (NegJ, J) => One,
(K, NegK) | (NegK, K) => One,
(I, I) => NegOne,
(J, J) => NegOne,
(K, K) => NegOne,
(NegI, NegI) => NegOne,
(NegJ, NegJ) => NegOne,
(NegK, NegK) => NegOne,
(I, J) => K,
(J, K) => I,
(K, I) => J,
(J, I) => NegK,
(K, J) => NegI,
(I, K) => NegJ,
(I, NegJ) => NegK,
(I, NegK) => J,
(J, NegI) => K,
(J, NegK) => NegI,
(K, NegI) => NegJ,
(K, NegJ) => I,
(NegI, J) => NegK,
(NegI, K) => NegJ,
(NegJ, I) => K,
(NegJ, K) => I,
(NegK, I) => J,
(NegK, J) => NegI,
(NegI, NegJ) => K,
(NegJ, NegK) => I,
(NegK, NegI) => J,
(NegJ, NegI) => NegK,
(NegK, NegJ) => NegI,
(NegI, NegK) => NegJ,
}
}
}
impl One for Quaternion {
fn one() -> Self {
Quaternion::One
}
}
impl MulAssign for Quaternion {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl Inv for Quaternion {
type Output = Self;
fn inv(self) -> Self::Output {
use Quaternion::*;
match self {
One => One,
I => NegI,
J => NegJ,
K => NegK,
NegOne => NegOne,
NegI => I,
NegJ => J,
NegK => K,
}
}
}
impl Div for Quaternion {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
#[allow(clippy::suspicious_arithmetic_impl)]
let result = self * rhs.inv();
result
}
}
impl DivAssign for Quaternion {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl AssociativeMultiplication for Quaternion {}
#[test]
fn test_additive_magma() {
fn assert_is_additive_magma<T: AdditiveMagma>(_: &T) {}
assert_is_additive_magma(&IntegerMod7::new(3));
assert_is_additive_magma(&5i32);
assert_is_additive_magma(&std::f64::consts::PI);
}
#[test]
fn test_multiplicative_magma() {
fn assert_is_multiplicative_magma<T: MultiplicativeMagma>(_: &T) {}
assert_is_multiplicative_magma(&IntegerMod7::new(3));
assert_is_multiplicative_magma(&Quaternion::I);
assert_is_multiplicative_magma(&5i32);
assert_is_multiplicative_magma(&std::f64::consts::PI);
}
#[test]
fn test_additive_semigroup() {
fn assert_is_additive_semigroup<T: AdditiveSemigroup>(_: &T) {}
assert_is_additive_semigroup(&IntegerMod7::new(3));
assert_is_additive_semigroup(&5i32);
assert_is_additive_semigroup(&std::f64::consts::PI);
let a = IntegerMod7::new(2);
let b = IntegerMod7::new(3);
let c = IntegerMod7::new(5);
assert_eq!((a + b) + c, a + (b + c)); }
#[test]
fn test_multiplicative_semigroup() {
fn assert_is_multiplicative_semigroup<T: MultiplicativeSemigroup>(_: &T) {}
assert_is_multiplicative_semigroup(&IntegerMod7::new(3));
assert_is_multiplicative_semigroup(&Quaternion::I);
assert_is_multiplicative_semigroup(&5i32);
assert_is_multiplicative_semigroup(&std::f64::consts::PI);
let a = IntegerMod7::new(2);
let b = IntegerMod7::new(3);
let c = IntegerMod7::new(5);
assert_eq!((a * b) * c, a * (b * c));
use Quaternion::*;
let x = I;
let y = J;
let z = K;
assert_eq!((x * y) * z, x * (y * z)); }
#[test]
fn test_additive_monoid() {
fn assert_is_additive_monoid<T: AdditiveMonoid>(_: &T) {}
assert_is_additive_monoid(&IntegerMod7::new(3));
assert_is_additive_monoid(&5i32);
assert_is_additive_monoid(&std::f64::consts::PI);
let a = IntegerMod7::new(4);
let zero = IntegerMod7::zero();
assert_eq!(a + zero, a); assert_eq!(zero + a, a); }
#[test]
fn test_multiplicative_monoid() {
fn assert_is_multiplicative_monoid<T: MultiplicativeMonoid>(_: &T) {}
assert_is_multiplicative_monoid(&IntegerMod7::new(3));
assert_is_multiplicative_monoid(&Quaternion::I);
assert_is_multiplicative_monoid(&5i32);
assert_is_multiplicative_monoid(&std::f64::consts::PI);
let a = IntegerMod7::new(4);
let one = IntegerMod7::one();
assert_eq!(a * one, a); assert_eq!(one * a, a);
let q = Quaternion::J;
let q_one = Quaternion::one();
assert_eq!(q * q_one, q); assert_eq!(q_one * q, q); }
#[test]
fn test_additive_group() {
fn assert_is_additive_group<T: AdditiveGroup>(_: &T) {}
assert_is_additive_group(&IntegerMod7::new(3));
assert_is_additive_group(&5i32);
assert_is_additive_group(&std::f64::consts::PI);
let a = IntegerMod7::new(4);
let neg_a = -a; let zero = IntegerMod7::zero();
assert_eq!(a + neg_a, zero); assert_eq!(neg_a + a, zero); }
#[test]
fn test_multiplicative_group() {
fn assert_is_multiplicative_group<T: MultiplicativeGroup>(_: &T) {}
assert_is_multiplicative_group(&IntegerMod7::new(3));
assert_is_multiplicative_group(&Quaternion::I);
assert_is_multiplicative_group(&std::f64::consts::PI);
let a = IntegerMod7::new(3); let a_inv = a.inv(); let one = IntegerMod7::one();
assert_eq!(a * a_inv, one); assert_eq!(a_inv * a, one);
use Quaternion::*;
let q = J; let q_inv = q.inv(); let q_one = One;
assert_eq!(q * q_inv, q_one);
assert_eq!(q_inv * q, q_one);
}
#[test]
fn test_additive_abelian_group() {
fn assert_is_additive_abelian_group<T: AdditiveAbelianGroup>(_: &T) {}
assert_is_additive_abelian_group(&IntegerMod7::new(3));
assert_is_additive_abelian_group(&5i32);
assert_is_additive_abelian_group(&std::f64::consts::PI);
let a = IntegerMod7::new(2);
let b = IntegerMod7::new(5);
assert_eq!(a + b, b + a); }
#[test]
fn test_multiplicative_abelian_group() {
fn assert_is_multiplicative_abelian_group<T: MultiplicativeAbelianGroup>(_: &T) {}
assert_is_multiplicative_abelian_group(&IntegerMod7::new(3));
assert_is_multiplicative_abelian_group(&std::f64::consts::PI);
let a = IntegerMod7::new(2);
let b = IntegerMod7::new(5);
assert_eq!(a * b, b * a);
let q1 = Quaternion::I;
let q2 = Quaternion::J;
assert_ne!(q1 * q2, q2 * q1); }
#[test]
fn test_group_laws() {
let elements: Vec<IntegerMod7> = (0..7).map(IntegerMod7::new).collect();
for &a in &elements {
for &b in &elements {
let c = a + b;
assert!(
elements.contains(&c),
"Closure law violated: {} + {} = {} is not in Z/7Z",
a.0,
b.0,
c.0
);
}
}
for &a in &elements {
for &b in &elements {
for &c in &elements {
assert_eq!(
(a + b) + c,
a + (b + c),
"Associativity law violated: ({} + {}) + {} ≠ {} + ({} + {})",
a.0,
b.0,
c.0,
a.0,
b.0,
c.0
);
}
}
}
let zero = IntegerMod7::zero();
for &a in &elements {
assert_eq!(a + zero, a, "Identity law violated: {} + 0 ≠ {}", a.0, a.0);
assert_eq!(zero + a, a, "Identity law violated: 0 + {} ≠ {}", a.0, a.0);
}
for &a in &elements {
let neg_a = -a;
assert_eq!(
a + neg_a,
zero,
"Inverse law violated: {} + ({}) ≠ 0",
a.0,
neg_a.0
);
assert_eq!(
neg_a + a,
zero,
"Inverse law violated: ({}) + {} ≠ 0",
neg_a.0,
a.0
);
}
for &a in &elements {
for &b in &elements {
assert_eq!(
a + b,
b + a,
"Commutativity law violated: {} + {} ≠ {} + {}",
a.0,
b.0,
b.0,
a.0
);
}
}
}
}