un_algebra 0.9.0

Simple implementations of selected abstract algebraic structures--including groups, rings, and fields. Intended for self-study of abstract algebra concepts and not for production use.
//!
//! Algebraic rings.
//!
//! An algebraic _ring_ `R`, is an _additive_ _commutative_ _group_
//! **and** a _multiplicative_ _monoid_, and therefore has both
//! _addition_ `+` and _multiplication_ `×` operators. In addition to
//! group and monoid axioms ring multiplication is required to
//! _distribute_ over addition.
//!
//! Because of their additive group aspect, rings have a unique `0`
//! additive identity element. Not all authors require rings to have a
//! multiplicative identity element (`1`), but in `un_algebra` they do,
//! i.e. `un_algebra` rings are _rings_ _with_ _unity_.
//!
//! # Axioms
//!
//! ```text
//! ∀x, y, z ∈ R
//!
//! Distributivity (left): x × (y + z) = (x × y) + (x × z).
//! Distributivity (right): (x + y) × z = (x × z) + (y × z).
//! ```
//!
//! # References
//!
//! See [references] for a formal definition of a ring.
//!
#![doc(include = "../../doc/references.md")]

use crate::monoid::*;
use crate::helpers::*;
use crate::numeric::*;
use crate::com_group::*;


///
/// An algebraic _ring_.
///
pub trait Ring: AddComGroup + MulMonoid {}


///
/// Laws (axioms and properties) of rings.
///
pub trait RingLaws: Ring {

  /// The axiom of _left_ _distributivity_ (multiplication).
  fn left_distributivity(&self, x: &Self, y: &Self) -> bool {
    self.mul(&x.add(y)) == self.mul(x).add(&self.mul(y))
  }


  /// The axiom of _right_ _distributivity_ (multiplication).
  fn right_distributivity(&self, x: &Self, y: &Self) -> bool {
    x.add(y).mul(self) == x.mul(self).add(&y.mul(self))
  }


  /// The axiom of _distributivity_ (multiplication).
  fn distributivity(&self, x: &Self, y: &Self) -> bool {
    self.left_distributivity(x, y) && self.right_distributivity(x, y)
  }


  /// The property of _left_ _zero_ _absorption_.
  fn left_absorption(&self) -> bool {
    self.mul(&Self::zero()) == Self::zero()
  }


  /// The property of _right_ _zero_ _absorption_.
  fn right_absorption(&self) -> bool {
    Self::zero().mul(self) == Self::zero()
  }


  /// The property of _zero_ _absorption_.
  fn absorption(&self) -> bool {
    self.left_absorption() && self.right_absorption()
  }


  /// The property of _left_ _negation_ (multiplicative).
  fn left_negation(&self, x: &Self) -> bool {
    self.negate().mul(x) == self.mul(x).negate()
  }


  /// The property of _right_ _negation_ (multiplicative).
  fn right_negation(&self, x: &Self) -> bool {
    self.mul(&x.negate()) == self.mul(x).negate()
  }


  /// The property of _negation_ (multiplicative).
  fn negation(&self, x: &Self) -> bool {
    self.left_negation(x) && self.right_negation(x)
  }


  /// The property of _left_ _zero_ _divisors_.
  fn left_zero_divisors(&self, x: &Self) -> bool {
    imply(self.mul(x).is_zero(), self.is_zero() || x.is_zero())
  }


  /// The property of _right_ _zero_ _divisors_.
  fn right_zero_divisors(&self, x: &Self) -> bool {
    imply(x.mul(self).is_zero(), self.is_zero() || x.is_zero())
  }


  /// The property of _zero_ _divisors_.
  fn zero_divisors(&self, x: &Self) -> bool {
    self.left_zero_divisors(x) && self.right_zero_divisors(x)
  }
}


///
/// Blanket implementation of ring laws for ring implementations.
///
impl<R: Ring> RingLaws for R {}


///
/// Laws (axioms and properties) of rings.
///
pub trait NumRingLaws: NumEq + Ring {

  /// The numeric axiom of _left_ _distributivity_ (multiplication).
  fn num_left_distributivity(&self, x: &Self, y: &Self, eps: &Self::Eps) -> bool {
    self.mul(&x.add(y)).num_eq(&self.mul(x).add(&self.mul(y)), eps)
  }


