use core::fmt::Debug;
use proptest::prelude::*;
use crate::op::{Additive, Multiplicative, Operator};
use crate::tower::{
AbelianGroup, Band, BilinearForm, CommutativeRing, ComplementedLattice, DistributiveLattice,
DivisionRing, EuclideanDomain, Field, FieldExtension, FreeModule, Group, IntegralDomain,
Lattice, LieAlgebra, LinearMap, Magma, Module, Monoid, OrderedField, PositiveDefinite,
Quasigroup, Ring, Semiring, StarSemiring, SymmetricBilinearForm, TensorProduct, VectorSpace,
};
pub fn associativity<Op: Operator, T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Magma<Op> + Copy + PartialEq + Debug,
{
let lhs = T::combine(&T::combine(&a, &b), &c);
let rhs = T::combine(&a, &T::combine(&b, &c));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn monoid_identity<Op: Operator, T>(a: T) -> Result<(), TestCaseError>
where
T: Monoid<Op> + Copy + PartialEq + Debug,
{
let e = T::identity();
prop_assert_eq!(T::combine(&e, &a), a);
prop_assert_eq!(T::combine(&a, &e), a);
Ok(())
}
pub fn group_inverse<Op: Operator, T>(a: T) -> Result<(), TestCaseError>
where
T: Group<Op> + Copy + PartialEq + Debug,
{
let e = T::identity();
let inv = T::inverse(&a);
prop_assert_eq!(T::combine(&a, &inv), e);
prop_assert_eq!(T::combine(&inv, &a), e);
Ok(())
}
pub fn quasigroup_latin_square<Op: Operator, T>(a: T, b: T) -> Result<(), TestCaseError>
where
T: Quasigroup<Op> + Group<Op> + Copy + PartialEq + Debug,
{
let x = T::combine(&T::inverse(&a), &b);
prop_assert_eq!(T::combine(&a, &x), b);
let y = T::combine(&b, &T::inverse(&a));
prop_assert_eq!(T::combine(&y, &a), b);
Ok(())
}
pub fn commutativity<Op: Operator, T>(a: T, b: T) -> Result<(), TestCaseError>
where
T: AbelianGroup<Op> + Copy + PartialEq + Debug,
{
prop_assert_eq!(T::combine(&a, &b), T::combine(&b, &a));
Ok(())
}
pub fn distributivity<Oa: Operator, Om: Operator, T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Semiring<Oa, Om> + Copy + PartialEq + Debug,
{
let add = |x: &T, y: &T| <T as Magma<Oa>>::combine(x, y);
let mul = |x: &T, y: &T| <T as Magma<Om>>::combine(x, y);
prop_assert_eq!(mul(&a, &add(&b, &c)), add(&mul(&a, &b), &mul(&a, &c)));
prop_assert_eq!(mul(&add(&b, &c), &a), add(&mul(&b, &a), &mul(&c, &a)));
Ok(())
}
pub fn additive_abelian_group_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: AbelianGroup<Additive> + Copy + PartialEq + Debug,
{
associativity::<Additive, _>(a, b, c)?;
monoid_identity::<Additive, _>(a)?;
group_inverse::<Additive, _>(a)?;
commutativity::<Additive, _>(a, b)?;
Ok(())
}
pub fn multiplicative_monoid_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Monoid<Multiplicative> + Copy + PartialEq + Debug,
{
associativity::<Multiplicative, _>(a, b, c)?;
monoid_identity::<Multiplicative, _>(a)?;
Ok(())
}
pub fn semiring_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Semiring<Additive, Multiplicative> + Copy + PartialEq + Debug,
{
distributivity::<Additive, Multiplicative, _>(a, b, c)?;
Ok(())
}
pub fn ring_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Ring<Additive, Multiplicative> + Copy + PartialEq + Debug,
{
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
semiring_laws(a, b, c)?;
Ok(())
}
pub fn commutative_ring_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: CommutativeRing<Additive, Multiplicative> + Copy + PartialEq + Debug,
{
ring_laws(a, b, c)?;
prop_assert_eq!(
<T as Magma<Multiplicative>>::combine(&a, &b),
<T as Magma<Multiplicative>>::combine(&b, &a)
);
Ok(())
}
pub fn field_laws<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: Field<Additive, Multiplicative> + Copy + PartialEq + Debug,
{
commutative_ring_laws(a, b, c)?;
let zero = <T as Monoid<Additive>>::identity();
let one = <T as Monoid<Multiplicative>>::identity();
prop_assert_ne!(zero, one);
if a != zero {
let inv = <T as DivisionRing<Additive, Multiplicative>>::inv(&a);
prop_assert_eq!(<T as Magma<Multiplicative>>::combine(&a, &inv), one);
}
Ok(())
}
pub fn module_laws<Oa: Operator, Om: Operator, T>(
s: T::Scalar, t: T::Scalar, u: T, v: T,
) -> Result<(), TestCaseError>
where
T: Module<Oa, Om> + Copy + PartialEq + Debug,
T::Scalar: Copy + PartialEq + Debug,
{
let add = |x: &T, y: &T| <T as Magma<Oa>>::combine(x, y);
let scalar_add = |x: &T::Scalar, y: &T::Scalar| <T::Scalar as Magma<Oa>>::combine(x, y);
let scalar_mul = |x: &T::Scalar, y: &T::Scalar| <T::Scalar as Magma<Om>>::combine(x, y);
prop_assert_eq!(T::scale(&s, add(&u, &v)), add(&T::scale(&s, u), &T::scale(&s, v)));
prop_assert_eq!(T::scale(&scalar_add(&s, &t), v), add(&T::scale(&s, v), &T::scale(&t, v)));
prop_assert_eq!(T::scale(&scalar_mul(&s, &t), v), T::scale(&s, T::scale(&t, v)));
let one = <T::Scalar as Monoid<Om>>::identity();
prop_assert_eq!(T::scale(&one, v), v);
Ok(())
}
pub fn module_laws_default<T>(s: T::Scalar, t: T::Scalar, u: T, v: T) -> Result<(), TestCaseError>
where
T: Module<Additive, Multiplicative> + Copy + PartialEq + Debug,
T::Scalar: Copy + PartialEq + Debug,
{
module_laws::<Additive, Multiplicative, _>(s, t, u, v)
}
pub fn lattice_absorption<T>(a: T, b: T) -> Result<(), TestCaseError>
where
T: Lattice + Copy + PartialEq + Debug,
{
prop_assert_eq!(a.meet(&a.join(&b)), a);
prop_assert_eq!(a.join(&a.meet(&b)), a);
Ok(())
}
pub fn band_idempotent<Op: Operator, T>(a: T) -> Result<(), TestCaseError>
where
T: Band<Op> + Copy + PartialEq + Debug,
{
prop_assert_eq!(T::combine(&a, &a), a);
Ok(())
}
pub fn lie_alternating<Op: Operator, T>(a: T) -> Result<(), TestCaseError>
where
T: LieAlgebra<Op> + Magma<Additive> + Monoid<Additive> + Copy + PartialEq + Debug,
{
let zero = <T as Monoid<Additive>>::identity();
prop_assert_eq!(T::bracket(&a, &a), zero);
Ok(())
}
pub fn lie_jacobi<Op: Operator, T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: LieAlgebra<Op> + Magma<Additive> + Monoid<Additive> + Copy + PartialEq + Debug,
{
let zero = <T as Monoid<Additive>>::identity();
let abc = T::bracket(&a, &T::bracket(&b, &c));
let bca = T::bracket(&b, &T::bracket(&c, &a));
let cab = T::bracket(&c, &T::bracket(&a, &b));
prop_assert_eq!(
<T as Magma<Additive>>::combine(&<T as Magma<Additive>>::combine(&abc, &bca), &cab),
zero
);
Ok(())
}
pub fn euclidean_division<Oa: Operator, Om: Operator, T>(a: T, b: T) -> Result<(), TestCaseError>
where
T: EuclideanDomain<Oa, Om> + Copy + PartialEq + Debug + Monoid<Oa>,
{
let zero = <T as Monoid<Oa>>::identity();
if b == zero {
return Ok(());
}
let (q, r) = a.quot_rem(&b);
let back = <T as Magma<Om>>::combine(&q, &b);
prop_assert_eq!(<T as Magma<Oa>>::combine(&back, &r), a);
if r != zero {
prop_assert!(r.euclidean_norm() < b.euclidean_norm());
}
Ok(())
}
pub fn integral_domain_laws<Oa: Operator, Om: Operator, T>(a: T, b: T) -> Result<(), TestCaseError>
where
T: IntegralDomain<Oa, Om> + Copy + PartialEq + Debug + Monoid<Oa> + Monoid<Om>,
{
let zero = <T as Monoid<Oa>>::identity();
let one = <T as Monoid<Om>>::identity();
prop_assert_ne!(zero, one);
let ab = <T as Magma<Om>>::combine(&a, &b);
if ab == zero {
prop_assert!(a == zero || b == zero);
}
Ok(())
}
pub fn lattice_distributivity<T>(a: T, b: T, c: T) -> Result<(), TestCaseError>
where
T: DistributiveLattice + PartialEq + Debug,
{
prop_assert_eq!(a.meet(&b.join(&c)), a.meet(&b).join(&a.meet(&c)));
prop_assert_eq!(a.join(&b.meet(&c)), a.join(&b).meet(&a.join(&c)));
Ok(())
}
pub fn complemented_lattice_laws<T>(a: T) -> Result<(), TestCaseError>
where
T: ComplementedLattice + PartialEq + Debug,
{
prop_assert_eq!(a.join(&a.complement()), T::top());
prop_assert_eq!(a.meet(&a.complement()), T::bottom());
Ok(())
}
pub fn linear_map_additive<Oa: Operator, Om: Operator, M>(
f: M, u: M::Domain, v: M::Domain,
) -> Result<(), TestCaseError>
where
M: LinearMap<Oa, Om> + Clone,
M::Domain: Magma<Oa> + PartialEq + Debug,
M::Codomain: Magma<Oa> + PartialEq + Debug,
{
let lhs = f.apply(&<M::Domain as Magma<Oa>>::combine(&u, &v));
let rhs = <M::Codomain as Magma<Oa>>::combine(&f.apply(&u), &f.apply(&v));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn linear_map_scalar<Oa: Operator, Om: Operator, M>(
f: M, s: <M::Domain as Module<Oa, Om>>::Scalar, u: M::Domain,
) -> Result<(), TestCaseError>
where
M: LinearMap<Oa, Om> + Clone,
M::Domain: Module<Oa, Om> + Clone + PartialEq + Debug,
M::Codomain: Module<Oa, Om> + PartialEq + Debug,
<M::Domain as Module<Oa, Om>>::Scalar: Magma<Om> + Copy,
{
let su = <M::Domain as Module<Oa, Om>>::scale(&s, u.clone());
let lhs = f.apply(&su);
let rhs = <M::Codomain as Module<Oa, Om>>::scale(&s, f.apply(&u));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn star_semiring_laws<Oa: Operator, Om: Operator, T>(a: T) -> Result<(), TestCaseError>
where
T: StarSemiring<Oa, Om> + PartialEq + Debug,
{
let one = <T as Monoid<Om>>::identity();
let a_star = a.star();
let a_a_star = <T as Magma<Om>>::combine(&a, &a_star);
prop_assert_eq!(<T as Magma<Oa>>::combine(&one, &a_a_star), a.star());
let a_star_a = <T as Magma<Om>>::combine(&a_star, &a);
prop_assert_eq!(<T as Magma<Oa>>::combine(&one, &a_star_a), a.star());
Ok(())
}
pub fn free_module_basis<Oa: Operator, Om: Operator, T>(
i: usize, j: usize,
) -> Result<(), TestCaseError>
where
T: FreeModule<Oa, Om>,
T::Scalar: Monoid<Oa> + Monoid<Om> + PartialEq + Debug,
{
let one = <T::Scalar as Monoid<Om>>::identity();
let zero = <T::Scalar as Monoid<Oa>>::identity();
let e_i = T::basis_element(i);
if i == j {
prop_assert_eq!(e_i.coordinate(j), one);
} else {
prop_assert_eq!(e_i.coordinate(j), zero);
}
Ok(())
}
pub fn field_extension_trace_additive<Oa: Operator, Om: Operator, T>(
x: T, y: T,
) -> Result<(), TestCaseError>
where
T: FieldExtension<Oa, Om> + Copy + Magma<Oa>,
T::BaseField: Magma<Oa> + PartialEq + Debug,
{
let xy = <T as Magma<Oa>>::combine(&x, &y);
let tr = <T::BaseField as Magma<Oa>>::combine(&x.trace(), &y.trace());
prop_assert_eq!(xy.trace(), tr);
Ok(())
}
pub fn field_extension_norm_multiplicative<Oa: Operator, Om: Operator, T>(
x: T, y: T,
) -> Result<(), TestCaseError>
where
T: FieldExtension<Oa, Om> + Copy + Magma<Om>,
T::BaseField: Magma<Om> + PartialEq + Debug,
{
let xy = <T as Magma<Om>>::combine(&x, &y);
let n = <T::BaseField as Magma<Om>>::combine(&x.norm(), &y.norm());
prop_assert_eq!(xy.norm(), n);
Ok(())
}
pub fn bilinear_form_additive_left<F, S>(f: F, u1: S, u2: S, v: S) -> Result<(), TestCaseError>
where
F: BilinearForm<Space = S>,
<S as Module<Additive, Multiplicative>>::Scalar: Field<Additive, Multiplicative>,
S: VectorSpace<Additive, Multiplicative> + Magma<Additive> + Clone + PartialEq + Debug,
F::Scalar: Magma<Additive> + PartialEq + Debug,
{
let lhs = f.apply(&<S as Magma<Additive>>::combine(&u1, &u2), &v);
let rhs = <F::Scalar as Magma<Additive>>::combine(&f.apply(&u1, &v), &f.apply(&u2, &v));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn bilinear_form_additive_right<F, S>(f: F, u: S, v1: S, v2: S) -> Result<(), TestCaseError>
where
F: BilinearForm<Space = S>,
<S as Module<Additive, Multiplicative>>::Scalar: Field<Additive, Multiplicative>,
S: VectorSpace<Additive, Multiplicative> + Magma<Additive> + Clone + PartialEq + Debug,
F::Scalar: Magma<Additive> + PartialEq + Debug,
{
let lhs = f.apply(&u, &<S as Magma<Additive>>::combine(&v1, &v2));
let rhs = <F::Scalar as Magma<Additive>>::combine(&f.apply(&u, &v1), &f.apply(&u, &v2));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn bilinear_form_scalar_left<F, S>(f: F, s: F::Scalar, u: S, v: S) -> Result<(), TestCaseError>
where
F: BilinearForm<Space = S, Scalar = <S as Module<Additive, Multiplicative>>::Scalar>,
<S as Module<Additive, Multiplicative>>::Scalar: Field<Additive, Multiplicative>,
S: VectorSpace<Additive, Multiplicative> + Clone + PartialEq + Debug,
F::Scalar: Magma<Multiplicative> + PartialEq + Debug + Copy,
{
let su = <S as Module<Additive, Multiplicative>>::scale(&s, u.clone());
let lhs = f.apply(&su, &v);
let rhs = <F::Scalar as Magma<Multiplicative>>::combine(&s, &f.apply(&u, &v));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn bilinear_form_symmetric<F, S>(f: F, u: S, v: S) -> Result<(), TestCaseError>
where
F: SymmetricBilinearForm<Space = S>,
<S as Module<Additive, Multiplicative>>::Scalar: Field<Additive, Multiplicative>,
S: VectorSpace<Additive, Multiplicative> + PartialEq + Debug,
F::Scalar: PartialEq + Debug,
{
prop_assert_eq!(f.apply(&u, &v), f.apply(&v, &u));
Ok(())
}
pub fn bilinear_form_positive_definite<F, S>(f: F, v: S) -> Result<(), TestCaseError>
where
F: PositiveDefinite<Space = S>,
<S as Module<Additive, Multiplicative>>::Scalar: Field<Additive, Multiplicative>,
S: VectorSpace<Additive, Multiplicative> + Monoid<Additive> + Clone + PartialEq + Debug,
F::Scalar: OrderedField<Additive, Multiplicative> + PartialOrd + Debug,
{
let zero = <S as Monoid<Additive>>::identity();
if v != zero {
let b = f.apply(&v, &v);
prop_assert!(b > <F::Scalar as Monoid<Additive>>::identity());
}
Ok(())
}
pub fn tensor_bilinear_left<Op: Operator, T>(
u1: T::Left, u2: T::Left, v: T::Right,
) -> Result<(), TestCaseError>
where
T: TensorProduct<Op> + Magma<Op> + PartialEq + Debug,
T::Left: Magma<Op> + Clone,
T::Right: Clone,
{
let lhs = T::tensor_product(<T::Left as Magma<Op>>::combine(&u1, &u2), v.clone());
let rhs =
<T as Magma<Op>>::combine(&T::tensor_product(u1, v.clone()), &T::tensor_product(u2, v));
prop_assert_eq!(lhs, rhs);
Ok(())
}
pub fn tensor_bilinear_right<Op: Operator, T>(
u: T::Left, v1: T::Right, v2: T::Right,
) -> Result<(), TestCaseError>
where
T: TensorProduct<Op> + Magma<Op> + PartialEq + Debug,
T::Left: Clone,
T::Right: Magma<Op> + Clone,
{
let lhs = T::tensor_product(u.clone(), <T::Right as Magma<Op>>::combine(&v1, &v2));
let rhs =
<T as Magma<Op>>::combine(&T::tensor_product(u.clone(), v1), &T::tensor_product(u, v2));
prop_assert_eq!(lhs, rhs);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::complex::Complex;
use crate::modn::ModN;
proptest! {
#[test]
fn u8_laws(a: u8, b: u8, c: u8) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn u16_laws(a: u16, b: u16, c: u16) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn u32_laws(a: u32, b: u32, c: u32) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn u64_laws(a: u64, b: u64, c: u64) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn u128_laws(a: u128, b: u128, c: u128) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn usize_laws(a: usize, b: usize, c: usize) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn i8_laws(a: i8, b: i8, c: i8) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn i16_laws(a: i16, b: i16, c: i16) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn i32_laws(a: i32, b: i32, c: i32) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn i64_laws(a: i64, b: i64, c: i64) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn i128_laws(a: i128, b: i128, c: i128) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn isize_laws(a: isize, b: isize, c: isize) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
commutative_ring_laws(a, b, c)?;
}
#[test]
fn f32_laws(a: f32, b: f32, c: f32) {
prop_assume!(a.is_finite() && b.is_finite() && c.is_finite());
monoid_identity::<Additive, _>(a)?;
group_inverse::<Additive, _>(a)?;
commutativity::<Additive, _>(a, b)?;
monoid_identity::<Multiplicative, _>(a)?;
prop_assert_eq!(
<f32 as Magma<Multiplicative>>::combine(&a, &b),
<f32 as Magma<Multiplicative>>::combine(&b, &a)
);
if a.abs() >= f32::MIN_POSITIVE {
let inv = <f32 as DivisionRing<Additive, Multiplicative>>::inv(&a);
prop_assert!((a * inv - 1.0).abs() < 1e-6);
}
}
#[test]
fn f64_laws(a: f64, b: f64, c: f64) {
prop_assume!(a.is_finite() && b.is_finite() && c.is_finite());
monoid_identity::<Additive, _>(a)?;
group_inverse::<Additive, _>(a)?;
commutativity::<Additive, _>(a, b)?;
monoid_identity::<Multiplicative, _>(a)?;
prop_assert_eq!(
<f64 as Magma<Multiplicative>>::combine(&a, &b),
<f64 as Magma<Multiplicative>>::combine(&b, &a)
);
if a.abs() >= f64::MIN_POSITIVE {
let inv = <f64 as DivisionRing<Additive, Multiplicative>>::inv(&a);
prop_assert!((a * inv - 1.0).abs() < 1e-12);
}
}
#[test]
fn tuple_laws(a: (u8, i16), b: (u8, i16), c: (u8, i16)) {
additive_abelian_group_laws(a, b, c)?;
multiplicative_monoid_laws(a, b, c)?;
ring_laws(a, b, c)?;
}
#[test]
fn triple_laws(a: (u8, i16, f32), b: (u8, i16, f32), c: (u8, i16, f32)) {
monoid_identity::<Additive, _>(a)?;
group_inverse::<Additive, _>(a)?;
commutativity::<Additive, _>(a, b)?;
monoid_identity::<Multiplicative, _>(a)?;
}
#[test]
fn u8_module_laws(s: u8, t: u8, u: u8, v: u8) {
module_laws_default(s, t, u, v)?;
}
#[test]
fn tuple_module_laws(s: u8, t: u8, u: (u8, u8), v: (u8, u8)) {
module_laws_default(s, t, u, v)?;
}
#[test]
fn tuple_vecspace_laws(s: f64, t: f64, u: (f64, f64), v: (f64, f64)) {
prop_assume!(
s.is_finite() && t.is_finite() && u.0.is_finite() && u.1.is_finite()
&& v.0.is_finite() && v.1.is_finite()
);
let one = 1.0f64;
let scale = |w: (f64, f64)| <(f64, f64) as Module<Additive, Multiplicative>>::scale(&one, w);
prop_assert_eq!(scale(u), u);
}
#[test]
fn bool_field_laws(a: bool, b: bool, c: bool) {
field_laws(a, b, c)?;
module_laws_default(a, b, a, b)?;
}
#[test]
fn u8_lattice_laws(a: u8, b: u8, c: u8) {
lattice_absorption(a, b)?;
lattice_distributivity(a, b, c)?;
}
#[test]
fn tuple_lattice_laws(a: (u8, u8), b: (u8, u8)) {
lattice_absorption(a, b)?;
}
#[test]
fn bool_boolean_algebra(a: bool) {
complemented_lattice_laws(a)?;
}
#[test]
fn bool_band_and_euclidean(a: bool, i: i32, j: i32) {
band_idempotent::<Multiplicative, _>(a)?;
euclidean_division(i, j)?;
lie_alternating::<Additive, _>(i)?;
lie_jacobi::<Additive, _>(i, j, 3)?;
}
#[test]
fn tuple_free_module_laws(i in 0usize..2, j in 0usize..2) {
free_module_basis::<Additive, Multiplicative, (f64, f64)>(i, j)?;
}
#[test]
fn f64_bilinear_form_laws(f: f64, u: f64, v: f64) {
prop_assume!(f.is_finite() && u.is_finite() && v.is_finite() && u.abs() > 1e-100);
bilinear_form_symmetric(f, u, v)?;
bilinear_form_positive_definite(f, u)?;
}
#[test]
fn tensor_product_laws(a: i32, b: i32, c: i32) {
tensor_bilinear_left::<Additive, Tensor2>(a, b, c)?;
tensor_bilinear_right::<Additive, Tensor2>(a, b, c)?;
}
#[test]
fn modn_field_laws(a: usize, b: usize, c: usize) {
let a = ModN::<97>::new(a);
let b = ModN::<97>::new(b);
let c = ModN::<97>::new(c);
field_laws(a, b, c)?;
integral_domain_laws(a, b)?;
euclidean_division(a, b)?;
}
#[test]
fn linear_map_laws(s: usize, u: usize, v: usize) {
let s = ModN::<97>::new(s);
let u = ModN::<97>::new(u);
let v = ModN::<97>::new(v);
let f = Scale(s);
linear_map_additive::<Additive, Multiplicative, Scale>(f, u, v)?;
linear_map_scalar::<Additive, Multiplicative, Scale>(f, s, u)?;
}
}
#[derive(Clone, Copy)]
struct Scale(ModN<97>);
impl LinearMap<Additive, Multiplicative> for Scale {
type Domain = ModN<97>;
type Codomain = ModN<97>;
fn apply(&self, v: &Self::Domain) -> Self::Codomain {
<ModN<97> as Module<Additive, Multiplicative>>::scale(&self.0, *v)
}
}
#[test]
fn complex_field_extension_exact() {
let x = Complex::new(3.0f64, 4.0);
let y = Complex::new(1.0, 2.0);
assert_eq!(Complex::new(4.0, 6.0).trace(), x.trace() + y.trace());
assert_eq!(Complex::new(-5.0, 10.0).norm(), x.norm() * y.norm());
}
#[derive(Clone, PartialEq, Debug)]
struct Tensor2(i32, i32);
impl Magma<Additive> for Tensor2 {
fn combine(&self, rhs: &Self) -> Self {
Tensor2(self.0.wrapping_add(rhs.0), self.1.wrapping_add(rhs.1))
}
}
impl TensorProduct<Additive> for Tensor2 {
type Left = i32;
type Right = i32;
fn tensor_product(left: i32, right: i32) -> Self {
Tensor2(left.wrapping_mul(right), 0)
}
}
}