// // # 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::()) { 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::()) { 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::()) { 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::()) { prop_assert!(x.right_inverse()) } #[test] fn right_inverse_a2(xs in any::<[D3; 2]>()) { prop_assert!(xs.right_inverse()) } } fn main() { }