use std::collections::BTreeSet;
use super::witness::{ClauseSet, Core, Model, Step, WitnessResult};
pub struct Rng(u64);
impl Rng {
#[must_use]
pub fn new(seed: u64) -> Self {
Self(seed)
}
pub fn next(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn below(&mut self, n: usize) -> usize {
usize::try_from(self.next() % (n as u64)).unwrap_or(0)
}
pub fn coin(&mut self) -> bool {
self.next() & 1 == 1
}
}
#[must_use]
pub fn var(i: usize) -> String {
format!("c{i}")
}
fn ordered(a: usize, b: usize) -> (String, String) {
let (x, y) = (var(a), var(b));
if x <= y {
(x, y)
} else {
(y, x)
}
}
pub struct Planted {
pub n: usize,
pub clauses: ClauseSet,
pub planted: WitnessResult,
}
pub fn consistent(rng: &mut Rng) -> Planted {
let n = 2 + rng.below(7);
let mut sigma: Vec<bool> = (0..n).map(|_| rng.coin()).collect();
sigma[rng.below(n)] = true; let mut cs = ClauseSet::default();
for (i, t) in sigma.iter().enumerate() {
if *t && (cs.units.is_empty() || rng.coin()) {
cs.units.insert(var(i));
}
}
for _ in 0..(3 * n) {
let (a, b) = (rng.below(n), rng.below(n));
if a == b {
continue;
}
if !sigma[a] || sigma[b] {
cs.implies.insert((var(a), var(b)));
}
if !(sigma[a] && sigma[b]) {
cs.conflicts.insert(ordered(a, b));
}
}
let planted = WitnessResult::Model(Model {
false_vars: (0..n).filter(|i| !sigma[*i]).map(var).collect(),
});
Planted {
n,
clauses: cs,
planted,
}
}
fn path(rng: &mut Rng, n: usize, from: usize, len: usize, avoid: &BTreeSet<usize>) -> Vec<usize> {
let mut p = vec![from];
for _ in 0..len {
let free: Vec<usize> = (0..n)
.filter(|v| !p.contains(v) && !avoid.contains(v))
.collect();
if free.is_empty() {
break;
}
p.push(free[rng.below(free.len())]);
}
p
}
pub fn contradicted(rng: &mut Rng) -> Planted {
let n = 3 + rng.below(6);
let u = rng.below(n);
let ll = 1 + rng.below(3);
let left = path(rng, n, u, ll, &BTreeSet::new());
let used: BTreeSet<usize> = left[1..].iter().copied().collect();
let rl = 1 + rng.below(3);
let right = path(rng, n, u, rl, &used);
let (x, y) = (*left.last().unwrap_or(&u), *right.last().unwrap_or(&u));
let y = if x == y { (x + 1) % n } else { y };
let mut cs = ClauseSet::default();
cs.units.insert(var(u));
let mut steps = vec![Step::Unit(var(u))];
let mut derived: BTreeSet<usize> = [u].into();
for p in [&left, &right] {
for w in p.windows(2) {
cs.implies.insert((var(w[0]), var(w[1])));
if derived.insert(w[1]) {
steps.push(Step::Implies(var(w[0]), var(w[1])));
}
}
}
if !derived.contains(&y) {
cs.implies.insert((var(u), var(y)));
steps.push(Step::Implies(var(u), var(y)));
}
cs.conflicts.insert(ordered(x, y));
for _ in 0..(2 * n) {
let (a, b) = (rng.below(n), rng.below(n));
match rng.below(3) {
0 => {
cs.units.insert(var(a));
}
1 if a != b => {
cs.implies.insert((var(a), var(b)));
}
_ if a != b => {
cs.conflicts.insert(ordered(a, b));
}
_ => {}
}
}
let conflict = (var(x), var(y));
Planted {
n,
clauses: cs,
planted: WitnessResult::UnsatCore(Core { steps, conflict }),
}
}
fn index(v: &str) -> Option<usize> {
v.strip_prefix('c').and_then(|d| d.parse().ok())
}
#[must_use]
pub fn satisfiable(cs: &ClauseSet, n: usize) -> bool {
let t = |m: u32, v: &str| index(v).is_some_and(|i| m & (1 << i) != 0);
(0u32..(1 << n)).any(|m| {
cs.units.iter().all(|u| t(m, u))
&& cs.implies.iter().all(|(a, b)| !t(m, a) || t(m, b))
&& cs.conflicts.iter().all(|(a, b)| !t(m, a) || !t(m, b))
})
}
pub fn adversarial_cores(rng: &mut Rng, cs: &ClauseSet, n: usize) -> Vec<WitnessResult> {
let mut out = Vec::new();
for _ in 0..12 {
let (steps, derived) = valid_derivation(rng, cs, n);
let conflict = closing_conflict(rng, cs, &derived, n);
out.push(WitnessResult::UnsatCore(Core { steps, conflict }));
out.push(WitnessResult::UnsatCore(random_core(rng, n)));
}
out
}
fn valid_derivation(rng: &mut Rng, cs: &ClauseSet, n: usize) -> (Vec<Step>, Vec<String>) {
let mut steps = Vec::new();
let mut derived: Vec<String> = Vec::new();
for u in &cs.units {
if rng.coin() || derived.is_empty() {
steps.push(Step::Unit(u.clone()));
derived.push(u.clone());
}
}
for _ in 0..n {
let from: Vec<&(String, String)> = cs
.implies
.iter()
.filter(|(a, b)| derived.contains(a) && !derived.contains(b))
.collect();
if from.is_empty() {
break;
}
let (a, b) = from[rng.below(from.len())].clone();
steps.push(Step::Implies(a, b.clone()));
derived.push(b);
}
(steps, derived)
}
fn closing_conflict(
rng: &mut Rng,
cs: &ClauseSet,
derived: &[String],
n: usize,
) -> (String, String) {
let pick = |rng: &mut Rng, d: &[String]| d[rng.below(d.len())].clone();
match rng.below(3) {
0 => {
let x = pick(rng, derived);
(x, pick(rng, derived))
}
1 if !cs.conflicts.is_empty() => {
let all: Vec<&(String, String)> = cs.conflicts.iter().collect();
all[rng.below(all.len())].clone()
}
_ => {
let x = var(rng.below(n));
(x, var(rng.below(n)))
}
}
}
fn random_core(rng: &mut Rng, n: usize) -> Core {
let steps: Vec<Step> = (0..=rng.below(n))
.map(|_| {
if rng.coin() {
Step::Unit(var(rng.below(n)))
} else {
Step::Implies(var(rng.below(n)), var(rng.below(n)))
}
})
.collect();
let x = var(rng.below(n));
Core {
steps,
conflict: (x, var(rng.below(n))),
}
}
pub fn adversarial_models(rng: &mut Rng, n: usize) -> Vec<WitnessResult> {
let masks: Vec<u32> = if n <= 6 {
(0u32..(1 << n)).collect()
} else {
(0..64)
.map(|_| u32::try_from(rng.next() & ((1 << n) - 1)).unwrap_or(0))
.collect()
};
masks
.into_iter()
.map(|m| {
WitnessResult::Model(Model {
false_vars: (0..n).filter(|i| m & (1 << i) != 0).map(var).collect(),
})
})
.collect()
}