mod rebuild;
#[cfg(loom)]
pub mod support;
#[cfg(not(loom))]
pub(crate) mod support;
use crate::spec::law::{AlgebraicLaw, LawViolation};
#[cfg(not(loom))]
use rayon::prelude::*;
use std::sync::{Mutex, OnceLock};
use support::{check_unary_law_exhaustive_u8, check_unary_law_witnessed_u32};
#[derive(Debug, Clone)]
pub struct LawResult {
pub op_id: String,
pub law_name: String,
pub level: VerificationLevel,
pub cases_tested: u64,
pub violation: Option<LawViolation>,
}
impl LawResult {
#[must_use]
#[inline]
pub fn passed(&self) -> bool {
self.violation.is_none() && self.cases_tested > 0
}
#[must_use]
#[inline]
pub fn is_inapplicable(&self) -> bool {
self.cases_tested == 0 && self.violation.is_none()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerificationLevel {
ExhaustiveU8,
WitnessedU32 {
count: u64,
},
GpuWitnessedU32 {
count: u64,
},
Combined {
witness_count: u64,
},
}
#[inline]
pub fn verify_laws(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
laws: &[AlgebraicLaw],
is_binary: bool,
) -> Vec<LawResult> {
laws.iter()
.map(|law| verify_one_law_exhaustive(op_id, cpu_fn, law, is_binary))
.collect()
}
#[cfg(loom)]
#[inline]
pub fn verify_laws_witnessed(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
laws: &[AlgebraicLaw],
is_binary: bool,
witness_count: u64,
) -> Vec<LawResult> {
use loom::sync::{mpsc, Arc, Mutex};
use loom::thread;
if laws.is_empty() {
return Vec::new();
}
let (tx, rx) = mpsc::channel();
let rx = Arc::new(Mutex::new(rx));
let worker_count = 2;
let mut handles = Vec::with_capacity(worker_count);
for _ in 0..worker_count {
let rx = Arc::clone(&rx);
let handle = thread::spawn(move || {
let mut results = Vec::new();
loop {
let msg = {
let lock = rx.lock().unwrap();
lock.recv()
};
match msg {
Ok((idx, law)) => {
let result =
verify_one_law_witnessed(op_id, cpu_fn, &law, is_binary, witness_count);
results.push((idx, result));
}
Err(_) => break,
}
}
results
});
handles.push(handle);
}
for (idx, law) in laws.iter().enumerate() {
tx.send((idx, law.clone())).unwrap();
}
drop(tx);
let mut all_results = vec![None; laws.len()];
for handle in handles {
for (idx, result) in handle.join().unwrap() {
all_results[idx] = Some(result);
}
}
all_results.into_iter().flatten().collect()
}
#[cfg(not(loom))]
#[inline]
pub fn verify_laws_witnessed(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
laws: &[AlgebraicLaw],
is_binary: bool,
witness_count: u64,
) -> Vec<LawResult> {
laws.par_iter()
.map(|law| verify_one_law_witnessed(op_id, cpu_fn, law, is_binary, witness_count))
.collect()
}
fn verify_one_law_exhaustive(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
is_binary: bool,
) -> LawResult {
let (cases, violation) = if is_binary {
check_binary_law_exhaustive_u8(op_id, cpu_fn, law)
} else {
check_unary_law_exhaustive_u8(op_id, cpu_fn, law)
};
let violation = violation.map(|v| {
if is_binary {
match minimize_binary_violation(op_id, cpu_fn, law, v.a, v.b, v.c) {
Ok(minimized) => minimized.unwrap_or(v),
Err(rebuild_err) => rebuild_err,
}
} else {
match minimize_unary_violation(op_id, cpu_fn, law, v.a) {
Ok(minimized) => minimized.unwrap_or(v),
Err(rebuild_err) => rebuild_err,
}
}
});
LawResult {
op_id: op_id.to_string(),
law_name: law.name().to_string(),
level: VerificationLevel::ExhaustiveU8,
cases_tested: cases,
violation,
}
}
fn verify_one_law_witnessed(
op_id: &str,
cpu_fn: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
is_binary: bool,
witness_count: u64,
) -> LawResult {
let (cases, violation) = if is_binary {
check_binary_law_witnessed_u32(op_id, cpu_fn, law, witness_count)
} else {
check_unary_law_witnessed_u32(op_id, cpu_fn, law, witness_count)
};
let violation = violation.map(|v| {
let mut minimized = match if is_binary {
minimize_binary_violation(op_id, cpu_fn, law, v.a, v.b, v.c)
} else {
minimize_unary_violation(op_id, cpu_fn, law, v.a)
} {
Ok(minimized) => minimized.unwrap_or(v),
Err(rebuild_err) => rebuild_err,
};
if let Err(err) = persist_violation(op_id, &minimized) {
minimized.message = format!(
"{} Regression persistence failed: {err}. Fix: ensure conform/regressions is writable before trusting witnessed law replay.",
minimized.message
);
}
minimized
});
LawResult {
op_id: op_id.to_string(),
law_name: law.name().to_string(),
level: VerificationLevel::WitnessedU32 {
count: witness_count,
},
cases_tested: cases,
violation,
}
}
fn persist_violation(op_id: &str, v: &LawViolation) -> std::io::Result<()> {
static PERSIST_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
let _guard = PERSIST_LOCK
.get_or_init(|| Mutex::new(()))
.lock()
.map_err(|_| std::io::Error::other("Fix: regression persistence lock was poisoned."))?;
let mut input = Vec::with_capacity(12);
input.extend_from_slice(&v.a.to_le_bytes());
input.extend_from_slice(&v.b.to_le_bytes());
input.extend_from_slice(&v.c.to_le_bytes());
let failure = crate::spec::types::ParityFailure {
op_id: op_id.to_string(),
generator: "algebra_checker".to_string(),
input_label: format!("law_violation:{}", v.law),
input,
gpu_output: vec![],
cpu_output: v.lhs.to_le_bytes().to_vec(),
message: v.message.clone(),
spec_version: 1,
workgroup_size: 1,
};
crate::verify::regression::save(&failure).map(|_| ())
}
fn check_binary_law_exhaustive_u8(
op_id: &str,
f: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
) -> (u64, Option<LawViolation>) {
match crate::proof::algebra::laws::binary::check_exhaustive_u8(op_id, f, law) {
Ok(r) => r,
Err(v) => (0, Some(v)),
}
}
fn check_binary_law_witnessed_u32(
op_id: &str,
f: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
count: u64,
) -> (u64, Option<LawViolation>) {
match crate::proof::algebra::laws::binary::check_witnessed_u32(op_id, f, law, count) {
Ok(r) => r,
Err(v) => (0, Some(v)),
}
}
use rebuild::{minimize_binary_violation, minimize_unary_violation};