use batch_impl::batch_trait;
use crate::op::{Additive, Multiplicative};
use crate::tower::{
AbelianGroup, CommutativeRing, DivisionRing, Field, FiniteField, FreeModule, Group, Loop,
Magma, Module, Monoid, Quasigroup, Ring, Semigroup, Semiring, VectorSpace,
};
batch_trait! {
@int=[@u*,@i*];
@pri=[@num, bool];
@tr_mul=@trait<Multiplicative>;
@tr_am=@trait<Additive,Multiplicative>;
Magma:@int{
fn combine(&self, rhs: &Self) -> Self { self.wrapping_add(*rhs) }
}, @f*{
fn combine(&self, rhs: &Self) -> Self { *self + *rhs }
}, bool{
fn combine(&self, rhs: &Self) -> Self { *self != *rhs }
},
@tr_mul [@int{
fn combine(&self, rhs: &Self) -> Self { self.wrapping_mul(*rhs) }
}, @f*{
fn combine(&self, rhs: &Self) -> Self { *self * *rhs }
}, bool{
fn combine(&self, rhs: &Self) -> Self { *self && *rhs }
}];
Monoid:@int{
fn identity() -> Self { 0 }
}, @f*{
fn identity() -> Self { 0. }
}, bool{
fn identity() -> Self { false }
},
@tr_mul [
@int{
fn identity() -> Self { 1 }
}, @f*{
fn identity() -> Self { 1. }
}, bool{
fn identity() -> Self { true }
}];
Group: @int{
fn inverse(&self) -> Self { self.wrapping_neg() }
}, @f*{
fn inverse(&self) -> Self { -*self }
}, bool{
fn inverse(&self) -> Self { *self }
};
DivisionRing: @f*{
fn inv(&self) -> Self { 1.0 / *self }
}, bool{
fn inv(&self) -> Self { *self }
};
Semigroup: @trait[<Additive>,<Multiplicative>] @pri;
Quasigroup: @pri;
Loop: @pri;
AbelianGroup: @pri;
Semiring: @pri;
Ring: @pri;
CommutativeRing: @pri;
Field: [@f*, bool];
FiniteField: bool{
fn characteristic() -> u64 { 2 }
fn order() -> u64 { 2 }
};
VectorSpace: @tr_am [@f*,bool] where Self::Scalar: Field;
FreeModule: @tr_am @pri{
fn rank() -> usize { 1 }
fn basis_element(_i: usize) -> Self { <Self as Monoid<Multiplicative>>::identity() }
fn coordinate(&self, _i: usize) -> Self::Scalar { *self }
};
Module: @trait<Additive, Multiplicative,Scalar=Self> [
@int{
fn scale(s: &Self::Scalar, v: Self) -> Self { s.wrapping_mul(v) }
}, @f*{
fn scale(s: &Self::Scalar, v: Self) -> Self { s * v }
}, bool{
fn scale(s: &Self::Scalar, v: Self) -> Self { *s && v }
}
];
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tower::{AbelianGroup, DivisionRing, Field, Group, Magma, Monoid, Ring};
fn add<T: Magma<Additive>>(a: T, b: T) -> T {
<T as Magma<Additive>>::combine(&a, &b)
}
fn mul<T: Magma<Multiplicative>>(a: T, b: T) -> T {
<T as Magma<Multiplicative>>::combine(&a, &b)
}
fn assert_additive_abelian<T>(a: T, b: T)
where
T: AbelianGroup<Additive> + Copy + PartialEq + core::fmt::Debug,
{
assert_eq!(add(add(a, b), a), add(a, add(b, a)));
let id = <T as Monoid<Additive>>::identity();
assert_eq!(add(a, id), a);
assert_eq!(add(id, a), a);
let inv = <T as Group<Additive>>::inverse(&a);
assert_eq!(add(a, inv), id);
assert_eq!(add(a, b), add(b, a));
}
fn assert_multiplicative_monoid<T>(a: T, b: T, c: T)
where
T: Monoid<Multiplicative> + Copy + PartialEq + core::fmt::Debug,
{
assert_eq!(mul(mul(a, b), c), mul(a, mul(b, c)));
let one = <T as Monoid<Multiplicative>>::identity();
assert_eq!(mul(a, one), a);
assert_eq!(mul(one, a), a);
}
fn assert_ring<T>(a: T, b: T, c: T)
where
T: Ring<Additive, Multiplicative> + Copy + PartialEq + core::fmt::Debug,
{
let one = <T as Monoid<Multiplicative>>::identity();
assert_eq!(mul(a, one), a);
assert_eq!(mul(one, a), a);
assert_eq!(mul(a, add(b, c)), add(mul(a, b), mul(a, c)));
}
fn assert_field<T>(a: T)
where
T: Field<Additive, Multiplicative> + Copy + PartialEq + core::fmt::Debug,
{
let one = <T as Monoid<Multiplicative>>::identity();
let inv = <T as DivisionRing<Additive, Multiplicative>>::inv(&a);
assert_eq!(mul(a, inv), one);
assert_eq!(mul(inv, a), one);
}
#[test]
fn additive_ladder_holds_for_all_numerics() {
assert_additive_abelian(3u8, 5u8);
assert_additive_abelian(3u16, 5u16);
assert_additive_abelian(3u32, 5u32);
assert_additive_abelian(3u64, 5u64);
assert_additive_abelian(3u128, 5u128);
assert_additive_abelian(3usize, 5usize);
assert_additive_abelian(3i8, 5i8);
assert_additive_abelian(3i16, 5i16);
assert_additive_abelian(3i32, 5i32);
assert_additive_abelian(3i64, 5i64);
assert_additive_abelian(3i128, 5i128);
assert_additive_abelian(3isize, 5isize);
assert_additive_abelian(3.5f32, 1.25f32);
assert_additive_abelian(3.5f64, 1.25f64);
}
#[test]
fn multiplicative_monoid_holds_for_all_numerics() {
assert_multiplicative_monoid(3u8, 5u8, 7u8);
assert_multiplicative_monoid(3u16, 5u16, 7u16);
assert_multiplicative_monoid(3u32, 5u32, 7u32);
assert_multiplicative_monoid(3u64, 5u64, 7u64);
assert_multiplicative_monoid(3u128, 5u128, 7u128);
assert_multiplicative_monoid(3usize, 5usize, 7usize);
assert_multiplicative_monoid(3i8, 5i8, 7i8);
assert_multiplicative_monoid(3i16, 5i16, 7i16);
assert_multiplicative_monoid(3i32, 5i32, 7i32);
assert_multiplicative_monoid(3i64, 5i64, 7i64);
assert_multiplicative_monoid(3i128, 5i128, 7i128);
assert_multiplicative_monoid(3isize, 5isize, 7isize);
assert_multiplicative_monoid(3.0f32, 5.0f32, 7.0f32);
assert_multiplicative_monoid(3.0f64, 5.0f64, 7.0f64);
}
#[test]
fn ring_laws_hold_for_all_numerics() {
assert_ring(2u8, 3u8, 4u8);
assert_ring(2u16, 3u16, 4u16);
assert_ring(2u32, 3u32, 4u32);
assert_ring(2u64, 3u64, 4u64);
assert_ring(2u128, 3u128, 4u128);
assert_ring(2usize, 3usize, 4usize);
assert_ring(2i8, 3i8, 4i8);
assert_ring(2i16, 3i16, 4i16);
assert_ring(2i32, 3i32, 4i32);
assert_ring(2i64, 3i64, 4i64);
assert_ring(2i128, 3i128, 4i128);
assert_ring(2isize, 3isize, 4isize);
assert_ring(2.0f32, 3.0f32, 4.0f32);
assert_ring(2.0f64, 3.0f64, 4.0f64);
}
#[test]
fn field_laws_hold_for_floats() {
assert_field(2.0f32);
assert_field(0.25f32);
assert_field(-3.0f32);
assert_field(2.0f64);
assert_field(0.25f64);
assert_field(-3.0f64);
}
#[test]
fn bool_is_the_two_element_field() {
assert_additive_abelian(true, false);
assert_additive_abelian(false, false);
assert_multiplicative_monoid(true, false, true);
assert!(!add(true, true));
assert!(mul(true, true));
assert!(!<bool as Monoid<Additive>>::identity());
assert!(<bool as Monoid<Multiplicative>>::identity());
assert!(<bool as DivisionRing<Additive, Multiplicative>>::inv(&true));
}
#[test]
fn bool_is_finite_field_f2() {
use crate::tower::FiniteField;
assert_eq!(<bool as FiniteField<Additive, Multiplicative>>::characteristic(), 2);
assert_eq!(<bool as FiniteField<Additive, Multiplicative>>::order(), 2);
}
}