use crate::proof::algebra::checker::verify_laws;
use crate::spec::law::{AlgebraicLaw, MonotonicDirection, ALL_ALGEBRAIC_LAWS};
#[derive(Debug, Clone)]
pub struct InferredLaw {
pub law: AlgebraicLaw,
pub name: &'static str,
pub recommendation: String,
}
#[derive(Debug, Clone)]
pub struct InferenceReport {
pub op_id: String,
pub proven: Vec<InferredLaw>,
pub skipped: Vec<&'static str>,
pub findings: Vec<crate::spec::law::LawViolation>,
pub summary: String,
}
#[inline]
pub fn infer_binary_laws(op_id: &str, cpu_fn: fn(&[u8]) -> Vec<u8>) -> InferenceReport {
let mut proven = Vec::new();
let mut skipped = Vec::new();
for law in binary_candidate_laws() {
try_law(op_id, cpu_fn, law, true, &mut proven);
}
skipped.push("involution (unary only)");
skipped.push("monotone (unary only)");
skipped.push("monotonic (unary only)");
skipped.push("de-morgan (unary transform over binary companions)");
let summary = build_summary(op_id, &proven, &skipped);
InferenceReport {
op_id: op_id.to_string(),
proven,
skipped,
findings: Vec::new(),
summary,
}
}
#[inline]
pub fn infer_unary_laws(op_id: &str, cpu_fn: fn(&[u8]) -> Vec<u8>) -> InferenceReport {
let mut proven = Vec::new();
let mut skipped = Vec::new();
for law in unary_candidate_laws() {
try_law(op_id, cpu_fn, law, false, &mut proven);
}
skipped.push("commutative (binary only)");
skipped.push("associative (binary only)");
skipped.push("identity (binary only)");
skipped.push("left-identity (binary only)");
skipped.push("right-identity (binary only)");
skipped.push("self-inverse (binary only)");
skipped.push("idempotent (binary only)");
skipped.push("absorbing (binary only)");
skipped.push("left-absorbing (binary only)");
skipped.push("right-absorbing (binary only)");
skipped.push("zero-product (binary only)");
skipped.push("distributive (binary only)");
skipped.push("lattice-absorption (binary only)");
skipped.push("inverse-of (binary only)");
skipped.push("trichotomy (binary only)");
let summary = build_summary(op_id, &proven, &skipped);
InferenceReport {
op_id: op_id.to_string(),
proven,
skipped,
findings: Vec::new(),
summary,
}
}
#[inline]
pub(crate) fn binary_candidate_laws() -> Vec<AlgebraicLaw> {
let mut laws = Vec::new();
for law in ALL_ALGEBRAIC_LAWS {
match law {
AlgebraicLaw::Commutative
| AlgebraicLaw::Associative
| AlgebraicLaw::Idempotent
| AlgebraicLaw::ZeroProduct { .. }
| AlgebraicLaw::DistributiveOver { .. }
| AlgebraicLaw::Complement { .. }
| AlgebraicLaw::LatticeAbsorption { .. }
| AlgebraicLaw::InverseOf { .. }
| AlgebraicLaw::Trichotomy { .. }
| AlgebraicLaw::Custom { .. } => laws.push(law.clone()),
AlgebraicLaw::Identity { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::Identity { element });
}
}
AlgebraicLaw::LeftIdentity { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::LeftIdentity { element });
}
}
AlgebraicLaw::RightIdentity { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::RightIdentity { element });
}
}
AlgebraicLaw::SelfInverse { .. } => {
for result in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::SelfInverse { result });
}
}
AlgebraicLaw::Absorbing { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::Absorbing { element });
}
}
AlgebraicLaw::LeftAbsorbing { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::LeftAbsorbing { element });
}
}
AlgebraicLaw::RightAbsorbing { .. } => {
for element in [0u32, 1, u32::MAX] {
laws.push(AlgebraicLaw::RightAbsorbing { element });
}
}
AlgebraicLaw::Bounded { .. } => {
for (lo, hi) in [(0u32, 1), (0, 32), (0, 255), (0, u32::MAX)] {
laws.push(AlgebraicLaw::Bounded { lo, hi });
}
}
AlgebraicLaw::Involution | AlgebraicLaw::Monotone | AlgebraicLaw::Monotonic { .. } => {}
AlgebraicLaw::DeMorgan { .. } => {}
_ => {}
}
}
laws
}
#[inline]
pub(crate) fn unary_candidate_laws() -> Vec<AlgebraicLaw> {
let mut laws = Vec::new();
for law in ALL_ALGEBRAIC_LAWS {
match law {
AlgebraicLaw::Involution
| AlgebraicLaw::Monotone
| AlgebraicLaw::DeMorgan { .. }
| AlgebraicLaw::Complement { .. }
| AlgebraicLaw::Custom { .. } => laws.push(law.clone()),
AlgebraicLaw::Monotonic { .. } => {
laws.push(AlgebraicLaw::Monotonic {
direction: MonotonicDirection::NonDecreasing,
});
laws.push(AlgebraicLaw::Monotonic {
direction: MonotonicDirection::NonIncreasing,
});
}
AlgebraicLaw::Bounded { .. } => {
for (lo, hi) in [(0u32, 1), (0, 32), (0, 255), (0, u32::MAX)] {
laws.push(AlgebraicLaw::Bounded { lo, hi });
}
}
_ => {}
}
}
laws
}
fn try_law(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
law: AlgebraicLaw,
is_binary: bool,
proven: &mut Vec<InferredLaw>,
) {
let results = verify_laws(op_id, cpu_fn, std::slice::from_ref(&law), is_binary);
for result in results {
if result.cases_tested == 0 {
continue; }
if result.violation.is_none() {
let name = law.name();
let recommendation = recommend(&law);
proven.push(InferredLaw {
law: law.clone(),
name: static_name(name),
recommendation,
});
return; }
}
}
#[inline]
pub(crate) fn static_name(name: &str) -> &'static str {
match name {
"commutative" => "commutative",
"associative" => "associative",
"identity" => "identity",
"self-inverse" => "self-inverse",
"idempotent" => "idempotent",
"absorbing" => "absorbing",
"left-absorbing" => "left-absorbing",
"right-absorbing" => "right-absorbing",
"left-identity" => "left-identity",
"right-identity" => "right-identity",
"involution" => "involution",
"bounded" => "bounded",
"zero-product" => "zero-product",
"distributive" => "distributive",
"de-morgan" => "de-morgan",
"monotone" => "monotone",
"monotonic" => "monotonic",
"complement" => "complement",
"lattice-absorption" => "lattice-absorption",
"inverse-of" => "inverse-of",
"trichotomy" => "trichotomy",
_ => "custom",
}
}
#[inline]
pub(crate) fn recommend(law: &AlgebraicLaw) -> String {
match law {
AlgebraicLaw::Commutative => "Add: AlgebraicLaw::Commutative".to_string(),
AlgebraicLaw::Associative => "Add: AlgebraicLaw::Associative".to_string(),
AlgebraicLaw::Identity { element } => {
format!("Add: AlgebraicLaw::Identity {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::LeftIdentity { element } => {
format!("Add: AlgebraicLaw::LeftIdentity {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::RightIdentity { element } => {
format!("Add: AlgebraicLaw::RightIdentity {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::SelfInverse { result } => {
format!("Add: AlgebraicLaw::SelfInverse {{ result: {result} }}")
}
AlgebraicLaw::Idempotent => "Add: AlgebraicLaw::Idempotent".to_string(),
AlgebraicLaw::Absorbing { element } => {
format!("Add: AlgebraicLaw::Absorbing {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::LeftAbsorbing { element } => {
format!("Add: AlgebraicLaw::LeftAbsorbing {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::RightAbsorbing { element } => {
format!("Add: AlgebraicLaw::RightAbsorbing {{ element: 0x{element:08X} }}")
}
AlgebraicLaw::Involution => "Add: AlgebraicLaw::Involution".to_string(),
AlgebraicLaw::Monotone => "Add: AlgebraicLaw::Monotone".to_string(),
AlgebraicLaw::Monotonic { direction } => {
format!("Add: AlgebraicLaw::Monotonic {{ direction: {direction:?} }}")
}
AlgebraicLaw::Bounded { lo, hi } => {
format!("Add: AlgebraicLaw::Bounded {{ lo: {lo}, hi: {hi} }}")
}
AlgebraicLaw::ZeroProduct { holds } => {
format!("Add: AlgebraicLaw::ZeroProduct {{ holds: {holds} }}")
}
AlgebraicLaw::DistributiveOver { over_op } => {
format!("Add: AlgebraicLaw::DistributiveOver {{ over_op: \"{over_op}\" }}")
}
AlgebraicLaw::DeMorgan { inner_op, dual_op } => {
format!("Add: AlgebraicLaw::DeMorgan {{ inner_op: \"{inner_op}\", dual_op: \"{dual_op}\" }}")
}
AlgebraicLaw::Complement {
complement_op,
universe,
} => {
format!("Add: AlgebraicLaw::Complement {{ complement_op: \"{complement_op}\", universe: 0x{universe:08X} }}")
}
AlgebraicLaw::LatticeAbsorption { dual_op } => {
format!("Add: AlgebraicLaw::LatticeAbsorption {{ dual_op: \"{dual_op}\" }}")
}
AlgebraicLaw::InverseOf { op } => {
format!("Add: AlgebraicLaw::InverseOf {{ op: \"{op}\" }}")
}
AlgebraicLaw::Trichotomy {
less_op,
equal_op,
greater_op,
} => {
format!("Add: AlgebraicLaw::Trichotomy {{ less_op: \"{less_op}\", equal_op: \"{equal_op}\", greater_op: \"{greater_op}\" }}")
}
_ => "Review this law's parameters and add if applicable.".to_string(),
}
}
fn build_summary(op_id: &str, proven: &[InferredLaw], _skipped: &[&'static str]) -> String {
if proven.is_empty() {
return format!(
"{op_id}: no standard laws hold. This operation may need custom law declarations.\n\
If the op has structural properties, consider adding a Custom law."
);
}
let law_list: Vec<String> = proven.iter().map(|l| l.name.to_string()).collect();
let recommendations: Vec<String> = proven
.iter()
.map(|l| format!(" {}", l.recommendation))
.collect();
format!(
"{op_id}: inferred {} laws hold: [{}]\n\
Recommended declarations:\n{}",
proven.len(),
law_list.join(", "),
recommendations.join("\n")
)
}
#[inline]
pub fn find_missing_laws(
declared: &[AlgebraicLaw],
inferred: &InferenceReport,
) -> Vec<InferredLaw> {
let declared_names: std::collections::HashSet<&str> =
declared.iter().map(|l| l.name()).collect();
inferred
.proven
.iter()
.filter(|l| !declared_names.contains(l.name))
.cloned()
.collect()
}