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.
//
// # Example: The dihedral symmetry group _D3_.
//
// The _symmetry_ _group_ of the equilateral triangle consists of six
// transforms, i.e. _rotations_ about the triangle center and
// _reflections_ through a vertex and the triangle center.
//
// This group is known as the _dihedral_ _group_ _D3_ [D3]. The group
// operation is concatenation of transforms. The group is *not*
// commutative (Abelian).
//
// [D3]: https://en.wikipedia.org/wiki/Dihedral_group_of_order_6
//
#![allow(non_snake_case)]

use proptest_derive::*;
use un_algebra::tests::*;
use un_algebra::prelude::*;


//
// The _dihedral_ symmetry group _D3_ consists of two kinds of
// transforms: rotations _Rn_ through n*120 degrees about the triangle
// center, and reflections _Ln_ through the line of the triangle center
// and vertices n = 1, 2, 3.
//
// To keep this example focused I've represented the D3 transforms as
// enum values, but we could also use 2x2 transform matrices with matrix
// multiplication as the group operator.
//
#[derive(Clone, Copy, PartialEq, Debug, Arbitrary)]
pub enum D3 {
  R0, R1, R2, L1, L2, L3
}


//
// D3 transforms form a magma.
//
impl Magma for D3 {

  // Magma operation is concatenation of transforms.
  fn op(&self, other: &Self) -> Self {

    // Magma operations as per the definition of D3. Redundant branch
    // expressions used for clarity.
    match (self, other) {
      (D3::R0, D3::R0) => D3::R0,
      (D3::R0, D3::R1) => D3::R1,
      (D3::R0, D3::R2) => D3::R2,
      (D3::R0, D3::L1) => D3::L1,
      (D3::R0, D3::L2) => D3::L2,
      (D3::R0, D3::L3) => D3::L3,

      (D3::R1, D3::R0) => D3::R1,
      (D3::R1, D3::R1) => D3::R2,
      (D3::R1, D3::R2) => D3::R0,
      (D3::R1, D3::L1) => D3::L3,
      (D3::R1, D3::L2) => D3::L1,
      (D3::R1, D3::L3) => D3::L2,

      (D3::R2, D3::R0) => D3::R2,
      (D3::R2, D3::R1) => D3::R0,
      (D3::R2, D3::R2) => D3::R1,
      (D3::R2, D3::L1) => D3::L2,
      (D3::R2, D3::L2) => D3::L3,
      (D3::R2, D3::L3) => D3::L1,

      (D3::L1, D3::R0) => D3::L1,
      (D3::L1, D3::R1) => D3::L2,
      (D3::L1, D3::R2) => D3::L3,
      (D3::L1, D3::L1) => D3::R0,
      (D3::L1, D3::L2) => D3::R1,
      (D3::L1, D3::L3) => D3::R2,

      (D3::L2, D3::R0) => D3::L2,
      (D3::L2, D3::R1) => D3::L3,
      (D3::L2, D3::R2) => D3::L1,
      (D3::L2, D3::L1) => D3::R2,
      (D3::L2, D3::L2) => D3::R0,
      (D3::L2, D3::L3) => D3::R1,

      (D3::L3, D3::R0) => D3::L3,
      (D3::L3, D3::R1) => D3::L1,
      (D3::L3, D3::R2) => D3::L2,
      (D3::L3, D3::L1) => D3::R1,
      (D3::L3, D3::L2) => D3::R2,
      (D3::L3, D3::L3) => D3::R0,
    }
  }
}


//
// D3 transforms form a semigroup.
//
impl Semigroup for D3 {}


//
// D3 transforms form a monoid.
//
impl Monoid for D3 {

  // The D3 rotation by zero element acts as the identity.
  fn id() -> Self {
    D3::R0
  }
}


//
// D3 transforms form a (symmetry) group.
//
impl Group for D3 {

  // D3 transform inverses as per the definition. Redundant branch
  // expressions used for clarity.
  fn inverse(&self) -> Self {
    match self {
      D3::R0 => D3::R0,
      D3::R1 => D3::R2,
      D3::R2 => D3::R1,
      D3::L1 => D3::L1,
      D3::L2 => D3::L2,
      D3::L3 => D3::L3,
    }
  }
}


// Generative tests of D3 algebraic axioms and properties.
proptest! {
  #![proptest_config(config::standard())]


  #[test]
  fn closure_s((x, y) in any::<(D3, D3)>()) {
    prop_assert!(MagmaLaws::closure(&x, &y))
  }


  #[test]
  fn closure((x, y) in any::<(D3, D3)>()) {
    prop_assert!(MagmaLaws::closure(&x, &y))
  }


  #[test]
  fn associativity((x, y, z) in any::<(D3, D3, D3)>()) {
    prop_assert!(x.associativity(&y, &z))
  }


  #[test]
  fn left_identity(x in any::<D3>()) {
    prop_assert!(x.left_identity())
  }


  #[test]
  fn left_identity_a3(xs in any::<[D3; 3]>()) {
    prop_assert!(xs.left_identity())
  }


  #[test]
  fn right_identity(x in any::<D3>()) {
    prop_assert!(x.right_identity())
  }


  #[test]
  fn right_identity_a2(xs in any::<[D3; 2]>()) {
    prop_assert!(xs.right_identity())
  }


  #[test]
  fn left_inverse(x in any::<D3>()) {
    prop_assert!(x.left_inverse())
  }


  #[test]
  fn left_inverse_a2(xs in any::<[D3; 2]>()) {
    prop_assert!(xs.left_inverse())
  }


  #[test]
  fn right_inverse(x in any::<D3>()) {
    prop_assert!(x.right_inverse())
  }


  #[test]
  fn right_inverse_a2(xs in any::<[D3; 2]>()) {
    prop_assert!(xs.right_inverse())
  }
}


fn main() {
}