#![doc(include = "../../doc/references.md")]
use crate::magma::*;
use crate::helpers::*;
use crate::numeric::*;
pub trait Quasigroup: MulMagma {
fn is_divisor(&self) -> bool;
fn ldiv(&self, other: &Self) -> Self;
fn rdiv(&self, other: &Self) -> Self;
}
pub trait QuasigroupLaws: Quasigroup {
fn left_lcancellation(&self, x: &Self) -> bool {
self.ldiv(&self.mul(x)) == *x
}
fn right_lcancellation(&self, x: &Self) -> bool {
self.mul(&self.ldiv(x)) == *x
}
fn left_rcancellation(&self, x: &Self) -> bool {
self.rdiv(x).mul(x) == *self
}
fn right_rcancellation(&self, x: &Self) -> bool {
self.mul(x).rdiv(x) == *self
}
}
impl<Q: Quasigroup> QuasigroupLaws for Q {}
pub trait NumQuasigroupLaws: NumEq + Quasigroup {
fn num_left_lcancellation(&self, x: &Self, eps: &Self::Eps) -> bool {
self.ldiv(&self.mul(x)).num_eq(&x, eps)
}
fn num_right_lcancellation(&self, x: &Self, eps: &Self::Eps) -> bool {
self.mul(&self.ldiv(x)).num_eq(&x, eps)
}
fn num_left_rcancellation(&self, x: &Self, eps: &Self::Eps) -> bool {
self.rdiv(x).mul(x).num_eq(&self, eps)
}
fn num_right_rcancellation(&self, x: &Self, eps: &Self::Eps) -> bool {
self.mul(x).rdiv(x).num_eq(&self, eps)
}
}
impl<Q: NumEq + Quasigroup> NumQuasigroupLaws for Q {}
macro_rules! float_quasigroup {
($type:ty) => {
impl Quasigroup for $type {
fn is_divisor(&self) -> bool {
*self != 0.0
}
fn ldiv(&self, other: &Self) -> Self {
other / self
}
fn rdiv(&self, other: &Self) -> Self {
self / other
}
}
};
($type:ty, $($others:ty),+) => {
float_quasigroup! {$type}
float_quasigroup! {$($others),+}
};
}
float_quasigroup! {
f32, f64
}
impl Quasigroup for () {
fn is_divisor(&self) -> bool {
true
}
fn ldiv(&self, _: &Self) -> Self {}
fn rdiv(&self, _: &Self) -> Self {}
}
impl<A: Quasigroup> Quasigroup for (A,) {
fn is_divisor(&self) -> bool {
self.0.is_divisor()
}
fn ldiv(&self, other: &Self) -> Self {
(self.0.ldiv(&other.0),)
}
fn rdiv(&self, other: &Self) -> Self {
(self.0.rdiv(&other.0),)
}
}
impl<A: Quasigroup, B: Quasigroup> Quasigroup for (A, B) {
fn is_divisor(&self) -> bool {
self.0.is_divisor() && self.1.is_divisor()
}
fn ldiv(&self, other: &Self) -> Self {
(self.0.ldiv(&other.0), self.1.ldiv(&other.1))
}
fn rdiv(&self, other: &Self) -> Self {
(self.0.rdiv(&other.0), self.1.rdiv(&other.1))
}
}
impl<A: Quasigroup, B: Quasigroup, C: Quasigroup> Quasigroup for (A, B, C) {
fn is_divisor(&self) -> bool {
let (a, b, c) = self;
a.is_divisor() && b.is_divisor() && c.is_divisor()
}
fn ldiv(&self, other: &Self) -> Self {
(self.0.ldiv(&other.0), self.1.ldiv(&other.1), self.2.ldiv(&other.2))
}
fn rdiv(&self, other: &Self) -> Self {
(self.0.rdiv(&other.0), self.1.rdiv(&other.1), self.2.rdiv(&other.2))
}
}
macro_rules! array_quasigroup {
($size:expr) => {
impl<T: Copy + Quasigroup> Quasigroup for [T; $size] {
fn is_divisor(&self) -> bool {
self.all(&|&x| x.is_divisor())
}
fn ldiv(&self, other: &Self) -> Self {
self.map_with(other, &|&x, &y| x.ldiv(&y))
}
fn rdiv(&self, other: &Self) -> Self {
self.map_with(other, &|&x, &y| x.rdiv(&y))
}
}
};
($size:expr, $($others:expr),+) => {
array_quasigroup! {$size}
array_quasigroup! {$($others),+}
};
}
array_quasigroup! {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
}
#[cfg(test)]
mod tests;