use crate::proof::algebra::checker::verify_laws;
use crate::proof::algebra::inference::{infer_binary_laws, InferenceReport};
use crate::spec::law::{canonical_law_id, AlgebraicLaw};
#[derive(Debug, Clone, PartialEq)]
pub struct AuditedLaw {
pub law: AlgebraicLaw,
pub verdict: AuditVerdict,
}
#[derive(Debug, Clone, PartialEq)]
pub enum AuditVerdict {
Confirmed,
OverClaimed {
witness: String,
},
UnderClaimed {
suggestion: String,
},
}
#[derive(Debug, Clone, Default)]
pub struct LawAuditReport {
pub op_id: String,
pub laws: Vec<AuditedLaw>,
}
impl LawAuditReport {
#[must_use]
#[inline]
pub fn is_clean(&self) -> bool {
self.laws
.iter()
.all(|audited| matches!(audited.verdict, AuditVerdict::Confirmed))
}
#[must_use]
#[inline]
pub fn over_claimed(&self) -> Vec<&AuditedLaw> {
self.laws
.iter()
.filter(|a| matches!(a.verdict, AuditVerdict::OverClaimed { .. }))
.collect()
}
#[must_use]
#[inline]
pub fn under_claimed(&self) -> Vec<&AuditedLaw> {
self.laws
.iter()
.filter(|a| matches!(a.verdict, AuditVerdict::UnderClaimed { .. }))
.collect()
}
}
#[must_use]
#[inline]
pub fn audit_binary(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
declared: &[AlgebraicLaw],
) -> LawAuditReport {
let mut report = LawAuditReport {
op_id: op_id.to_string(),
laws: Vec::new(),
};
for law in declared {
let results = verify_laws(op_id, cpu_fn, std::slice::from_ref(law), true);
let any_violation = results.iter().find_map(|r| r.violation.clone());
let verdict = if any_violation.is_some() {
AuditVerdict::OverClaimed {
witness: format!("{:?}", any_violation),
}
} else {
AuditVerdict::Confirmed
};
report.laws.push(AuditedLaw {
law: law.clone(),
verdict,
});
}
let inferred: InferenceReport = infer_binary_laws(op_id, cpu_fn);
for inferred_law in &inferred.proven {
if declared.iter().any(|d| same_law(d, &inferred_law.law)) {
continue;
}
report.laws.push(AuditedLaw {
law: inferred_law.law.clone(),
verdict: AuditVerdict::UnderClaimed {
suggestion: inferred_law.recommendation.clone(),
},
});
}
for element in PROBE_VALUES {
check_and_record(
op_id,
cpu_fn,
AlgebraicLaw::Identity { element: *element },
declared,
&mut report,
);
check_and_record(
op_id,
cpu_fn,
AlgebraicLaw::Absorbing { element: *element },
declared,
&mut report,
);
}
report
}
#[must_use]
#[inline]
pub fn audit_unary(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
declared: &[AlgebraicLaw],
) -> LawAuditReport {
let mut report = LawAuditReport {
op_id: op_id.to_string(),
laws: Vec::new(),
};
for law in declared {
let results = verify_laws(op_id, cpu_fn, std::slice::from_ref(law), false);
let any_violation = results.iter().find_map(|r| r.violation.clone());
let verdict = if any_violation.is_some() {
AuditVerdict::OverClaimed {
witness: format!("{:?}", any_violation),
}
} else {
AuditVerdict::Confirmed
};
report.laws.push(AuditedLaw {
law: law.clone(),
verdict,
});
}
let inferred: InferenceReport =
crate::proof::algebra::inference::infer_unary_laws(op_id, cpu_fn);
for inferred_law in &inferred.proven {
if declared.iter().any(|d| same_law(d, &inferred_law.law)) {
continue;
}
report.laws.push(AuditedLaw {
law: inferred_law.law.clone(),
verdict: AuditVerdict::UnderClaimed {
suggestion: inferred_law.recommendation.clone(),
},
});
}
for (lo, hi) in [(0u32, 1), (0, 32), (0, 255), (0, u32::MAX)] {
check_and_record(
op_id,
cpu_fn,
AlgebraicLaw::Bounded { lo, hi },
declared,
&mut report,
);
}
report
}
const PROBE_VALUES: &[u32] = &[
0,
1,
2,
u32::MAX,
u32::MAX / 2,
1 << 31,
(1 << 31) - 1,
1 << 0,
1 << 1,
1 << 2,
1 << 4,
1 << 8,
1 << 16,
1 << 24,
1 << 30,
0x0000_0001,
0x0000_0003,
0x0000_000F,
0x0000_00FF,
0x0000_FFFF,
0x00FF_FFFF,
0x0FFF_FFFF,
0x5555_5555,
0xAAAA_AAAA,
0xF0F0_F0F0,
0x0F0F_0F0F,
0xFF00_FF00,
0x00FF_00FF,
0xDEAD_BEEF,
0xCAFE_BABE,
0x1234_5678,
];
fn check_and_record(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
law: AlgebraicLaw,
declared: &[AlgebraicLaw],
report: &mut LawAuditReport,
) {
let already_in_report = report.laws.iter().any(|a| same_law(&a.law, &law));
if already_in_report {
return;
}
let results = verify_laws(op_id, cpu_fn, std::slice::from_ref(&law), true);
let proven = results
.iter()
.all(|r| r.cases_tested > 0 && r.violation.is_none())
&& !results.is_empty();
if !proven {
return;
}
let is_declared = declared.iter().any(|d| same_law(d, &law));
let verdict = if is_declared {
AuditVerdict::Confirmed
} else {
AuditVerdict::UnderClaimed {
suggestion: format!("Add: {:?}", law),
}
};
report.laws.push(AuditedLaw { law, verdict });
}
fn same_law(left: &AlgebraicLaw, right: &AlgebraicLaw) -> bool {
if canonical_law_id(left) == canonical_law_id(right) {
return true;
}
subsumes(left, right) || subsumes(right, left)
}
fn subsumes(left: &AlgebraicLaw, right: &AlgebraicLaw) -> bool {
match (left, right) {
(AlgebraicLaw::Identity { element: g }, AlgebraicLaw::LeftIdentity { element: s }) => {
g == s
}
(AlgebraicLaw::Identity { element: g }, AlgebraicLaw::RightIdentity { element: s }) => {
g == s
}
(AlgebraicLaw::Absorbing { element: g }, AlgebraicLaw::LeftAbsorbing { element: s }) => {
g == s
}
(AlgebraicLaw::Absorbing { element: g }, AlgebraicLaw::RightAbsorbing { element: s }) => {
g == s
}
_ => false,
}
}
#[cfg(test)]
mod tests {
use super::{audit_binary, same_law, AlgebraicLaw, AuditVerdict};
fn add_cpu(input: &[u8]) -> Vec<u8> {
let a = u32::from_le_bytes(input[..4].try_into().unwrap_or_default());
let b = u32::from_le_bytes(input[4..8].try_into().unwrap_or_default());
a.wrapping_add(b).to_le_bytes().to_vec()
}
fn sub_cpu(input: &[u8]) -> Vec<u8> {
let a = u32::from_le_bytes(input[..4].try_into().unwrap_or_default());
let b = u32::from_le_bytes(input[4..8].try_into().unwrap_or_default());
a.wrapping_sub(b).to_le_bytes().to_vec()
}
#[test]
fn audit_add_confirms_commutativity_and_identity_zero() {
let report = audit_binary(
"test.add",
add_cpu,
&[
AlgebraicLaw::Commutative,
AlgebraicLaw::Identity { element: 0 },
],
);
assert!(
report
.laws
.iter()
.any(|a| matches!(a.law, AlgebraicLaw::Commutative)
&& matches!(a.verdict, AuditVerdict::Confirmed)),
"commutativity must confirm on add: {report:?}"
);
assert!(
report
.laws
.iter()
.any(|a| matches!(a.law, AlgebraicLaw::Identity { element: 0 })
&& matches!(a.verdict, AuditVerdict::Confirmed)),
"identity 0 must confirm on add: {report:?}"
);
assert!(report.over_claimed().is_empty());
}
#[test]
fn audit_sub_rejects_falsely_claimed_commutativity() {
let report = audit_binary("test.sub", sub_cpu, &[AlgebraicLaw::Commutative]);
assert!(
report
.laws
.iter()
.any(|a| matches!(a.verdict, AuditVerdict::OverClaimed { .. })),
"subtraction is not commutative; the audit must reject the false claim: {report:?}"
);
assert!(!report.is_clean());
}
#[test]
fn identity_subsumes_left_identity() {
assert!(same_law(
&AlgebraicLaw::Identity { element: 5 },
&AlgebraicLaw::LeftIdentity { element: 5 }
));
}
#[test]
fn identity_subsumes_right_identity() {
assert!(same_law(
&AlgebraicLaw::Identity { element: 5 },
&AlgebraicLaw::RightIdentity { element: 5 }
));
}
#[test]
fn left_identity_subsumes_identity_symmetric() {
assert!(same_law(
&AlgebraicLaw::LeftIdentity { element: 7 },
&AlgebraicLaw::Identity { element: 7 }
));
}
#[test]
fn right_identity_subsumes_identity_symmetric() {
assert!(same_law(
&AlgebraicLaw::RightIdentity { element: 7 },
&AlgebraicLaw::Identity { element: 7 }
));
}
#[test]
fn identity_does_not_subsume_different_element() {
assert!(!same_law(
&AlgebraicLaw::Identity { element: 5 },
&AlgebraicLaw::LeftIdentity { element: 6 }
));
}
#[test]
fn absorbing_subsumes_left_absorbing() {
assert!(same_law(
&AlgebraicLaw::Absorbing { element: 3 },
&AlgebraicLaw::LeftAbsorbing { element: 3 }
));
}
#[test]
fn absorbing_subsumes_right_absorbing() {
assert!(same_law(
&AlgebraicLaw::Absorbing { element: 3 },
&AlgebraicLaw::RightAbsorbing { element: 3 }
));
}
#[test]
fn left_absorbing_subsumes_absorbing_symmetric() {
assert!(same_law(
&AlgebraicLaw::LeftAbsorbing { element: 7 },
&AlgebraicLaw::Absorbing { element: 7 }
));
}
#[test]
fn right_absorbing_subsumes_absorbing_symmetric() {
assert!(same_law(
&AlgebraicLaw::RightAbsorbing { element: 7 },
&AlgebraicLaw::Absorbing { element: 7 }
));
}
#[test]
fn absorbing_does_not_subsume_different_element() {
assert!(!same_law(
&AlgebraicLaw::Absorbing { element: 5 },
&AlgebraicLaw::LeftAbsorbing { element: 6 }
));
}
#[test]
fn distinct_laws_remain_distinct() {
assert!(!same_law(
&AlgebraicLaw::Commutative,
&AlgebraicLaw::Associative
));
}
}