use std::fmt::Debug;
use crate::test::cartesian_power;
pub fn monoid<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
zero: S, ) -> Result<(), &'static str> {
semigroup(items, f)?;
identity(items, f, zero)?;
Ok(())
}
pub fn semigroup<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
associativity(items, f)?;
Ok(())
}
pub fn semiring<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
g: &impl Fn(S, S) -> S,
zero: S, one: S, ) -> Result<(), &'static str> {
commutative_monoid(items, f, zero.clone())?;
monoid(items, g, one)?;
absorbing_element(items, g, zero)?;
distributive(items, f, g)?;
Ok(())
}
pub fn ring<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
g: &impl Fn(S, S) -> S,
zero: S, one: S, b: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
semiring(items, f, g, zero.clone(), one)?;
inverse(items, f, zero, b)?;
Ok(())
}
pub fn integral_domain<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
g: &impl Fn(S, S) -> S,
zero: S, one: S, inverse_f: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
commutative_ring(items, f, g, zero.clone(), one, inverse_f)?;
no_nonzero_zero_divisors(items, g, zero)?;
Ok(())
}
pub fn no_nonzero_zero_divisors<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
zero: S,
) -> Result<(), &'static str> {
for a in items {
for b in items {
if *a != zero && *b != zero {
if f(a.clone(), b.clone()) == zero {
return Err("No nonzero zero divisors check failed.");
};
if f(b.clone(), a.clone()) == zero {
return Err("No nonzero zero divisors check failed.");
};
}
}
}
Ok(())
}
pub fn commutative_ring<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S, g: &impl Fn(S, S) -> S, zero: S, one: S, inverse_f: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
semiring(items, f, g, zero.clone(), one)?;
inverse(items, f, zero, inverse_f)?;
commutativity(items, g)?;
Ok(())
}
pub fn field<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
g: &impl Fn(S, S) -> S,
zero: S, one: S, inverse_f: &impl Fn(S) -> S,
inverse_g: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
commutative_ring(items, f, g, zero.clone(), one.clone(), inverse_f)?;
nonzero_inverse(items, g, one, zero, inverse_g)?;
Ok(())
}
pub fn commutative_monoid<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
zero: S,
) -> Result<(), &'static str> {
monoid(items, f, zero)?;
commutativity(items, f)?;
Ok(())
}
pub fn group<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
zero: S, b: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
monoid(items, f, zero.clone())?;
inverse(items, f, zero, b)?;
Ok(())
}
pub fn abelian_group<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
zero: S,
b: &impl Fn(S) -> S,
) -> Result<(), &'static str> {
group(items, f, zero, b)?;
commutativity(items, f)?;
Ok(())
}
pub fn distributive<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: &impl Fn(S, S) -> S,
g: &impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
left_distributes(items, f, g)?;
right_distributes(items, f, g)?;
Ok(())
}
pub fn left_distributes<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
g: impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
for [a, b, c] in cartesian_power(items) {
if g(a.clone(), f(b.clone(), c.clone()))
!= f(g(a.clone(), b.clone()), g(a.clone(), c.clone()))
{
return Err("Left distributive property check failed.");
}
}
Ok(())
}
pub fn right_distributes<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
g: impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
for [a, b, c] in cartesian_power(items) {
if g(f(b.clone(), c.clone()), a.clone())
!= f(g(b.clone(), a.clone()), g(c.clone(), a.clone()))
{
return Err("Right distributive property check failed.");
}
}
Ok(())
}
pub fn absorbing_element<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
z: S, ) -> Result<(), &'static str> {
for a in items {
if f(a.clone(), z.clone()) != z.clone() {
return Err("Absorbing element property check failed.");
}
if f(z.clone(), a.clone()) != z.clone() {
return Err("Absorbing element property check failed.");
}
}
Ok(())
}
pub fn inverse<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
e: S, b: impl Fn(S) -> S,
) -> Result<(), &'static str> {
for a in items {
if f(a.clone(), b(a.clone())) != e {
return Err("Inverse check failed.");
}
if f(b(a.clone()), a.clone()) != e {
return Err("Inverse check failed.");
}
}
Ok(())
}
pub fn nonzero_inverse<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
e: S,
zero: S,
b: impl Fn(S) -> S,
) -> Result<(), &'static str> {
for a in items {
if *a != zero {
if f(a.clone(), b(a.clone())) != e {
return Err("Nonzero inverse check failed.");
}
if f(b(a.clone()), a.clone()) != e {
return Err("Nonzero inverse check failed.");
}
}
}
Ok(())
}
pub fn identity<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
e: S,
) -> Result<(), &'static str> {
for a in items {
if f(e.clone(), a.clone()) != a.clone() {
return Err("Left Identity check failed.");
}
if f(a.clone(), e.clone()) != a.clone() {
return Err("Right Identity check failed.");
}
}
Ok(())
}
pub fn associativity<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
for [a, b, c] in cartesian_power(items) {
if f(a.clone(), f(b.clone(), c.clone())) != f(f(a.clone(), b.clone()), c.clone())
{
return Err("Associativity check failed.");
}
}
Ok(())
}
pub fn commutativity<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
for [x, y] in cartesian_power(items) {
if f(x.clone(), y.clone()) != f(y.clone(), x.clone()) {
return Err("Commutativity check failed.");
}
}
Ok(())
}
pub fn idempotency<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
) -> Result<(), &'static str> {
for x in items {
if f(x.clone(), x.clone()) != x.clone() {
return Err("Idempotency check failed.");
}
}
Ok(())
}
pub fn linearity<S: Debug + PartialEq + Clone, R: Debug + PartialEq + Clone>(
items: &[S],
f: impl Fn(S, S) -> S,
g: impl Fn(R, R) -> R,
q: impl Fn(S) -> R,
) -> Result<(), &'static str> {
for [a, b] in cartesian_power(items) {
if q(f(a.clone(), b.clone())) != g(q(b.clone()), q(a.clone())) {
return Err("Linearity check failed.");
}
}
Ok(())
}
pub fn bilinearity<
S: Debug + PartialEq + Clone,
R: Debug + PartialEq + Clone,
T: Debug + PartialEq + Clone,
>(
items_f: &[S],
items_h: &[T],
f: impl Fn(S, S) -> S,
h: impl Fn(T, T) -> T,
g: impl Fn(R, R) -> R,
q: impl Fn(S, T) -> R,
) -> Result<(), &'static str> {
for [a, b] in cartesian_power(items_f) {
for [c, d] in cartesian_power(items_h) {
if q(f(a.clone(), b.clone()), c.clone())
!= g(q(a.clone(), c.clone()), q(b.clone(), c.clone()))
|| q(a.clone(), h(c.clone(), d.clone()))
!= g(q(a.clone(), c.clone()), q(a.clone(), d.clone()))
{
return Err("Bilinearity check failed.");
}
}
}
Ok(())
}
pub fn get_single_function_properties<S: Debug + PartialEq + Clone, const N: usize>(
items: &[S; N],
f: impl Fn(S, S) -> S,
e: S,
b: impl Fn(S) -> S,
z: S,
) -> Vec<String> {
let mut properties_satisfied: Vec<String> = Vec::new();
if associativity(items, &f).is_ok() {
properties_satisfied.push("associativity".to_owned());
}
if commutativity(items, &f).is_ok() {
properties_satisfied.push("commutativity".to_owned());
}
if idempotency(items, &f).is_ok() {
properties_satisfied.push("idempotency".to_owned());
}
if identity(items, &f, e.clone()).is_ok() {
properties_satisfied.push("identity".to_owned());
}
if inverse(items, &f, e, b).is_ok() {
properties_satisfied.push("inverse".to_owned());
}
if absorbing_element(items, &f, z).is_ok() {
properties_satisfied.push("absorbing_element".to_owned());
}
properties_satisfied
}
#[cfg(test)]
mod test {
use std::collections::HashSet;
use crate::algebra::*;
static TEST_ITEMS: &[u32; 14] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
static TEST_ITEMS_NONZERO: &[u32; 13] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
static TEST_MOD_PRIME_7: &[u32; 7] = &[0, 1, 2, 3, 4, 5, 6];
static TEST_BOOLS: &[bool; 2] = &[false, true];
#[test]
fn test_associativity() {
assert!(associativity(TEST_ITEMS, u32::max).is_ok());
assert!(associativity(TEST_ITEMS, u32::wrapping_pow).is_err());
}
#[test]
fn test_left_distributes() {
assert!(left_distributes(TEST_ITEMS, u32::wrapping_sub, u32::wrapping_mul).is_ok());
assert!(left_distributes(TEST_ITEMS, u32::wrapping_sub, u32::wrapping_pow).is_err());
}
#[test]
fn test_right_distributes() {
assert!(right_distributes(TEST_ITEMS, u32::wrapping_sub, u32::wrapping_mul).is_ok());
assert!(right_distributes(TEST_ITEMS, u32::wrapping_sub, u32::wrapping_pow).is_err());
}
#[test]
fn test_nonzero_inverse() {
assert!(
nonzero_inverse(TEST_ITEMS, u32::wrapping_add, 0, 0, |x| {
0u32.wrapping_sub(x)
})
.is_ok()
);
assert!(
nonzero_inverse(TEST_ITEMS, u32::wrapping_sub, 0, 0, |x| {
0u32.wrapping_add(x)
})
.is_ok()
);
assert!(
right_distributes(TEST_ITEMS_NONZERO, u32::wrapping_div, u32::wrapping_mul).is_err()
);
}
#[test]
fn test_idempotency() {
assert!(idempotency(TEST_ITEMS, u32::max).is_ok());
assert!(idempotency(TEST_ITEMS, u32::wrapping_add).is_err());
}
#[test]
fn test_commutativity() {
assert!(commutativity(TEST_ITEMS, u32::max).is_ok());
assert!(commutativity(TEST_ITEMS_NONZERO, u32::wrapping_div).is_err());
}
#[test]
fn test_commutative_ring() {
assert!(
commutative_ring(
TEST_ITEMS,
&u32::wrapping_add,
&u32::wrapping_mul,
0,
1,
&|x| 0u32.wrapping_sub(x),
)
.is_ok()
);
assert!(
commutative_ring(
TEST_ITEMS,
&u32::wrapping_add,
&u32::wrapping_pow,
0,
1,
&|x| 0u32.wrapping_sub(x),
)
.is_err()
);
assert!(
commutative_ring(
&[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]],
&|a, b| {
[
[a[0][0] + b[0][0], a[0][1] + b[0][1]],
[a[1][0] + b[1][0], a[1][0] + b[1][1]],
]
},
&|a, b| {
[
[
a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1],
],
]
},
[[0, 0], [0, 0]],
[[1, 0], [0, 1]],
&|a| {
[
[
-a[0][0] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
-a[0][1] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
],
[
-a[1][0] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
-a[1][1] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
],
]
},
)
.is_err()
);
}
#[test]
fn test_commutative_monoid() {
assert!(commutative_monoid(TEST_ITEMS, &u32::wrapping_add, 0).is_ok());
assert!(commutative_monoid(TEST_ITEMS, &u32::wrapping_mul, 1).is_ok());
assert!(commutative_monoid(TEST_ITEMS, &u32::wrapping_add, 0).is_ok());
assert!(commutative_monoid(TEST_BOOLS, &|a, b| a & b, true).is_ok());
assert!(commutative_monoid(TEST_ITEMS, &u32::wrapping_sub, 0).is_err());
assert!(commutative_monoid(TEST_ITEMS_NONZERO, &u32::wrapping_add, 1).is_err());
assert!(commutative_monoid(TEST_ITEMS, &u32::wrapping_pow, 3).is_err());
}
#[test]
fn test_semigroup() {
assert!(semigroup(TEST_ITEMS_NONZERO, &u32::wrapping_add).is_ok());
assert!(semigroup(TEST_ITEMS, &u32::wrapping_add).is_ok());
assert!(semigroup(TEST_ITEMS, &u32::wrapping_mul).is_ok());
assert!(semigroup(TEST_BOOLS, &|a, b| a & b).is_ok()); assert!(
semigroup(
&[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]],
&|a, b| {
[
[
a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1],
],
]
},
)
.is_ok()
);
assert!(semigroup(TEST_ITEMS, &u32::wrapping_pow).is_err());
}
#[test]
fn test_identity() {
assert!(identity(TEST_ITEMS, u32::wrapping_add, 0).is_ok());
assert!(identity(TEST_ITEMS, u32::wrapping_add, 5).is_err());
}
#[test]
fn test_inverse() {
assert!(inverse(TEST_ITEMS, u32::wrapping_add, 0, |x| 0u32.wrapping_sub(x)).is_ok());
assert!(inverse(TEST_ITEMS, u32::wrapping_add, 0, |x| 0u32.wrapping_add(x)).is_err());
}
#[test]
fn test_distributive() {
assert!(distributive(TEST_ITEMS, &u32::wrapping_add, &u32::wrapping_mul).is_ok());
assert!(distributive(TEST_ITEMS, &u32::wrapping_add, &u32::max).is_err());
}
#[test]
fn test_linearity() {
assert!(
linearity(TEST_ITEMS, u32::wrapping_add, u32::wrapping_add, |x| {
u32::wrapping_mul(x, 5)
})
.is_ok()
);
assert!(
linearity(TEST_ITEMS, u32::wrapping_add, u32::wrapping_add, |x| {
u32::pow(x, 5)
})
.is_err()
);
}
#[test]
fn test_bilinearity() {
assert!(
bilinearity(
TEST_ITEMS,
TEST_ITEMS,
u32::wrapping_add,
u32::wrapping_add,
u32::wrapping_add,
u32::wrapping_mul
)
.is_ok()
);
assert!(
bilinearity(
TEST_ITEMS,
TEST_ITEMS,
u32::wrapping_add,
u32::wrapping_add,
u32::wrapping_add,
u32::pow
)
.is_err()
);
}
#[test]
fn test_group() {
assert!(group(TEST_ITEMS, &u32::wrapping_add, 0, &|x| 0u32.wrapping_sub(x)).is_ok());
assert!(group(TEST_MOD_PRIME_7, &modulo_add_7, 0, &modulo_sub_7).is_ok());
assert!(group(TEST_ITEMS, &modulo_add_14, 0, &modulo_sub_14).is_ok());
assert!(
group(TEST_ITEMS_NONZERO, &u32::wrapping_mul, 1, &|x| 1u32
.wrapping_div(x))
.is_err()
);
}
#[test]
fn test_abelian_group() {
assert!(
abelian_group(TEST_ITEMS, &u32::wrapping_add, 0, &|x| 0u32.wrapping_sub(x)).is_ok()
);
assert!(abelian_group(TEST_MOD_PRIME_7, &modulo_add_7, 0, &modulo_sub_7).is_ok());
assert!(
abelian_group(TEST_ITEMS_NONZERO, &u32::wrapping_mul, 1, &|x| 1u32
.wrapping_div(x))
.is_err()
);
assert!(
abelian_group(
&[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]],
&|a, b| {
[
[
a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1],
],
]
},
[[1, 0], [0, 1]],
&|a| {
[
[
-a[0][0] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
-a[0][1] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
],
[
-a[1][0] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
-a[1][1] / (a[0][0] * a[1][1] - a[0][1] * a[1][1]),
],
]
},
)
.is_err()
);
}
#[test]
fn test_monoid() {
assert!(monoid(TEST_ITEMS, &u32::wrapping_add, 0).is_ok());
assert!(monoid(TEST_ITEMS_NONZERO, &u32::wrapping_mul, 1).is_ok());
assert!(monoid(TEST_ITEMS, &u32::wrapping_mul, 1).is_ok());
assert!(
monoid(
&[[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]]],
&|a, b| {
[
[
a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1],
],
[
a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1],
],
]
},
[[1, 0], [0, 1]],
)
.is_ok()
);
assert!(monoid(TEST_ITEMS_NONZERO, &u32::wrapping_add, 1).is_err());
}
#[test]
fn test_absorbing() {
assert!(absorbing_element(TEST_ITEMS, u32::wrapping_mul, 0).is_ok());
assert!(absorbing_element(TEST_ITEMS, u32::wrapping_mul, 5).is_err());
}
fn modulo_add_7(a: u32, b: u32) -> u32 {
u32::wrapping_add(a, b) % 7
}
fn modulo_add_14(a: u32, b: u32) -> u32 {
u32::wrapping_add(a, b) % 14
}
fn modulo_sub_7(a: u32) -> u32 {
u32::wrapping_sub(7, a) % 7
}
fn modulo_sub_14(a: u32) -> u32 {
u32::wrapping_sub(14, a) % 14
}
fn modulo_mult_7(a: u32, b: u32) -> u32 {
u32::wrapping_mul(a, b) % 7
}
fn modulo_mult_14(a: u32, b: u32) -> u32 {
u32::wrapping_mul(a, b) % 14
}
#[test]
fn test_additive_inverse_7() {
assert_eq!(0, modulo_sub_7(0));
assert_eq!(1, modulo_sub_7(6));
assert_eq!(2, modulo_sub_7(5));
assert_eq!(3, modulo_sub_7(4));
assert_eq!(4, modulo_sub_7(3));
assert_eq!(6, modulo_sub_7(1));
}
#[test]
fn test_modulo_mu14() {
assert_eq!(0, modulo_mult_14(2, 7));
assert_eq!(3, modulo_mult_14(1, 3));
assert_eq!(2, modulo_mult_14(2, 1));
assert_eq!(3, modulo_mult_14(3, 1));
assert_eq!(4, modulo_mult_14(2, 2));
assert_eq!(6, modulo_mult_14(2, 3));
assert_eq!(9, modulo_mult_14(3, 3));
}
#[test]
fn test_modulo_mu7() {
assert_eq!(0, modulo_mult_7(0, 0));
assert_eq!(3, modulo_mult_7(1, 3));
assert_eq!(2, modulo_mult_7(2, 1));
assert_eq!(2, modulo_mult_7(3, 3));
assert_eq!(2, modulo_mult_7(3, 3));
assert_eq!(5, modulo_mult_7(3, 4));
assert_eq!(1, modulo_mult_7(3, 5));
}
#[test]
fn test_no_nonzero_zero_divisors() {
assert!(no_nonzero_zero_divisors(TEST_MOD_PRIME_7, &modulo_mult_7, 0).is_ok());
assert!(no_nonzero_zero_divisors(TEST_ITEMS, &modulo_mult_7, 0).is_err());
}
#[test]
fn test_integral_domain() {
assert!(
integral_domain(
TEST_MOD_PRIME_7,
&modulo_add_7,
&modulo_mult_7,
0,
1,
&modulo_sub_7,
)
.is_ok()
);
assert!(
integral_domain(
TEST_ITEMS,
&modulo_add_14,
&modulo_mult_14,
0,
1,
&modulo_sub_14,
)
.is_err()
);
}
#[test]
fn test_field() {
assert!(
field(
TEST_BOOLS,
&|a, b| a ^ b, &|a, b| a & b, false,
true,
&|x| x, &|_x| true
)
.is_ok()
);
assert!(
field(
TEST_ITEMS,
&u32::wrapping_add,
&u32::wrapping_mul,
0,
1,
&|x| 0u32.wrapping_sub(x),
&|x| 0u32.wrapping_sub(x) )
.is_err()
);
}
#[test]
fn test_ring() {
assert!(
ring(
TEST_ITEMS,
&u32::wrapping_add,
&u32::wrapping_mul,
0,
1,
&|x| 0u32.wrapping_sub(x),
)
.is_ok()
);
assert!(
ring(
TEST_ITEMS,
&u32::wrapping_add,
&u32::wrapping_mul,
0,
5,
&|x| 0u32.wrapping_sub(x),
)
.is_err()
);
}
#[test]
fn test_semiring() {
assert!(semiring(TEST_ITEMS, &u32::wrapping_add, &u32::wrapping_mul, 0, 1).is_ok());
assert!(semiring(&[false, true], &|x, y| x | y, &|x, y| x & y, false, true).is_ok());
assert!(
semiring(
&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, f64::INFINITY],
&f64::min,
&|x, y| x + y,
f64::INFINITY,
0.0,
)
.is_ok()
);
assert!(
semiring(
&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, f64::NEG_INFINITY],
&f64::max,
&|x, y| x + y,
f64::NEG_INFINITY,
0.0,
)
.is_ok()
);
assert!(
semiring(
&[
HashSet::from([]),
HashSet::from(["".to_owned()]),
HashSet::from(["a".to_owned()]),
HashSet::from(["aa".to_owned(), "bb".to_owned()]),
HashSet::from(["ab".to_owned(), "bb".to_owned(), "cc".to_owned()]),
HashSet::from(["ba".to_owned()]),
HashSet::from(["bb".to_owned()]),
],
&|x, y| x.union(&y).cloned().collect(),
&|x, y| {
let mut new_set = HashSet::new();
#[expect(
clippy::disallowed_methods,
reason = "nondeterministic iteration order, fine to collect into set"
)]
for a in x.iter() {
for b in y.iter() {
new_set.insert(format!("{a}{b}"));
}
}
new_set
},
HashSet::from([]),
HashSet::from(["".to_owned()]),
)
.is_ok()
);
}
#[test]
fn test_get_single_function_properties() {
let test_properties_satisfied = get_single_function_properties(
TEST_ITEMS,
u32::wrapping_add,
0,
|x| 0u32.wrapping_sub(x),
0,
);
let correct_properties = vec![
"associativity".to_owned(),
"commutativity".to_owned(),
"identity".to_owned(),
"inverse".to_owned(),
];
assert_eq!(test_properties_satisfied, correct_properties);
let test_properties_satisfied = get_single_function_properties(
&[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, f64::INFINITY],
f64::max,
0.0,
|x| x,
f64::INFINITY,
);
let correct_properties = vec![
"associativity".to_owned(),
"commutativity".to_owned(),
"idempotency".to_owned(),
"identity".to_owned(),
"absorbing_element".to_owned(),
];
assert_eq!(test_properties_satisfied, correct_properties);
let f = |x: u32, _y: u32| x;
let test_properties_satisfied =
get_single_function_properties(TEST_ITEMS, f, 0, |x| 0u32.wrapping_sub(x), 0);
let correct_properties = vec!["associativity".to_owned(), "idempotency".to_owned()];
assert_eq!(test_properties_satisfied, correct_properties);
}
}