use std::ops::{Add, Mul};
use approx::ApproxEq;
use general::{Operator, Additive, Multiplicative, Inverse, Identity};
pub trait AbstractMagma<O: Operator>: Sized + Clone {
fn operate(&self, right: &Self) -> Self;
#[inline]
fn op(&self, _: O, lhs: &Self) -> Self {
self.operate(lhs)
}
}
pub trait AbstractQuasigroup<O: Operator>
: PartialEq + AbstractMagma<O> + Inverse<O>
{
fn prop_inv_is_latin_square_approx(args: (Self, Self)) -> bool
where Self: ApproxEq {
let (a, b) = args;
relative_eq!(a, a.operate(&b.inverse()).operate(&b)) &&
relative_eq!(a, a.operate(&b.operate(&b.inverse())))
}
fn prop_inv_is_latin_square(args: (Self, Self)) -> bool
where Self: Eq {
let (a, b) = args;
a == a.operate(&b.inverse()).operate(&b) &&
a == a.operate(&b.operate(&b.inverse()))
}
}
pub trait AbstractSemigroup<O: Operator> : PartialEq + AbstractMagma<O> {
fn prop_is_associative_approx(args: (Self, Self, Self)) -> bool
where Self: ApproxEq {
let (a, b, c) = args;
relative_eq!(a.operate(&b).operate(&c), a.operate(&b.operate(&c)))
}
fn prop_is_associative(args: (Self, Self, Self)) -> bool
where Self: Eq {
let (a, b, c) = args;
a.operate(&b).operate(&c) == a.operate(&b.operate(&c))
}
}
pub trait AbstractLoop<O: Operator>
: AbstractQuasigroup<O>
+ Identity<O>
{ }
pub trait AbstractMonoid<O: Operator>
: AbstractSemigroup<O>
+ Identity<O>
{
fn prop_operating_identity_element_is_noop_approx(a: Self) -> bool
where Self: ApproxEq {
relative_eq!(a.operate(&Self::identity()), a) &&
relative_eq!(Self::identity().operate(&a), a)
}
fn prop_operating_identity_element_is_noop(a: Self) -> bool
where Self: Eq {
a.operate(&Self::identity()) == a &&
Self::identity().operate(&a) == a
}
}
pub trait AbstractGroup<O: Operator>
: AbstractLoop<O> + AbstractMonoid<O>
{ }
pub trait AbstractGroupAbelian<O: Operator>
: AbstractGroup<O> {
fn prop_is_commutative_approx(args: (Self, Self)) -> bool
where Self: ApproxEq {
let (a, b) = args;
relative_eq!(a.operate(&b), b.operate(&a))
}
fn prop_is_commutative(args: (Self, Self)) -> bool
where Self: Eq {
let (a, b) = args;
a.operate(&b) == b.operate(&a)
}
}
macro_rules! impl_magma(
($M:ty; $op: ident; $($T:ty),* $(,)*) => {
$(impl AbstractMagma<$M> for $T {
#[inline]
fn operate(&self, lhs: &Self) -> Self {
self.$op(*lhs)
}
})*
}
);
impl_magma!(Additive; add; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_magma!(Multiplicative; mul; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractQuasigroup<Additive>; i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractQuasigroup<Multiplicative>; f32, f64);
impl_marker!(AbstractMonoid<Additive>; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractMonoid<Multiplicative>; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractSemigroup<Additive>; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractSemigroup<Multiplicative>; u8, u16, u32, u64, i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractLoop<Additive>; i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractLoop<Multiplicative>; f32, f64);
impl_marker!(AbstractGroup<Additive>; i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractGroup<Multiplicative>; f32, f64);
impl_marker!(AbstractGroupAbelian<Additive>; i8, i16, i32, i64, f32, f64);
impl_marker!(AbstractGroupAbelian<Multiplicative>; f32, f64);