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.
//!
//! Fields.
//!
//! An algebraic _field_ is a _commutative_ _ring_ (with identity) `R`,
//! where each _invertible_ field element `f` has a unique
//! _multiplicative_ _inverse_ `f^-1`. The inverse operation is called
//! _invert_.
//!
//! # Axioms
//!
//! ```text
//! ∀g, 1 ∈ R
//!
//! Inverse: ∃g^-1 ∈ R: g × g^-1 = g^-1 × g = 1.
//! ```
//!
//! # References
//!
//! See [references] for a formal definition of a field.
//!
#![doc(include = "../../doc/references.md")]

use crate::helpers::*;
use crate::numeric::*;
use crate::com_ring::*;


///
/// An algebraic _field_.
///
pub trait Field: ComRing {

  /// The unique _multiplicative_ _inverse_ of a field element. The
  /// inverse is only defined for _invertible_ field elements.
  fn invert(&self) -> Self;


  /// Test for an _invertible_ field element.
  fn is_invertible(&self) -> bool {
    *self != Self::zero()
  }


  /// The multiplicative "division" of two field elements.
  fn div(&self, other: &Self) -> Self {
    self.mul(&other.invert())
  }
}


///
/// Laws (axioms and properties) of fields.
///
pub trait FieldLaws: Field {

  /// The _left_ _multiplicative_ _inverse_ axiom.
  fn left_inverse(&self) -> bool {
    self.invert().mul(self) == Self::one()
  }


  /// The _right_ _multiplicative_ _inverse_ axiom.
  fn right_inverse(&self) -> bool {
    self.mul(&self.invert()) == Self::one()
  }


  /// The _two_ _sided_ _multiplicative_ _inverse_ axiom.
  fn inverse(&self) -> bool {
    self.left_inverse() && self.right_inverse()
  }


  /// The property of _additive_ _cancellation_.
  fn add_cancellation(&self, x: &Self, y: &Self) -> bool {
    imply(self.add(x) == y.add(x), self == y)
  }


  /// The property of _multiplicative_ _cancellation_.
  fn mul_cancellation(&self, x: &Self, y: &Self) -> bool {
    if x.is_zero() {return true;}

    imply(self.mul(x) == y.mul(x), self == y)
  }


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


///
/// Blanket implementation of field laws for field implementations.
///
impl<F: Field> FieldLaws for F {}


///
/// Numeric laws (axioms and properties) of fields.
///
pub trait NumFieldLaws: NumEq + Field {

  /// The numeric _left_ _multiplicative_ _inverse_ axiom.
  fn num_left_inverse(&self, eps: &Self::Eps) -> bool {
    self.invert().mul(self).num_eq(&Self::one(), eps)
  }


  /// The numeric _right_ _multiplicative_ _inverse_ axiom.
  fn num_right_inverse(&self, eps: &Self::Eps) -> bool {
    self.mul(&self.invert()).num_eq(&Self::one(), eps)
  }


  /// The numeric _two_ _sided_ _multiplicative_ _inverse_ axiom.
  fn num_inverse(&self, eps: &Self::Eps) -> bool {
    self.num_left_inverse(eps) && self.num_right_inverse(eps)
  }


  /// The numeric property of _additive_ _cancellation_.
  fn num_add_cancellation(&self, x: &Self, y: &Self, eps: &Self::Eps) -> bool {
    imply(self.add(x).num_eq(&y.add(x), eps), self.num_eq(&y, eps))
  }


  /// The numeric property of _multiplicative_ _cancellation_.
  fn num_mul_cancellation(&self, x: &Self, y: &Self, eps: &Self::Eps) -> bool {
    if x.is_zero() {return true;}

    imply(self.mul(x).num_eq(&y.mul(x), eps), self.num_eq(&y, eps))
  }


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


///
/// Blanket implementation of numeric field laws for field
/// implementations.
///
impl<F: NumEq + Field> NumFieldLaws for F {}


///
/// Define `Field` implementations for floating point types. Probably
/// not needed if Rust had a `Float` super-trait.
///
macro_rules! float_field {
  ($type:ty) => {
    impl Field for $type {

      /// Inversion is just floating point inverse.
      fn invert(&self) -> Self {
        self.recip()
      }
    }
  };

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


// Field floating point types.
float_field! {
  f32, f64
}


///
/// 0-tuples form a field.
///
impl Field for () {

  /// Inverted value can only be `()`.
  fn invert(&self) -> Self {}


  /// The only value is invertible.
  fn is_invertible(&self) -> bool {
    true
  }
}


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

  /// Inversion is by matching element.
  fn invert(&self) -> Self {
    (self.0.invert(), )
  }


  /// Invertibility is across the tuple.
  fn is_invertible(&self) -> bool {
    self.0.is_invertible()
  }
}


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

  /// Inversion is by matching element.
  fn invert(&self) -> Self {
    (self.0.invert(), self.1.invert())
  }


  /// Invertibility is across the tuple.
  fn is_invertible(&self) -> bool {
    self.0.is_invertible() && self.1.is_invertible()
  }
}


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

  /// Inversion is by matching element.
  fn invert(&self) -> Self {
    let (a, b, c) = self;

    (a.invert(), b.invert(), c.invert())
  }


  /// Invertibility is across the tuple.
  fn is_invertible(&self) -> bool {
    let (a, b, c) = self;

    a.is_invertible() && b.is_invertible() && c.is_invertible()
  }
}


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

      // Invert each array element.
      fn invert(&self) -> Self {
        self.map(&|&x| x.invert())
      }


      // Test each array element for invertibility.
      fn is_invertible(&self) -> bool {
        self.all(&|&x| x.is_invertible())
      }
    }
  };

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


// Array field types.
array_field! {
  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;