#![doc = document_features::document_features!()]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![allow(clippy::type_complexity)]
use std::collections::hash_map::Entry;
use std::fmt::{self, Write};
use bumpalo::boxed::Box as BumpBox;
use bumpalo::collections::Vec as BumpVec;
use derive_builder::Builder;
use fixedbitset::FixedBitSet;
use rustc_hash::FxHashMap;
pub mod aiger;
pub mod dimacs;
pub mod nnf;
mod tv_bitvec;
mod util;
mod vec2d;
use tv_bitvec::TVBitVec;
pub use vec2d::{Vec2d, Vec2dIter};
#[cfg(feature = "load-file")]
mod load_file;
#[cfg(feature = "load-file")]
pub use load_file::*;
pub type Var = usize;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Literal(usize);
impl Literal {
const POLARITY_BIT: u32 = 0;
const GATE_BIT: u32 = 1;
const VAR_LSB: u32 = 2;
pub const MAX_INPUT: usize = (usize::MAX >> Self::VAR_LSB) - 2;
pub const MAX_GATE: usize = usize::MAX >> Self::VAR_LSB;
pub const FALSE: Self = Self(0);
pub const TRUE: Self = Self(1 << Self::POLARITY_BIT);
pub const UNDEF: Self = Self((Self::MAX_INPUT + 2) << Self::VAR_LSB);
#[track_caller]
#[inline]
pub const fn from_input(negative: bool, input: Var) -> Self {
debug_assert!(input <= Self::MAX_INPUT, "input too large");
Self(((input + 1) << Self::VAR_LSB) | ((negative as usize) << Self::POLARITY_BIT))
}
#[track_caller]
#[inline]
const fn from_input_or_false(negative: bool, input: Var) -> Self {
debug_assert!(input <= Self::MAX_INPUT + 1, "input too large");
Self((input << Self::VAR_LSB) | ((negative as usize) << Self::POLARITY_BIT))
}
#[track_caller]
#[inline]
pub const fn from_gate(negative: bool, gate: Var) -> Self {
debug_assert!(gate <= Self::MAX_GATE, "gate number too large");
Self(
(gate << Self::VAR_LSB)
| (1 << Self::GATE_BIT)
| ((negative as usize) << Self::POLARITY_BIT),
)
}
#[inline(always)]
pub const fn is_positive(self) -> bool {
self.0 & (1 << Self::POLARITY_BIT) == 0
}
#[inline(always)]
pub const fn is_negative(self) -> bool {
!self.is_positive()
}
#[inline(always)]
pub const fn positive(self) -> Self {
Self(self.0 & !(1 << Self::POLARITY_BIT))
}
#[inline(always)]
pub const fn negative(self) -> Self {
Self(self.0 | (1 << Self::POLARITY_BIT))
}
#[inline(always)]
pub const fn is_input(self) -> bool {
self.get_input().is_some()
}
#[inline(always)]
pub const fn is_gate(self) -> bool {
self.0 & (1 << Self::GATE_BIT) != 0
}
#[inline]
pub const fn get_input(self) -> Option<Var> {
if self.is_gate() {
return None;
}
(self.0 >> Self::VAR_LSB).checked_sub(1)
}
#[inline]
pub const fn get_gate_no(self) -> Option<Var> {
if self.is_gate() {
return Some(self.0 >> Self::VAR_LSB);
}
None
}
#[inline]
pub fn apply_gate_map(self, gate_map: &[Literal]) -> Self {
if let Some(gate) = self.get_gate_no() {
if let Some(mapped) = gate_map.get(gate) {
*mapped ^ self.is_negative()
} else {
Self::UNDEF
}
} else {
self
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let i = self.0 >> Literal::VAR_LSB;
let (kind, i) = if self.0 & (1 << Self::GATE_BIT) != 0 {
('g', i)
} else {
if i == 0 {
return f.write_char(if self.is_positive() { '⊥' } else { '⊤' });
}
if i == Literal::MAX_INPUT + 2 {
return f.write_str(if self.is_positive() { "+U" } else { "-U" });
}
('i', i - 1)
};
let sign = if self.is_positive() { '+' } else { '-' };
write!(f, "{sign}{kind}{i}")
}
}
impl fmt::Debug for Literal {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
<Self as fmt::Display>::fmt(self, f)
}
}
impl std::ops::Not for Literal {
type Output = Self;
fn not(self) -> Self {
Self(self.0 ^ (1 << Self::POLARITY_BIT))
}
}
impl std::ops::BitXor<bool> for Literal {
type Output = Self;
#[inline(always)]
fn bitxor(self, rhs: bool) -> Self::Output {
Self(self.0 ^ ((rhs as usize) << Self::POLARITY_BIT))
}
}
#[allow(missing_docs)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Tree<T> {
Inner(Box<[Tree<T>]>),
Leaf(T),
}
impl<T: Clone> Tree<T> {
fn flatten_into(&self, into: &mut Vec<T>) {
match self {
Tree::Inner(sub) => sub.iter().for_each(|t| t.flatten_into(into)),
Tree::Leaf(v) => into.push(v.clone()),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Problem {
pub circuit: Circuit,
pub details: ProblemDetails,
}
impl Problem {
pub fn simplify(&self) -> Result<(Self, Vec<Literal>), Literal> {
let (circuit, map) = match &self.details {
ProblemDetails::Root(l) => self.circuit.simplify([*l]),
ProblemDetails::AIGER(aig) => {
let aig = &**aig;
self.circuit.simplify(
aig.latches
.iter()
.chain(aig.outputs.iter())
.chain(aig.bad.iter())
.chain(aig.invariants.iter())
.chain(aig.justice.all_elements().iter())
.chain(aig.fairness.iter())
.copied(),
)
}
}?;
let new_problem = Self {
circuit,
details: self.details.apply_gate_map(&map),
};
Ok((new_problem, map))
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ProblemDetails {
Root(Literal),
AIGER(Box<AIGERDetails>),
}
impl ProblemDetails {
pub fn apply_gate_map(&self, map: &[Literal]) -> Self {
match self {
ProblemDetails::Root(l) => ProblemDetails::Root(l.apply_gate_map(map)),
ProblemDetails::AIGER(aig) => ProblemDetails::AIGER(Box::new(aig.apply_gate_map(map))),
}
}
pub fn apply_gate_map_in_place(&mut self, map: &[Literal]) {
match self {
ProblemDetails::Root(l) => *l = l.apply_gate_map(map),
ProblemDetails::AIGER(aig) => aig.apply_gate_map_in_place(map),
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct VarSet {
len: usize,
order: Vec<Var>,
order_tree: Option<Tree<Var>>,
names: Vec<Option<String>>,
}
impl VarSet {
#[inline(always)]
pub const fn new(n: usize) -> Self {
Self {
len: n,
order: Vec::new(),
order_tree: None,
names: Vec::new(),
}
}
pub fn with_names(mut names: Vec<Option<String>>) -> Self {
let len = names.len();
while let Some(None) = names.last() {
names.pop();
}
Self {
len,
order: Vec::new(),
order_tree: None,
names,
}
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn order(&self) -> Option<&[Var]> {
if self.len != self.order.len() {
None
} else {
Some(&self.order)
}
}
#[inline]
pub fn order_tree(&self) -> Option<&Tree<Var>> {
self.order_tree.as_ref()
}
#[inline]
pub fn has_names(&self) -> bool {
!self.names.is_empty()
}
#[inline]
pub fn name(&self, var: Var) -> Option<&str> {
self.names.get(var)?.as_deref()
}
#[track_caller]
pub fn set_name(&mut self, var: Var, name: impl Into<String>) -> Option<String> {
if var >= self.names.len() {
if var >= self.len {
return None;
}
self.names.resize(var + 1, None);
}
self.names[var].replace(name.into())
}
#[allow(unused)]
fn check_valid(&self) {
assert!(self.order.is_empty() || self.order.len() == self.len);
assert!(!self.order.is_empty() || self.order_tree.is_none());
assert_ne!(self.names.last(), Some(&None));
}
}
type GateVec2d = vec2d::with_metadata::Vec2d<Literal, { Circuit::GATE_METADATA_BITS }>;
#[derive(Clone, PartialEq, Eq)]
pub struct Circuit {
inputs: VarSet,
gates: GateVec2d,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Gate<'a> {
pub kind: GateKind,
pub inputs: &'a [Literal],
}
impl fmt::Debug for Gate<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut builder = f.debug_tuple(match self.kind {
GateKind::And => "and",
GateKind::Or => "or",
GateKind::Xor => "xor",
});
for literal in self.inputs {
builder.field(literal);
}
builder.finish()
}
}
#[allow(unused)] impl<'a> Gate<'a> {
const fn and(inputs: &'a [Literal]) -> Self {
Self {
kind: GateKind::And,
inputs,
}
}
const fn or(inputs: &'a [Literal]) -> Self {
Self {
kind: GateKind::Or,
inputs,
}
}
const fn xor(inputs: &'a [Literal]) -> Self {
Self {
kind: GateKind::Xor,
inputs,
}
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum GateKind {
And,
Or,
Xor,
}
impl GateKind {
const fn empty_gate(self) -> Literal {
match self {
GateKind::And => Literal::TRUE,
GateKind::Or | GateKind::Xor => Literal::FALSE,
}
}
}
impl From<usize> for GateKind {
fn from(value: usize) -> Self {
match value {
_ if value == GateKind::And as usize => GateKind::And,
_ if value == GateKind::Or as usize => GateKind::Or,
_ => GateKind::Xor,
}
}
}
impl Circuit {
const GATE_METADATA_BITS: u32 = 2;
#[inline(always)]
pub fn new(inputs: VarSet) -> Self {
Self {
inputs,
gates: Default::default(),
}
}
#[inline(always)]
pub fn inputs(&self) -> &VarSet {
&self.inputs
}
#[inline(always)]
pub fn inputs_mut(&mut self) -> &mut VarSet {
&mut self.inputs
}
#[inline(always)]
pub fn reserve_gates(&mut self, additional: usize) {
self.gates.reserve_vectors(additional);
}
#[inline(always)]
pub fn reserve_gate_inputs(&mut self, additional: usize) {
self.gates.reserve_elements(additional);
}
#[inline(always)]
pub fn num_gates(&self) -> usize {
self.gates.len()
}
#[inline]
pub fn gate(&self, literal: Literal) -> Option<Gate<'_>> {
self.gate_for_no(literal.get_gate_no()?)
}
pub fn gate_for_no(&self, gate_no: Var) -> Option<Gate<'_>> {
if let Some((kind, inputs)) = self.gates.get(gate_no) {
Some(Gate {
kind: kind.into(),
inputs,
})
} else {
None
}
}
#[inline]
pub fn gate_inputs_mut(&mut self, literal: Literal) -> Option<&mut [Literal]> {
self.gates.get_mut(literal.get_gate_no()?)
}
#[inline(always)]
pub fn gate_inputs_mut_for_no(&mut self, gate_no: Var) -> Option<&mut [Literal]> {
self.gates.get_mut(gate_no)
}
#[track_caller]
#[inline]
pub fn set_gate_kind(&mut self, literal: Literal, kind: GateKind) {
self.gates.set_metadata(
literal
.get_gate_no()
.expect("`literal` must refer to a gate"),
kind as usize,
);
}
#[track_caller]
#[inline(always)]
pub fn set_gate_kind_for_no(&mut self, gate_no: Var, kind: GateKind) {
self.gates.set_metadata(gate_no, kind as usize);
}
#[track_caller]
#[inline]
pub fn set_last_gate_kind(&mut self, kind: GateKind) {
let n = self.gates.len();
if n == 0 {
panic!("there are no gates in the circuit");
}
self.gates.set_metadata(n - 1, kind as usize);
}
pub fn first_gate(&self) -> Option<Gate<'_>> {
let (kind, inputs) = self.gates.first()?;
Some(Gate {
kind: kind.into(),
inputs,
})
}
pub fn last_gate(&self) -> Option<Gate<'_>> {
let (kind, inputs) = self.gates.last()?;
Some(Gate {
kind: kind.into(),
inputs,
})
}
pub fn push_gate(&mut self, kind: GateKind) -> Literal {
let l = Literal::from_gate(false, self.gates.len());
self.gates.push_vec(kind as _);
l
}
#[inline(always)]
pub fn pop_gate(&mut self) -> bool {
self.gates.pop_vec()
}
#[inline(always)]
#[track_caller]
pub fn push_gate_input(&mut self, literal: Literal) {
self.gates.push_element(literal);
}
#[inline(always)]
#[track_caller]
pub fn push_gate_inputs(&mut self, literals: impl IntoIterator<Item = Literal>) {
self.gates.push_elements(literals);
}
#[inline(always)]
pub fn iter_gates(&self) -> CircuitGateIter<'_> {
CircuitGateIter(self.gates.iter())
}
#[inline]
pub fn retain_gates(&mut self, mut predicate: impl FnMut(&mut [Literal]) -> bool) {
self.gates.retain(move |_, inputs| predicate(inputs))
}
#[inline(always)]
pub fn clear_gates(&mut self) {
self.gates.clear();
}
pub fn find_cycle(&self) -> Option<Literal> {
let mut visited = FixedBitSet::with_capacity(self.gates.len() * 2);
fn inner(gates: &GateVec2d, visited: &mut FixedBitSet, index: usize) -> bool {
if visited.contains(index * 2 + 1) {
return false; }
if visited.contains(index * 2) {
return true; }
visited.insert(index * 2);
for &l in gates.get(index).unwrap().1 {
if l.is_gate() && inner(gates, visited, l.0 >> Literal::VAR_LSB) {
return true;
}
}
visited.insert(index * 2 + 1); false
}
for index in 0..self.gates.len() {
if inner(&self.gates, &mut visited, index) {
return Some(Literal::from_gate(false, index));
}
}
None
}
pub fn simplify(
&self,
roots: impl IntoIterator<Item = Literal>,
) -> Result<(Self, Vec<Literal>), Literal> {
const DISCOVERED: Literal = Literal::UNDEF.negative();
let bump = bumpalo::Bump::new();
let mut gate_map = Vec::new();
gate_map.resize(self.gates.len(), Literal::UNDEF);
let mut input_set = FixedBitSet::with_capacity(2 * (self.gates.len() + self.inputs.len()));
let mut new_gates: GateVec2d =
GateVec2d::with_capacity(self.gates.len(), self.gates.all_elements().len());
let mut unique_map = FxHashMap::default();
unique_map.reserve(self.gates.len());
fn inner<'a>(
bump: &'a bumpalo::Bump,
gates: &GateVec2d,
index: usize,
input_set: &mut FixedBitSet,
unique_map: &mut FxHashMap<(GateKind, BumpBox<'a, [Literal]>), Literal>,
new_gates: &mut GateVec2d,
gate_map: &mut [Literal],
) -> Result<(), Literal> {
if gate_map[index] == DISCOVERED {
return Err(Literal::from_gate(false, index));
}
if gate_map[index] != Literal::UNDEF {
return Ok(()); }
gate_map[index] = DISCOVERED;
let (meta, inputs) = gates.get(index).unwrap();
let kind = GateKind::from(meta);
for &l in inputs {
if let Some(gate) = l.get_gate_no() {
inner(
bump, gates, gate, input_set, unique_map, new_gates, gate_map,
)?;
}
}
let mut neg_out = false;
let mut mapped = BumpVec::with_capacity_in(inputs.len(), bump);
let known_inputs = input_set.len() - gates.len();
match kind {
GateKind::And | GateKind::Or => {
let (identity, dominator) = match kind {
GateKind::And => (Literal::TRUE, Literal::FALSE),
GateKind::Or => (Literal::FALSE, Literal::TRUE),
_ => unreachable!(),
};
for &l in inputs {
let l = l.get_gate_no().map_or(l, |i| gate_map[i] ^ l.is_negative());
if l.is_input() && l.get_input().unwrap() > known_inputs {
return Err(l);
}
if l == dominator {
gate_map[index] = dominator;
return Ok(());
}
if l != identity {
mapped.push(l);
}
}
}
GateKind::Xor => {
for &l in inputs {
neg_out ^= l.is_negative(); let l = if let Some(i) = l.get_gate_no() {
let l = gate_map[i];
neg_out ^= l.is_negative();
l.positive()
} else {
let l = l.positive();
debug_assert!(l != Literal::TRUE);
if l == Literal::FALSE {
continue; }
l
};
if l.is_input() && l.get_input().unwrap() > known_inputs {
return Err(l);
}
mapped.push(l);
}
}
};
let mut inputs = mapped;
if inputs.is_empty() {
gate_map[index] = match kind {
GateKind::And => Literal::TRUE,
GateKind::Or => Literal::FALSE,
GateKind::Xor => Literal::TRUE ^ neg_out,
};
return Ok(());
}
if inputs.len() >= 3
|| (inputs.len() == 2 && inputs[0].negative() == inputs[1].negative())
{
let no_gate_add = 2 * gates.len() - 2; let map = move |l: Literal| {
const { assert!(Literal::POLARITY_BIT == 0) };
let i = ((l.0 >> Literal::VAR_LSB) << 1) | (l.0 & (1 << Literal::POLARITY_BIT));
if l.is_gate() { i } else { i + no_gate_add }
};
match kind {
GateKind::And | GateKind::Or => {
for (i, &l) in inputs.iter().enumerate() {
if input_set.contains(map(!l)) {
for &l in &inputs[..i] {
input_set.remove(map(l));
}
gate_map[index] = match kind {
GateKind::And => Literal::FALSE, GateKind::Or => Literal::TRUE, _ => unreachable!(),
};
return Ok(());
}
input_set.insert(map(l));
}
}
GateKind::Xor => {
for &l in &inputs {
let i = map(l);
input_set.toggle(i);
}
}
}
inputs.retain(|&l| {
let i = map(l);
if input_set.contains(i) {
input_set.remove(i);
true
} else {
false
}
});
}
if let [l] = &inputs[..] {
gate_map[index] = *l ^ neg_out;
return Ok(());
}
let new_gate_no = new_gates.len();
new_gates.push_vec(kind as usize);
new_gates.push_elements(inputs.iter().copied());
inputs.sort_unstable();
let l = match unique_map.entry((kind, inputs.into_boxed_slice())) {
Entry::Occupied(e) => {
new_gates.pop_vec();
*e.get()
}
Entry::Vacant(e) => *e.insert(Literal::from_gate(false, new_gate_no)),
};
gate_map[index] = l ^ neg_out;
Ok(())
}
for root in roots {
if let Some(i) = root.get_gate_no() {
inner(
&bump,
&self.gates,
i,
&mut input_set,
&mut unique_map,
&mut new_gates,
&mut gate_map,
)?;
}
}
let new_circuit = Self {
inputs: self.inputs.clone(),
gates: new_gates,
};
Ok((new_circuit, gate_map))
}
}
#[derive(Clone)]
pub struct CircuitGateIter<'a>(
vec2d::with_metadata::Vec2dIter<'a, Literal, { Circuit::GATE_METADATA_BITS }>,
);
impl<'a> Iterator for CircuitGateIter<'a> {
type Item = Gate<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let (kind, inputs) = self.0.next()?;
Some(Gate {
kind: kind.into(),
inputs,
})
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl ExactSizeIterator for CircuitGateIter<'_> {
#[inline(always)]
fn len(&self) -> usize {
self.0.len()
}
}
impl fmt::Debug for CircuitGateIter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.clone()).finish()
}
}
impl fmt::Debug for Circuit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Circuit")
.field("inputs", &self.inputs)
.field("gates", &self.iter_gates())
.finish()
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AIGERDetails {
inputs: usize,
latches: Vec<Literal>,
latch_init_values: TVBitVec,
outputs: Vec<Literal>,
bad: Vec<Literal>,
invariants: Vec<Literal>,
justice: Vec2d<Literal>,
fairness: Vec<Literal>,
map: Vec<Literal>,
output_names: Vec<Option<String>>,
bad_names: Vec<Option<String>>,
invariant_names: Vec<Option<String>>,
justice_names: Vec<Option<String>>,
fairness_names: Vec<Option<String>>,
}
impl AIGERDetails {
#[inline]
pub fn get_latch_no(&self, literal: Literal) -> Option<usize> {
if literal.is_gate() {
return None;
}
let first_latch = 1 + self.inputs;
let result = (literal.0 >> Literal::VAR_LSB).checked_sub(first_latch)?;
if result >= self.latches.len() {
return None;
}
Some(result)
}
#[inline(always)]
pub fn inputs(&self) -> usize {
self.inputs
}
#[inline(always)]
pub fn latches(&self) -> &[Literal] {
&self.latches
}
#[inline(always)]
pub fn latch_init_value(&self, i: usize) -> Option<bool> {
self.latch_init_values.at(i)
}
#[inline(always)]
pub fn outputs(&self) -> &[Literal] {
&self.outputs
}
#[inline(always)]
pub fn map_aiger_literal(&self, literal: usize) -> Option<Literal> {
let l = *self.map.get(literal >> 1)?;
Some(if literal & 1 != 0 { !l } else { l })
}
#[inline(always)]
pub fn output_name(&self, i: usize) -> Option<&str> {
self.output_names.get(i)?.as_deref()
}
#[inline(always)]
pub fn bad_name(&self, i: usize) -> Option<&str> {
self.bad_names.get(i)?.as_deref()
}
#[inline(always)]
pub fn invariant_name(&self, i: usize) -> Option<&str> {
self.invariant_names.get(i)?.as_deref()
}
#[inline(always)]
pub fn justice_name(&self, i: usize) -> Option<&str> {
self.justice_names.get(i)?.as_deref()
}
pub fn apply_gate_map(&self, gate_map: &[Literal]) -> Self {
let map_slice = move |slice: &[Literal]| {
slice
.iter()
.map(move |l| l.apply_gate_map(gate_map))
.collect::<Vec<_>>()
};
let mut justice =
Vec2d::with_capacity(self.justice.len(), self.justice.all_elements().len());
for j in self.justice.iter() {
justice.push_vec();
justice.push_elements(j.iter().map(move |l| l.apply_gate_map(gate_map)));
}
Self {
inputs: self.inputs,
latches: map_slice(&self.latches),
latch_init_values: self.latch_init_values.clone(),
outputs: map_slice(&self.outputs),
bad: map_slice(&self.bad),
invariants: map_slice(&self.invariants),
justice,
fairness: map_slice(&self.fairness),
map: map_slice(&self.map),
output_names: self.output_names.clone(),
bad_names: self.bad_names.clone(),
invariant_names: self.invariant_names.clone(),
justice_names: self.justice_names.clone(),
fairness_names: self.fairness_names.clone(),
}
}
pub fn apply_gate_map_in_place(&mut self, map: &[Literal]) {
let map_slice = move |slice: &mut [Literal]| {
for l in slice.iter_mut() {
*l = l.apply_gate_map(map);
}
};
map_slice(&mut self.latches);
map_slice(&mut self.outputs);
map_slice(&mut self.bad);
map_slice(&mut self.invariants);
map_slice(self.justice.all_elements_mut());
map_slice(&mut self.fairness);
map_slice(&mut self.map);
}
}
#[non_exhaustive]
#[derive(Clone, Builder, Default, Debug)]
pub struct ParseOptions {
#[builder(default = "false")]
pub var_order: bool,
#[builder(default = "false")]
pub clause_tree: bool,
#[builder(default = "true")]
pub check_acyclic: bool,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::util::test::*;
#[test]
fn simplify() {
let mut circuit = Circuit::new(VarSet::new(3));
circuit.push_gate(GateKind::Xor);
circuit.push_gate_inputs([!v(0), v(1), v(2)]);
circuit.push_gate(GateKind::And);
circuit.push_gate_input(g(0));
let (simplified, map) = circuit.simplify([Literal::from_gate(false, 1)]).unwrap();
assert_eq!(simplified.num_gates(), 1);
assert_eq!(
simplified.gate_for_no(0),
Some(Gate::xor(&[v(0), v(1), v(2)]))
);
assert_eq!(&map, &[!g(0), !g(0)]);
}
}