  /// The numeric axiom of _right_ _distributivity_ (multiplication).
  fn num_right_distributivity(&self, x: &Self, y: &Self, eps: &Self::Eps) -> bool {
    x.add(y).mul(self).num_eq(&x.mul(self).add(&y.mul(self)), eps)
  }


  /// The numeric axiom of _distributivity_ (multiplication).
  fn num_distributivity(&self, x: &Self, y: &Self, eps: &Self::Eps) -> bool {
    self.num_left_distributivity(x, y, eps) && self.num_right_distributivity(x, y, eps)
  }


  /// The numeric property of _left_ _zero_ _absorption_.
  fn num_left_absorption(&self, eps: &Self::Eps) -> bool {
    self.mul(&Self::zero()).num_eq(&Self::zero(), eps)
  }


  /// The numeric property of _right_ _zero_ _absorption_.
  fn num_right_absorption(&self, eps: &Self::Eps) -> bool {
    Self::zero().mul(self).num_eq(&Self::zero(), eps)
  }


  /// The numeric property of _zero_ _absorption_.
  fn num_absorption(&self, eps: &Self::Eps) -> bool {
    self.num_left_absorption(eps) && self.num_right_absorption(eps)
  }


  /// The numeric property of _left_ _negation_ (multiplicative).
  fn num_left_negation(&self, x: &Self, eps: &Self::Eps) -> bool {
    self.negate().mul(x).num_eq(&self.mul(x).negate(), eps)
  }


  /// The numeric property of _right_ _negation_ (multiplicative).
  fn num_right_negation(&self, x: &Self, eps: &Self::Eps) -> bool {
    self.mul(&x.negate()).num_eq(&self.mul(x).negate(), eps)
  }


  /// The numeric property of _negation_ (multiplicative).
  fn num_negation(&self, x: &Self, eps: &Self::Eps) -> bool {
    self.num_left_negation(x, eps) && self.num_right_negation(x, eps)
  }


  /// The numeric property of _left_ _zero_ _divisors_.
  fn num_left_zero_divisors(&self, x: &Self, _: &Self::Eps) -> bool {
    imply(self.mul(x).is_zero(), self.is_zero() || x.is_zero())
  }


  /// The numeric property of _right_ _zero_ _divisors_.
  fn num_right_zero_divisors(&self, x: &Self, _: &Self::Eps) -> bool {
    imply(x.mul(self).is_zero(), self.is_zero() || x.is_zero())
  }


  /// The numeric property of _zero_ _divisors_.
  fn num_zero_divisors(&self, x: &Self, eps: &Self::Eps) -> bool {
    self.num_left_zero_divisors(x, eps) && self.num_right_zero_divisors(x, eps)
  }
}


///
/// Blanket implementation of numeric ring laws for ring implementations.
///
impl<R: NumEq + Ring> NumRingLaws for R {}


///
/// Define `Ring` implementations for numeric types.
///
macro_rules! numeric_ring {
  ($type:ty) => {
    impl Ring for $type {}
  };

  ($type:ty, $($others:ty),+) => {
    numeric_ring! {$type}
    numeric_ring! {$($others),+}
  };
}


// Only signed integers and floats form a ring.
numeric_ring! {
  i8, i16, i32, i64, i128, isize, f32, f64
}


///
/// 0-tuples form a ring.
///
impl Ring for () {}


///
/// 1-tuples form a ring when their items do.
///
impl<A: Ring> Ring for (A,) {}


///
/// 2-tuples form a ring when their items do.
///
impl<A: Ring, B: Ring> Ring for (A, B) {}


///
/// 3-tuples form a ring when their items do.
///
impl<A: Ring, B: Ring, C: Ring> Ring for (A, B, C) {}


///
/// Define `Ring` implementations for arrays. Maybe not needed if Rust
/// had _const_ _generics_.
///
macro_rules! array_ring {
  ($size:expr) => {
    impl<T: Copy + Ring> Ring for [T; $size] {}
  };

  ($size:expr, $($others:expr),+) => {
    array_ring! {$size}
    array_ring! {$($others),+}
  };
}


// Array ring types.
array_ring! {
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
}


// Module unit tests are in a sub-module.
#[cfg(test)]
mod tests;