pub use vyre_spec::{AlgebraicLaw, LawCheckFn, MonotonicDirection};
pub const LAW_CATALOG: &[&str] = &[
"commutative",
"associative",
"identity",
"left-identity",
"right-identity",
"self-inverse",
"idempotent",
"absorbing",
"left-absorbing",
"right-absorbing",
"involution",
"de-morgan",
"monotone",
"monotonic",
"bounded",
"complement",
"distributive",
"lattice-absorption",
"inverse-of",
"trichotomy",
"zero-product",
"custom",
];
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
pub struct LawViolation {
pub law: String,
pub op_id: String,
pub a: u32,
pub b: u32,
pub c: u32,
pub lhs: u32,
pub rhs: u32,
pub message: String,
}
impl core::fmt::Display for LawViolation {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"LAW VIOLATION: {} on {}: a={}, b={}, c={}, lhs={}, rhs={}. {}",
self.law, self.op_id, self.a, self.b, self.c, self.lhs, self.rhs, self.message
)
}
}
#[inline]
pub fn canonical_law_id(law: &AlgebraicLaw) -> String {
match law {
AlgebraicLaw::Commutative => "commutative".to_string(),
AlgebraicLaw::Associative => "associative".to_string(),
AlgebraicLaw::Identity { element } => format!("identity({element})"),
AlgebraicLaw::LeftIdentity { element } => format!("left-identity({element})"),
AlgebraicLaw::RightIdentity { element } => format!("right-identity({element})"),
AlgebraicLaw::SelfInverse { result } => format!("self-inverse({result})"),
AlgebraicLaw::Idempotent => "idempotent".to_string(),
AlgebraicLaw::Absorbing { element } => format!("absorbing({element})"),
AlgebraicLaw::LeftAbsorbing { element } => format!("left-absorbing({element})"),
AlgebraicLaw::RightAbsorbing { element } => format!("right-absorbing({element})"),
AlgebraicLaw::Involution => "involution".to_string(),
AlgebraicLaw::DeMorgan { inner_op, dual_op } => {
format!("de-morgan({inner_op},{dual_op})")
}
AlgebraicLaw::Monotone => "monotone".to_string(),
AlgebraicLaw::Monotonic { direction } => {
format!("monotonic({direction:?})")
}
AlgebraicLaw::Bounded { lo, hi } => format!("bounded({lo},{hi})"),
AlgebraicLaw::Complement {
complement_op,
universe,
} => format!("complement({complement_op},{universe})"),
AlgebraicLaw::DistributiveOver { over_op } => format!("distributive({over_op})"),
AlgebraicLaw::LatticeAbsorption { dual_op } => {
format!("lattice-absorption({dual_op})")
}
AlgebraicLaw::InverseOf { op } => format!("inverse-of({op})"),
AlgebraicLaw::Trichotomy {
less_op,
equal_op,
greater_op,
} => format!("trichotomy({less_op},{equal_op},{greater_op})"),
AlgebraicLaw::ZeroProduct { holds } => format!("zero-product({holds})"),
AlgebraicLaw::Custom {
name, arity, check, ..
} => {
let check_addr = *check as usize;
format!("custom({name},{arity},fn:{check_addr:016x})")
}
other => format!("unhandled-law({})", other.name()),
}
}
fn always_true_custom_check(_f: fn(&[u8]) -> Vec<u8>, _args: &[u32]) -> bool {
true
}
pub static ALL_ALGEBRAIC_LAWS: &[AlgebraicLaw] = &[
AlgebraicLaw::Commutative,
AlgebraicLaw::Associative,
AlgebraicLaw::Identity { element: 0 },
AlgebraicLaw::LeftIdentity { element: 0 },
AlgebraicLaw::RightIdentity { element: 0 },
AlgebraicLaw::SelfInverse { result: 0 },
AlgebraicLaw::Idempotent,
AlgebraicLaw::Absorbing { element: 0 },
AlgebraicLaw::LeftAbsorbing { element: 0 },
AlgebraicLaw::RightAbsorbing { element: 0 },
AlgebraicLaw::Involution,
AlgebraicLaw::DeMorgan {
inner_op: "and",
dual_op: "or",
},
AlgebraicLaw::Monotone,
AlgebraicLaw::Monotonic {
direction: MonotonicDirection::NonDecreasing,
},
AlgebraicLaw::Bounded {
lo: 0,
hi: u32::MAX,
},
AlgebraicLaw::Complement {
complement_op: "not",
universe: u32::MAX,
},
AlgebraicLaw::DistributiveOver { over_op: "add" },
AlgebraicLaw::LatticeAbsorption { dual_op: "min" },
AlgebraicLaw::InverseOf { op: "add" },
AlgebraicLaw::Trichotomy {
less_op: "lt",
equal_op: "eq",
greater_op: "gt",
},
AlgebraicLaw::ZeroProduct { holds: true },
AlgebraicLaw::Custom {
name: "parity-even",
description: "All inputs have even parity",
arity: 1,
check: always_true_custom_check,
},
];