pub mod aig;
pub mod blast;
mod cuts;
pub mod domain;
pub mod drup;
mod exhaustion;
pub mod finite;
mod fp;
mod relations;
mod rule;
mod sample_words;
pub mod sat;
#[cfg(test)]
mod tests;
use crate::error::Error;
use crate::expr::{Context, Expr, OpCode};
use crate::facts::Assumptions;
use crate::hash::IdMap;
use crate::{BitVec, SymbolKey, Width};
pub use exhaustion::Exhaustion;
pub use rule::{RuleOutcome, WidthReport, rule, rule_all_widths};
use aig::{Aig, Cnf, L};
use blast::Bits;
pub use sat::Limits;
use sat::{Answer, Lit, Solver, Step};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum SampleMode {
#[default]
Random,
Small,
Words,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Config {
pub max_conflicts: u64,
pub max_propagations: u64,
pub max_nodes: usize,
pub certificate: bool,
pub simplify: bool,
pub samples: u32,
pub sample_mode: SampleMode,
pub relational_lemmas: bool,
pub selector_branching: bool,
pub input_cancellation: bool,
pub join_factoring: bool,
pub xor3_encoding: bool,
pub carry_save_multiplication: bool,
pub word_sampling: bool,
pub exhaustive_inputs: u8,
}
impl Default for Config {
fn default() -> Self {
Config {
max_conflicts: 1_000_000,
max_propagations: u64::MAX,
max_nodes: 4_000_000,
certificate: false,
simplify: true,
samples: 256,
sample_mode: SampleMode::Random,
relational_lemmas: false,
selector_branching: false,
input_cancellation: false,
join_factoring: false,
xor3_encoding: false,
carry_save_multiplication: false,
word_sampling: false,
exhaustive_inputs: 0,
}
}
}
setters!(Config {
with_max_conflicts: max_conflicts: u64,
with_max_propagations: max_propagations: u64,
with_max_nodes: max_nodes: usize,
with_certificate: certificate: bool,
with_simplify: simplify: bool,
with_samples: samples: u32,
with_sample_mode: sample_mode: SampleMode,
with_relational_lemmas: relational_lemmas: bool,
with_selector_branching: selector_branching: bool,
with_input_cancellation: input_cancellation: bool,
with_join_factoring: join_factoring: bool,
with_xor3_encoding: xor3_encoding: bool,
with_carry_save_multiplication: carry_save_multiplication: bool,
with_word_sampling: word_sampling: bool,
with_exhaustive_inputs: exhaustive_inputs: u8,
});
impl Config {
pub fn limits(&self) -> Limits {
Limits {
conflicts: self.max_conflicts,
propagations: self.max_propagations,
}
}
}
fn simplifier() -> &'static crate::engine::Engine {
static ENGINE: std::sync::OnceLock<crate::engine::Engine> = std::sync::OnceLock::new();
ENGINE.get_or_init(|| {
#[cfg(feature = "mba")]
let strategy =
crate::engine::Strategy::deobfuscate().with_mba(crate::mba::MbaConfig::default());
#[cfg(not(feature = "mba"))]
let strategy = crate::engine::Strategy::deobfuscate();
crate::engine::Engine::builder()
.builtin()
.strategy(strategy)
.build()
.unwrap_or_else(|_| crate::engine::Engine::standard())
})
}
#[derive(Clone, Debug)]
pub struct Certificate {
pub vars: u32,
pub clauses: Vec<Vec<Lit>>,
pub proof: Vec<Step>,
pub exhaustion: Option<Exhaustion>,
}
impl Certificate {
pub fn check(&self) -> Result<(), String> {
match &self.exhaustion {
Some(exhaustion) => exhaustion.check(),
None => drup::check(&self.clauses, &self.proof),
}
}
pub fn dimacs(&self) -> String {
let mut s = format!("p cnf {} {}\n", self.vars, self.clauses.len());
for c in &self.clauses {
for l in c {
s.push_str(&l.dimacs().to_string());
s.push(' ');
}
s.push_str("0\n");
}
s
}
pub fn drat(&self) -> String {
let mut s = String::new();
for step in &self.proof {
let (pre, c) = match step {
Step::Add(c) => ("", c),
Step::Delete(c) => ("d ", c),
};
s.push_str(pre);
for l in c {
s.push_str(&l.dimacs().to_string());
s.push(' ');
}
s.push_str("0\n");
}
s
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Outcome {
Proved(Option<Certificate>),
Refuted(Vec<(SymbolKey, BitVec)>),
Unknown(Unknown),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum Unknown {
Budget {
conflicts: u64,
propagations: u64,
},
TooLarge {
nodes: usize,
},
Unsupported(String),
}
impl Unknown {
pub fn is_budget(&self) -> bool {
matches!(self, Unknown::Budget { .. })
}
}
impl core::fmt::Display for Unknown {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Unknown::Budget {
conflicts,
propagations,
} => write!(
f,
"no answer within {conflicts} conflicts ({propagations} propagations)"
),
Unknown::TooLarge { nodes } => {
write!(f, "the circuit is too large ({nodes} nodes built)")
}
Unknown::Unsupported(why) => f.write_str(why),
}
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Stats {
pub nodes: usize,
pub samples: u64,
pub vars: u32,
pub clauses: usize,
pub learned: usize,
pub conflicts: u64,
pub decisions: u64,
pub propagations: u64,
}
pub(crate) struct Blaster<'c> {
pub(crate) cx: &'c mut Context,
pub(crate) g: Aig,
bits: IdMap<u32, Bits>,
pub(crate) symbols: Vec<(u32, Bits)>,
selectors: Vec<L>,
selector_hints: bool,
input_cancellation: bool,
join_factoring: bool,
carry_save_multiplication: bool,
max_nodes: usize,
too_large: bool,
}
impl<'c> Blaster<'c> {
pub(crate) fn new(cx: &'c mut Context, max_nodes: usize) -> Blaster<'c> {
Blaster {
cx,
g: Aig::new(),
bits: IdMap::default(),
symbols: Vec::new(),
selectors: Vec::new(),
selector_hints: false,
input_cancellation: false,
join_factoring: false,
carry_save_multiplication: false,
max_nodes,
too_large: false,
}
}
pub(crate) fn blast(&mut self, root: u32) -> Result<Bits, Error> {
let bits = &self.bits;
let order = self
.cx
.post_order_ids_pruned(&[root], |e| bits.contains_key(&e.index()));
for i in order {
if self.bits.contains_key(&i) {
continue;
}
if self.g.len() > self.max_nodes {
self.too_large = true;
return Err(Error::Unsupported("the circuit is too large".into()));
}
let b = self.node(i)?;
self.bits.insert(i, b);
}
Ok(self.bits[&root].clone())
}
fn node(&mut self, i: u32) -> Result<Bits, Error> {
use blast::*;
let n = self.cx.node(i);
let w = usize::from(n.width);
let get = |s: &Self, j: u32| s.bits[&j].clone();
let g = &mut self.g;
Ok(match n.op {
OpCode::Const => konst(&self.cx.const_val(i).unwrap_or(BitVec::zero(Width::W1))),
OpCode::Sym => {
let known = self.cx.declared_at(i);
let b: Bits = (0..w)
.map(|j| match known {
Some(k) if k.known_one().bit(j as u16) == Some(true) => aig::TRUE,
Some(k) if k.known_zero().bit(j as u16) == Some(true) => aig::FALSE,
_ => g.input(),
})
.collect();
self.symbols.push((i, b.clone()));
b
}
OpCode::Not => not(g, &self.bits[&n.a]),
OpCode::Neg => {
let a = get(self, n.a);
neg(&mut self.g, &a)
}
OpCode::Popcnt => popcnt(g, &self.bits[&n.a]),
OpCode::Clz => clz(g, &self.bits[&n.a]),
OpCode::Ctz => ctz(g, &self.bits[&n.a]),
OpCode::Bswap => bswap(&self.bits[&n.a]),
OpCode::BitRev => bitrev(&self.bits[&n.a]),
OpCode::Zext => {
let mut a = get(self, n.a);
a.resize(w, aig::FALSE);
a
}
OpCode::Sext => {
let mut a = get(self, n.a);
let top = *a.last().unwrap_or(&aig::FALSE);
a.resize(w, top);
a
}
OpCode::Extract => {
let a = get(self, n.a);
let lo = n.b as usize;
a[lo..lo + w].to_vec()
}
OpCode::Concat => {
let (hi, mut lo) = (get(self, n.a), get(self, n.b));
lo.extend(hi);
lo
}
OpCode::Select => {
let (c, t, e) = (get(self, n.a)[0], get(self, n.b), get(self, n.c));
mux(&mut self.g, c, &t, &e)
}
OpCode::Eq | OpCode::Ne | OpCode::Ult | OpCode::Ule | OpCode::Slt | OpCode::Sle => {
let (a, b) = (get(self, n.a), get(self, n.b));
let g = &mut self.g;
let r = match n.op {
OpCode::Eq if self.join_factoring => eq_factored(g, &a, &b),
OpCode::Ne if self.join_factoring => eq_factored(g, &a, &b) ^ 1,
OpCode::Eq => eq(g, &a, &b),
OpCode::Ne => eq(g, &a, &b) ^ 1,
OpCode::Ult => ult(g, &a, &b),
OpCode::Ule => ule(g, &a, &b),
OpCode::Slt => slt(g, &a, &b),
_ => sle(g, &a, &b),
};
vec![r]
}
op if op.as_bin().is_some() => {
let (a, b) = (get(self, n.a), get(self, n.b));
if self.selector_hints && matches!(op, OpCode::Shl | OpCode::LShr | OpCode::AShr) {
let unknown: Vec<_> = b
.iter()
.copied()
.filter(|&l| l > aig::TRUE)
.take(5)
.collect();
if !unknown.is_empty() && unknown.len() <= 4 {
self.selectors.extend(unknown);
}
}
let g = &mut self.g;
match op {
OpCode::Add => add(g, &a, &b),
OpCode::Sub => sub(g, &a, &b),
OpCode::Mul if self.carry_save_multiplication => mul_carry_save(g, &a, &b),
OpCode::Mul => mul(g, &a, &b),
OpCode::UMulHi => mulhi(g, &a, &b, false),
OpCode::SMulHi => mulhi(g, &a, &b, true),
OpCode::UDiv => udivrem(g, &a, &b).0,
OpCode::URem => udivrem(g, &a, &b).1,
OpCode::SDiv => sdiv(g, &a, &b),
OpCode::SRem => srem(g, &a, &b),
OpCode::And => and(g, &a, &b),
OpCode::Or => or(g, &a, &b),
OpCode::Xor if self.input_cancellation => xor_cancel_input(g, &a, &b),
OpCode::Xor => xor(g, &a, &b),
OpCode::Shl => shift(g, &a, &b, true, aig::FALSE),
OpCode::LShr => shift(g, &a, &b, false, aig::FALSE),
OpCode::AShr => {
let s = *a.last().unwrap_or(&aig::FALSE);
shift(g, &a, &b, false, s)
}
OpCode::RotL => rotate(g, &a, &b, true),
OpCode::RotR => rotate(g, &a, &b, false),
OpCode::Pdep => pdep(g, &a, &b),
_ => pext(g, &a, &b),
}
}
op if op.as_ext().is_some() => self.ext(i)?,
_ => {
let kids: Vec<Bits> = n.children().map(|c| self.bits[&c].clone()).collect();
fp::blast(self, i, &kids)?
}
})
}
fn ext(&mut self, i: u32) -> Result<Bits, Error> {
let n = self.cx.node(i);
let Some((_, k)) = n.op.as_ext() else {
return Err(Error::Unsupported("not an extension output".into()));
};
let args: Vec<Expr> = n.children().map(|c| self.cx.handle(c)).collect();
let reg = self
.cx
.registry
.clone()
.ok_or_else(|| Error::Unsupported("an extension without its registry".into()))?;
let op = reg
.op_at(n.aux)
.ok_or_else(|| Error::Unsupported("an extension without its operation".into()))?;
let Some(ex) = op.expand(self.cx, &args) else {
return Err(Error::Unsupported(format!(
"`{}` has no expansion to reason about",
op.name()
)));
};
let outs = ex?;
let e = self.cx.id(outs[k])?;
self.blast(e)
}
fn unknown(&self, e: Error) -> Result<Unknown, Error> {
match e {
_ if self.too_large => Ok(Unknown::TooLarge {
nodes: self.g.len(),
}),
Error::Unsupported(why) => Ok(Unknown::Unsupported(why)),
e => Err(e),
}
}
}
struct Search {
solver: Solver,
cnf: Cnf,
}
pub struct Question {
root: Expr,
symbols: Vec<(SymbolKey, Width, Bits)>,
search: Option<Box<Search>>,
outcome: Option<Outcome>,
stats: Stats,
}
impl core::fmt::Debug for Question {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Question")
.field("outcome", &self.outcome)
.field("stats", &self.stats)
.finish_non_exhaustive()
}
}
impl Question {
pub fn valid(cx: &mut Context, p: Expr, cfg: &Config) -> Result<Question, Error> {
Question::valid_under(cx, p, None, cfg)
}
pub fn equal(cx: &mut Context, a: Expr, b: Expr, cfg: &Config) -> Result<Question, Error> {
let (ai, bi) = (cx.id(a)?, cx.id(b)?);
if cx.wid(ai) != cx.wid(bi) {
return Err(crate::WidthError::Mismatch {
left: cx.wid(ai),
right: cx.wid(bi),
}
.into());
}
let p = cx.c_cmp(crate::CmpOp::Eq, ai, bi)?;
let p = cx.handle(p);
Question::valid(cx, p, cfg)
}
pub fn valid_under(
cx: &mut Context,
p: Expr,
assumptions: Option<&Assumptions>,
cfg: &Config,
) -> Result<Question, Error> {
Self::valid_under_with_pairs(cx, p, assumptions, &[], cfg)
}
pub fn valid_with_pairs(
cx: &mut Context,
p: Expr,
pairs: &[finite::CheckedPair],
cfg: &Config,
) -> Result<Question, Error> {
Self::valid_under_with_pairs(cx, p, None, pairs, cfg)
}
pub fn valid_under_with_pairs(
cx: &mut Context,
p: Expr,
assumptions: Option<&Assumptions>,
pairs: &[finite::CheckedPair],
cfg: &Config,
) -> Result<Question, Error> {
let pi = cx.id(p)?;
if cx.wid(pi) != 1 {
return Err(crate::WidthError::Mismatch {
left: 1,
right: cx.wid(pi),
}
.into());
}
let mut q = Question {
root: p,
symbols: Vec::new(),
search: None,
outcome: None,
stats: Stats::default(),
};
let decided = |mut q: Question, o: Outcome| {
q.outcome = Some(o);
Ok(q)
};
let mut cons: Vec<u32> = Vec::new();
if let Some(a) = assumptions {
for predicate in a.predicates(cx)? {
cons.push(cx.id(predicate)?);
}
}
let mut root = pi;
for &c in &cons {
let nc = cx.c_un(crate::UnOp::Not, c)?;
root = cx.c_bin(crate::BinOp::Or, nc, root)?;
}
q.root = cx.handle(root);
let mut goal_node = pi;
if !cfg.certificate {
for pair in pairs {
let rewritten = pair.simplify(cx, cx.handle(goal_node))?;
goal_node = cx.id(rewritten)?;
}
if cx
.const_val(goal_node)
.is_some_and(|value| !value.is_zero())
{
return decided(q, Outcome::Proved(None));
}
}
if cfg.simplify && !cfg.certificate {
let run = match assumptions {
Some(a) => crate::engine::Run::default().with_assumptions(a),
None => crate::engine::Run::default(),
};
let goal = cx.handle(goal_node);
let out = simplifier().run(cx, &[goal], run)?.roots[0];
let s = cx.id(out.expr)?;
if cx.const_val(s).is_some_and(|v| !v.is_zero()) {
return decided(q, Outcome::Proved(None));
}
goal_node = s;
}
let mut word_sampled = false;
if (cfg.word_sampling || cfg.sample_mode == SampleMode::Words)
&& cfg.samples != 0
&& let Some(trial) = sample_words::try_sample(cx, goal_node, &cons, assumptions, cfg)
{
q.stats.samples = trial.samples;
word_sampled = true;
if let Some(mut model) = trial.model {
complete_model(cx, q.root, &mut model)?;
let outcome = q.refuted(cx, model)?;
return decided(q, outcome);
}
if trial.complete && !cfg.certificate {
return decided(q, Outcome::Proved(None));
}
}
let mut exhausted = false;
if cfg.exhaustive_inputs != 0
&& let Some((failure, evaluated)) =
sample_words::try_exhaust(cx, goal_node, &cons, assumptions, cfg.exhaustive_inputs)
{
q.stats.samples += evaluated;
match failure {
Some(mut model) => {
complete_model(cx, q.root, &mut model)?;
let outcome = q.refuted(cx, model)?;
return decided(q, outcome);
}
None if !cfg.certificate => return decided(q, Outcome::Proved(None)),
None => exhausted = true,
}
}
let constant_goal = cuts::constant_true(cx, goal_node);
let mut b = Blaster::new(cx, cfg.max_nodes);
b.selector_hints = cfg.selector_branching;
b.input_cancellation = cfg.input_cancellation;
b.join_factoring = cfg.join_factoring;
b.carry_save_multiplication = cfg.carry_save_multiplication;
if constant_goal {
b.bits.insert(goal_node, vec![aig::TRUE]);
}
let mut blasted = || -> Result<(L, Vec<L>), Error> {
let goal = b.blast(goal_node)?[0];
let mut extra = Vec::new();
for &c in &cons {
extra.push(b.blast(c)?[0]);
}
Ok((goal, extra))
};
let (goal, extra) = match blasted() {
Ok(r) => r,
Err(e) => {
let why = b.unknown(e)?;
return decided(q, Outcome::Unknown(why));
}
};
q.stats.nodes = b.g.len();
if b.g.len() > cfg.max_nodes {
let why = Unknown::TooLarge { nodes: b.g.len() };
return decided(q, Outcome::Unknown(why));
}
for (s, bits) in &b.symbols {
let h = b.cx.handle(*s);
let key =
b.cx.symbol_id(h)?
.and_then(|id| b.cx.symbol_key(id))
.cloned()
.ok_or_else(|| Error::Contract("a symbol without its key".into()))?;
q.symbols.push((key, b.cx.width_of(*s), bits.clone()));
}
for id in b.cx.symbols_in(&[q.root])? {
if let (Some(k), Some(w)) = (b.cx.symbol_key(id).cloned(), b.cx.symbol_width(id))
&& !q.symbols.iter().any(|(kk, _, _)| *kk == k)
{
let bits =
b.cx.find_symbol(&k)
.and_then(|e| b.cx.declared_at(e.index()))
.map(|known| blast::konst(&known.known_one()))
.unwrap_or_default();
q.symbols.push((k, w, bits));
}
}
if exhausted {
let exhaustion = Exhaustion::of(&b.g, goal, &extra);
if exhaustion.inputs() <= u32::from(cfg.exhaustive_inputs.min(63)) {
let cert = Certificate {
vars: 0,
clauses: Vec::new(),
proof: Vec::new(),
exhaustion: Some(exhaustion),
};
return decided(q, Outcome::Proved(Some(cert)));
}
}
let batches = if word_sampled {
0
} else {
cfg.samples.div_ceil(64)
};
for batch in 0..batches {
let vals = b.g.eval_words(|k| match cfg.sample_mode {
SampleMode::Random => sample(batch, k),
SampleMode::Small | SampleMode::Words => small_sample(batch, k),
});
q.stats.samples += 64;
let mut bad = !Aig::word(&vals, goal);
for &e in &extra {
bad &= Aig::word(&vals, e);
}
if bad != 0 {
let lane = bad.trailing_zeros();
let ce = Question::counterexample(&q.symbols, |l| {
(Aig::word(&vals, l) >> lane) & 1 == 1
});
let o = q.refuted(b.cx, ce)?;
return decided(q, o);
}
}
let mut solver = Solver::new();
if cfg.certificate {
solver.log_proof();
}
let mut roots = vec![goal];
roots.extend_from_slice(&extra);
let mut cnf = Cnf::encode_with_xor3(
&b.g,
&roots,
&mut solver,
cfg.certificate,
cfg.xor3_encoding,
);
for &e in &extra {
cnf.assert(e, &mut solver);
}
cnf.assert(goal ^ 1, &mut solver);
let mut asserted = vec![goal ^ 1];
asserted.extend_from_slice(&extra);
relations::strengthen(&b.g, &asserted, &cnf, &mut solver, cfg.relational_lemmas);
if cfg.selector_branching {
for &l in &b.selectors {
if let Some(l) = cnf.mapped_lit(l) {
solver.prefer_selector(l);
}
}
}
q.stats.vars = solver.num_vars();
q.stats.clauses = cnf.emitted();
q.stats.learned = solver.num_learnts();
q.stats.propagations = solver.propagations;
q.search = Some(Box::new(Search { solver, cnf }));
Ok(q)
}
pub fn solve(&mut self, cx: &mut Context, limits: Limits) -> Result<Outcome, Error> {
if let Some(o) = &self.outcome {
return Ok(o.clone());
}
let Some(s) = self.search.as_mut() else {
return Err(Error::Contract(
"a question neither decided nor open".into(),
));
};
let answer = s.solver.solve_within(limits);
self.stats.learned = s.solver.num_learnts();
self.stats.conflicts = s.solver.conflicts;
self.stats.decisions = s.solver.decisions;
self.stats.propagations = s.solver.propagations;
let o = match answer {
Answer::Unknown => {
return Ok(Outcome::Unknown(Unknown::Budget {
conflicts: s.solver.conflicts,
propagations: s.solver.propagations,
}));
}
Answer::Unsat => {
let cert = s.solver.take_proof().map(|proof| Certificate {
vars: s.solver.num_vars(),
clauses: core::mem::take(&mut s.cnf.clauses),
proof,
exhaustion: None,
});
Outcome::Proved(cert)
}
Answer::Sat(model) => {
let cnf = &s.cnf;
let ce = Question::counterexample(&self.symbols, |l| cnf.value(l, &model));
self.refuted(cx, ce)?
}
};
self.search = None;
self.outcome = Some(o.clone());
Ok(o)
}
pub fn outcome(&self) -> Option<&Outcome> {
self.outcome.as_ref()
}
pub fn stats(&self) -> Stats {
self.stats
}
fn counterexample(symbols: &[(SymbolKey, Width, Bits)], bit: impl Fn(L) -> bool) -> Model {
symbols
.iter()
.map(|(k, w, bits)| {
let mut limbs = [0u64; 8];
for (j, &b) in bits.iter().enumerate() {
if bit(b) {
limbs[j / 64] |= 1 << (j % 64);
}
}
(k.clone(), BitVec::wrapping_from_limbs(*w, &limbs))
})
.collect()
}
fn refuted(&self, cx: &mut Context, ce: Model) -> Result<Outcome, Error> {
let env =
crate::FnEnv(|k: &SymbolKey, _| ce.iter().find(|(kk, _)| kk == k).map(|(_, v)| *v));
let v = cx.eval(&[self.root], &env)?[0];
if v.is_zero() {
Ok(Outcome::Refuted(ce))
} else {
Err(Error::Contract(format!(
"the prover's counterexample does not refute {}",
cx.display(self.root)
)))
}
}
}
fn complete_model(cx: &mut Context, root: Expr, model: &mut Model) -> Result<(), Error> {
for id in cx.symbols_in(&[root])? {
let key = cx.symbol_key(id).unwrap().clone();
if !model.iter().any(|(k, _)| *k == key) {
let symbol = cx.find_symbol(&key).unwrap();
let value = cx.declared_known(symbol)?.map_or_else(
|| BitVec::zero(cx.symbol_width(id).unwrap()),
|k| k.known_one(),
);
model.push((key, value));
}
}
Ok(())
}
fn small_sample(batch: u32, k: u32) -> u64 {
const LOW: [u64; 6] = [
0xaaaa_aaaa_aaaa_aaaa,
0xcccc_cccc_cccc_cccc,
0xf0f0_f0f0_f0f0_f0f0,
0xff00_ff00_ff00_ff00,
0xffff_0000_ffff_0000,
0xffff_ffff_0000_0000,
];
if k < 6 {
LOW[k as usize]
} else if k - 6 < 32 && (batch >> (k - 6)) & 1 == 1 {
u64::MAX
} else {
0
}
}
fn sample(batch: u32, k: u32) -> u64 {
let mut z = (u64::from(batch) << 32 | u64::from(k)).wrapping_add(0x9e37_79b9_7f4a_7c15);
z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
z ^= z >> 31;
if batch == 0 { (z & !0b11) | 0b10 } else { z }
}
pub fn valid(cx: &mut Context, p: Expr, cfg: &Config) -> Result<Outcome, Error> {
valid_under(cx, p, None, cfg)
}
pub fn valid_under(
cx: &mut Context,
p: Expr,
assumptions: Option<&Assumptions>,
cfg: &Config,
) -> Result<Outcome, Error> {
Question::valid_under(cx, p, assumptions, cfg)?.solve(cx, cfg.limits())
}
pub fn equal(cx: &mut Context, a: Expr, b: Expr, cfg: &Config) -> Result<Outcome, Error> {
Question::equal(cx, a, b, cfg)?.solve(cx, cfg.limits())
}
pub type Model = Vec<(SymbolKey, BitVec)>;
pub fn satisfy(
cx: &mut Context,
p: Expr,
cfg: &Config,
) -> Result<Result<Option<Model>, Unknown>, Error> {
let np = cx.un(crate::UnOp::Not, p)?;
Ok(match valid(cx, np, cfg)? {
Outcome::Proved(_) => Ok(None),
Outcome::Refuted(m) => Ok(Some(m)),
Outcome::Unknown(why) => Err(why),
})
}