use crate::proof::algebra::checker::support::{call_binary, call_unary, SupportError};
use crate::spec::law::AlgebraicLaw;
#[inline]
pub fn minimize_binary(f: fn(&[u8]) -> Vec<u8>, law: &AlgebraicLaw, a: u32, b: u32) -> (u32, u32) {
let mut cur_a = a;
let mut cur_b = b;
if !binary_pair_violates(f, law, cur_a, cur_b).unwrap_or(false) {
return (cur_a, cur_b);
}
loop {
let old_a = cur_a;
let old_b = cur_b;
let next_a = minimize_value(cur_a, |next| {
binary_pair_violates(f, law, next, cur_b).unwrap_or(false)
});
cur_a = next_a;
let next_b = minimize_value(cur_b, |next| {
binary_pair_violates(f, law, cur_a, next).unwrap_or(false)
});
if next_a == old_a && next_b == old_b {
return (cur_a, cur_b);
}
cur_b = next_b;
let still_violates = binary_pair_violates(f, law, cur_a, cur_b).unwrap_or(false);
if !still_violates {
return (cur_a, cur_b);
}
}
}
#[inline]
pub fn minimize_unary(f: fn(&[u8]) -> Vec<u8>, law: &AlgebraicLaw, a: u32) -> u32 {
if !unary_input_violates(f, law, a).unwrap_or(false) {
return a;
}
minimize_value(a, |next| {
unary_input_violates(f, law, next).unwrap_or(false)
})
}
fn minimize_value(mut current: u32, mut violates: impl FnMut(u32) -> bool) -> u32 {
let exhaustive_limit = current.min(1024);
for candidate in 0..=exhaustive_limit {
if violates(candidate) {
return candidate;
}
}
if current <= exhaustive_limit {
return current;
}
let mut low = exhaustive_limit.saturating_add(1);
let mut high = current;
while low < high {
let mid = low + ((high - low) / 2);
if violates(mid) {
high = mid;
} else {
low = mid.saturating_add(1);
}
}
if low < current && violates(low) {
current = low;
}
current
}
fn binary_pair_violates(
f: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
a: u32,
b: u32,
) -> Result<bool, SupportError> {
match law {
AlgebraicLaw::Commutative => Ok(call_binary(f, a, b)? != call_binary(f, b, a)?),
AlgebraicLaw::Associative => Ok(false), AlgebraicLaw::Identity { element } => {
let e = *element;
Ok(call_binary(f, a, e)? != a || call_binary(f, e, a)? != a)
}
AlgebraicLaw::SelfInverse { result } => {
let r = *result;
Ok(call_binary(f, a, a)? != r || call_binary(f, b, b)? != r)
}
AlgebraicLaw::Idempotent => Ok(call_binary(f, a, a)? != a || call_binary(f, b, b)? != b),
AlgebraicLaw::Absorbing { element } => {
let z = *element;
Ok(call_binary(f, a, z)? != z
|| call_binary(f, z, a)? != z
|| call_binary(f, b, z)? != z
|| call_binary(f, z, b)? != z)
}
AlgebraicLaw::Bounded { lo, hi } => {
let ab = call_binary(f, a, b)?;
Ok(ab < *lo || ab > *hi)
}
AlgebraicLaw::ZeroProduct { holds: true } => {
Ok(call_binary(f, a, b)? == 0 && a != 0 && b != 0)
}
AlgebraicLaw::ZeroProduct { holds: false } => {
Ok(call_binary(f, a, b)? == 0 && a != 0 && b != 0)
}
AlgebraicLaw::Custom { check, .. } => Ok(!check(f, &[a, b])),
_ => Ok(false),
}
}
fn unary_input_violates(
f: fn(&[u8]) -> Vec<u8>,
law: &AlgebraicLaw,
a: u32,
) -> Result<bool, SupportError> {
match law {
AlgebraicLaw::Involution => {
let fa = call_unary(f, a)?;
Ok(call_unary(f, fa)? != a)
}
AlgebraicLaw::Bounded { lo, hi } => {
let fa = call_unary(f, a)?;
Ok(fa < *lo || fa > *hi)
}
AlgebraicLaw::Custom { check, .. } => Ok(!check(f, &[a])),
_ => Ok(false),
}
}