// // # Example: The _complex_ _numbers_ ℚ. // // The complex numbers (ℚ) form a _field_. Rust does not have a // complex number type in the standard library so the complex number // type used here is the `Complex` type from the very handy `num` // crate. // use ::num::complex; use un_algebra::tests::*; use un_algebra::prelude::*; // // We use a newtype wrapper around the `num` crate `Complex` type to // work within Rust's _trait_ _coherence_ rules. To keep this example // simple-ish we limit real and imaginary components to `f64` values. // #[derive(Copy, Clone, Debug, PartialEq)] struct Complex(complex::Complex); // // Create a Complex instance from real and imaginary components. // impl Complex { pub fn new(re: f64, im: f64) -> Self { Self(complex::Complex::new(re, im)) } } // // Numeric equality (with f64 error values) for complex numbers. // impl NumEq for Complex { type Eps = f64; fn num_eq(&self, other: &Self, eps: &Self::Eps) -> bool { let re = self.0.re.num_eq(&other.0.re, eps); let im = self.0.im.num_eq(&other.0.im, eps); re && im } } // // Complex numbers form an additive magma with (complex) addition as the // operation. // impl AddMagma for Complex { fn add(&self, other: &Self) -> Self { Self(self.0 + other.0) } } // // Complex numbers form an additive semigroup as (complex) addition is // associative. // impl AddSemigroup for Complex {} // // Complex numbers form an additive monoid with (complex) zero as the // additive identity. // impl AddMonoid for Complex { fn zero() -> Self { Self::new(0.0, 0.0) } } // // Complex numbers form an additive group with (complex) negation as the // group inverse. // impl AddGroup for Complex { fn negate(&self) -> Self { Self(-self.0) } } // // Complex numbers form an additive commutative group as (complex) // addition is commutative. // impl AddComGroup for Complex {} // // Complex numbers form a multiplicative magma with (complex) // multiplication as the operation. // impl MulMagma for Complex { fn mul(&self, other: &Self) -> Self { Self(self.0 * other.0) } } // // Complex numbers form a multiplicative semigroup as (complex) // multiplication is associative. // impl MulSemigroup for Complex {} // // Complex numbers form a multiplicative monoid with (complex) one as // the multiplicative identity. // impl MulMonoid for Complex { fn one() -> Self { Self::new(1.0, 0.0) } } // // Complex numbers form a multiplicative group with inverse of non-zero // (complex) values as the group inverse. // impl MulGroup for Complex { fn is_invertible(&self) -> bool { !self.is_zero() } fn invert(&self) -> Self { Self(self.0.inv()) } } // // Complex numbers form a multiplicative commutative group as (complex) // multiplication is commutative. // impl MulComGroup for Complex {} // // Complex numbers form a ring. // impl Ring for Complex {} // // Complex numbers form a commutative ring. // impl ComRing for Complex {} // // Complex numbers (without zero) form a field with inverse of non-zero // (complex) values as the field inverse. // impl Field for Complex { fn invert(&self) -> Self { Self(self.0.inv()) } } // // Generate `proptest` arbitrary (i.e. boxed strategy) Complex values // from f64 real and imaginary components. Short function name to keep // generator expressions manageable. // fn cx() -> impl Strategy { let ranges = (-1.0e10_f64..1.0e10, -1.0e10_f64..1.0e10); ranges.prop_map(|(re, im)| Complex::new(re, im)) } #[cfg(test)] proptest! { #![proptest_config(config::standard())] #[test] fn add_closure([w, z] in [cx(), cx()]) { prop_assert!(NumAddMagmaLaws::num_closure(&w, &z, &F64_EPS)) } #[test] fn mul_closure([w, z] in [cx(), cx()]) { prop_assert!(NumMulMagmaLaws::num_closure(&w, &z, &F64_EPS)) } #[test] fn mul_associative([v, w, z] in [cx(), cx(), cx()]) { // Larger error term due to cancellation issues. prop_assert!(NumMulSemigroupLaws::num_associativity(&v, &w, &z, &(F64_EPS * 1e5))) } #[test] fn add_associative([v, w, z] in [cx(), cx(), cx()]) { // Larger error term due to cancellation issues. prop_assert!(NumAddSemigroupLaws::num_associativity(&v, &w, &z, &(F64_EPS * 1e5))) } #[test] fn left_add_identity(z in cx()) { prop_assert!(NumAddMonoidLaws::num_left_identity(&z, &F64_EPS)) } #[test] fn right_add_identity(z in cx()) { prop_assert!(NumAddMonoidLaws::num_left_identity(&z, &F64_EPS)) } #[test] fn left_mul_identity(z in cx()) { prop_assert!(NumMulMonoidLaws::num_left_identity(&z, &F64_EPS)) } #[test] fn right_mul_identity(z in cx()) { prop_assert!(NumMulMonoidLaws::num_right_identity(&z, &F64_EPS)) } #[test] fn left_add_inverse(z in cx()) { prop_assert!(NumAddGroupLaws::num_left_inverse(&z, &F64_EPS)) } #[test] fn right_add_inverse(z in cx()) { prop_assert!(NumAddGroupLaws::num_right_inverse(&z, &F64_EPS)) } #[test] fn left_mul_inverse(z in cx()) { prop_assume!(MulGroup::is_invertible(&z)); // Larger error term due to cancellation issues prop_assert!(NumMulGroupLaws::num_left_inverse(&z, &(F64_EPS * 1e3))) } #[test] fn right_inverse(z in cx()) { prop_assume!(MulGroup::is_invertible(&z)); prop_assert!(NumMulGroupLaws::num_right_inverse(&z, &F64_EPS)) } #[test] fn add_commute([w, z] in [cx(), cx()]) { prop_assert!(NumAddComGroupLaws::num_commutivity(&w, &z, &F64_EPS)) } #[test] fn mul_commute([w, z] in [cx(), cx()]) { prop_assert!(NumMulComGroupLaws::num_commutivity(&w, &z, &F64_EPS)) } #[test] fn left_distributivity([u, v, w] in [cx(), cx(), cx()]) { // Larger error term due to cancellation issues prop_assert!(NumRingLaws::num_left_distributivity(&u, &v, &w, &(F64_EPS * 1e5))) } #[test] fn right_distributivity([u, v, w] in [cx(), cx(), cx()]) { // Larger error term due to cancellation issues prop_assert!(NumRingLaws::num_right_distributivity(&u, &v, &w, &(F64_EPS * 1e5))) } #[test] fn left_absorption(x in cx()) { prop_assert!(NumRingLaws::num_left_absorption(&x, &F64_EPS)) } #[test] fn right_absorption(x in cx()) { prop_assert!(NumRingLaws::num_right_absorption(&x, &F64_EPS)) } #[test] fn left_negation((w, z) in (cx(), cx())) { prop_assert!(NumRingLaws::num_left_negation(&w, &z, &F64_EPS)) } #[test] fn right_negation((w, z) in (cx(), cx())) { prop_assert!(NumRingLaws::num_right_negation(&w, &z, &F64_EPS)) } #[test] fn mul_commutivity((x, y) in (cx(), cx())) { prop_assert!(NumComRingLaws::num_commutivity(&x, &y, &F64_EPS)) } #[test] fn field_left_inverse(z in cx()) { prop_assume!(Field::is_invertible(&z)); prop_assert!(NumFieldLaws::num_left_inverse(&z, &F64_EPS)) } #[test] fn field_right_inverse(z in cx()) { prop_assume!(Field::is_invertible(&z)); prop_assert!(NumFieldLaws::num_right_inverse(&z, &F64_EPS)) } #[test] fn zero_cancellation((w, z) in (cx(), cx())) { prop_assert!(NumFieldLaws::num_zero_cancellation(&w, &z, &F64_EPS)) } #[test] fn add_cancellation((v, w, z) in (cx(), cx(), cx())) { prop_assert!(NumFieldLaws::num_add_cancellation(&v, &w, &z, &F64_EPS)) } #[test] fn mul_cancellation((v, w, z) in (cx(), cx(), cx())) { prop_assert!(NumFieldLaws::num_mul_cancellation(&v, &w, &z, &F64_EPS)) } } fn main() { }