use std::{collections::HashSet, rc::Rc};
use super::literal::Literal;
#[derive(Clone, Debug)]
pub struct Gate(Rc<Operation>);
#[derive(Clone, Debug)]
pub enum Operation {
Variable(Rc<String>),
Constant(bool),
Negation(Gate),
Conjunction(Gate, Gate),
Disjunction(Gate, Gate),
Xor(Gate, Gate),
}
impl From<&str> for Gate {
fn from(name: &str) -> Gate {
name.to_string().into()
}
}
impl From<String> for Gate {
fn from(name: String) -> Gate {
Gate(Rc::new(Operation::Variable(Rc::new(name))))
}
}
impl From<bool> for Gate {
fn from(value: bool) -> Gate {
Gate(Rc::new(Operation::Constant(value)))
}
}
impl From<&Literal> for Gate {
fn from(literal: &Literal) -> Gate {
let v = literal.var().into();
if literal.is_negated() {
!&v
} else {
v
}
}
}
impl Gate {
pub fn id(&self) -> usize {
Rc::as_ptr(&self.0) as usize
}
pub fn operation(&self) -> &Operation {
&self.0
}
pub fn iter(&self) -> impl Iterator<Item = &Gate> {
self.into_iter()
}
pub fn post_visit_iter(&self) -> impl Iterator<Item = &Gate> {
PostVisitIterator::new(std::iter::once(self))
}
pub fn to_string_as_tree(&self) -> String {
match self.operation() {
Operation::Variable(name) => format!("{name}"),
Operation::Constant(value) => format!("{value}"),
Operation::Conjunction(left, right) => {
format!(
"({} & {})",
left.to_string_as_tree(),
right.to_string_as_tree()
)
}
Operation::Disjunction(left, right) => {
format!(
"({} | {})",
left.to_string_as_tree(),
right.to_string_as_tree()
)
}
Operation::Xor(left, right) => format!(
"({} ^ {})",
left.to_string_as_tree(),
right.to_string_as_tree()
),
Operation::Negation(gate) => format!("!{}", gate.to_string_as_tree()),
}
}
pub fn try_to_constant(&self) -> Option<bool> {
match self.operation() {
Operation::Constant(value) => Some(*value),
_ => None,
}
}
pub fn gate_count(&self) -> (usize, usize) {
let mut vars: HashSet<_> = Default::default();
let mut gate_count = 0;
for n in self {
match n.operation() {
Operation::Variable(v) => {
vars.insert(v.as_str());
}
Operation::Constant(_) => {}
Operation::Negation(..)
| Operation::Conjunction(..)
| Operation::Disjunction(..)
| Operation::Xor(..) => {
gate_count += 1;
}
}
}
(vars.len(), gate_count)
}
}
impl std::ops::BitAnd for Gate {
type Output = Gate;
fn bitand(self, other: Gate) -> Gate {
Gate(Rc::new(Operation::Conjunction(self, other)))
}
}
impl std::ops::BitAnd for &Gate {
type Output = Gate;
fn bitand(self, other: &Gate) -> Gate {
Gate::bitand(self.clone(), other.clone())
}
}
impl std::ops::BitOr for Gate {
type Output = Gate;
fn bitor(self, other: Gate) -> Gate {
Gate(Rc::new(Operation::Disjunction(self, other)))
}
}
impl std::ops::BitOr for &Gate {
type Output = Gate;
fn bitor(self, other: &Gate) -> Gate {
Gate::bitor(self.clone(), other.clone())
}
}
impl std::ops::BitXor for Gate {
type Output = Gate;
fn bitxor(self, other: Gate) -> Gate {
Gate(Rc::new(Operation::Xor(self, other)))
}
}
impl std::ops::BitXor for &Gate {
type Output = Gate;
fn bitxor(self, other: &Gate) -> Gate {
Gate::bitxor(self.clone(), other.clone())
}
}
impl std::ops::Not for Gate {
type Output = Gate;
fn not(self) -> Gate {
Gate(Rc::new(Operation::Negation(self)))
}
}
impl std::ops::Not for &Gate {
type Output = Gate;
fn not(self) -> Gate {
Gate::not(self.clone())
}
}
impl<'a> IntoIterator for &'a Gate {
type Item = &'a Gate;
type IntoIter = GraphIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
GraphIterator::new(std::iter::once(self))
}
}
pub struct GraphIterator<'a> {
visited: HashSet<usize>,
stack: Vec<&'a Gate>,
}
impl<'a> GraphIterator<'a> {
pub(crate) fn new(gates: impl IntoIterator<Item = &'a Gate>) -> Self {
Self {
visited: HashSet::new(),
stack: gates.into_iter().collect(),
}
}
fn add_predecessors(&mut self, gate: &'a Gate) {
match gate.operation() {
Operation::Variable(_) | Operation::Constant(_) => {}
Operation::Conjunction(left, right)
| Operation::Disjunction(left, right)
| Operation::Xor(left, right) => {
self.stack.push(right);
self.stack.push(left);
}
Operation::Negation(inner) => {
self.stack.push(inner);
}
}
}
}
impl<'a> Iterator for GraphIterator<'a> {
type Item = &'a Gate;
fn next(&mut self) -> Option<Self::Item> {
while let Some(gate) = self.stack.pop() {
if self.visited.insert(gate.id()) {
self.add_predecessors(gate);
return Some(gate);
}
}
None
}
}
pub(crate) struct PostVisitIterator<'a> {
visited: HashSet<usize>,
stack: Vec<(&'a Gate, bool)>,
}
impl<'a> PostVisitIterator<'a> {
pub(crate) fn new(gates: impl IntoIterator<Item = &'a Gate>) -> Self {
Self {
visited: HashSet::new(),
stack: gates.into_iter().map(|n| (n, false)).collect(),
}
}
}
impl<'a> Iterator for PostVisitIterator<'a> {
type Item = &'a Gate;
fn next(&mut self) -> Option<Self::Item> {
while let Some((gate, visited_predecessors)) = self.stack.pop() {
if visited_predecessors {
return Some(gate);
} else if self.visited.insert(gate.id()) {
self.stack.push((gate, true));
match gate.operation() {
Operation::Variable(_) | Operation::Constant(_) => {}
Operation::Conjunction(left, right)
| Operation::Disjunction(left, right)
| Operation::Xor(left, right) => {
self.stack.push((right, false));
self.stack.push((left, false));
}
Operation::Negation(inner) => {
self.stack.push((inner, false));
}
}
}
}
None
}
}