1use std::fmt;
4use std::hash::Hash;
5use std::iter::FusedIterator;
6use std::marker::PhantomData;
7
8use oxidd_core::util::{AllocResult, Borrowed, EdgeDropGuard};
9use oxidd_core::{DiagramRules, Edge, HasLevel, InnerNode, LevelNo, Manager, Node, ReducedOrNew};
10use oxidd_derive::Countable;
11use oxidd_dump::dddmp::AsciiDisplay;
12
13use crate::stat;
14
15mod apply_rec;
18
19#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Countable)]
23#[repr(u8)]
24pub enum EdgeTag {
25 #[default]
27 None,
28 Complemented,
30}
31
32impl std::ops::Not for EdgeTag {
33 type Output = EdgeTag;
34
35 #[inline]
36 fn not(self) -> EdgeTag {
37 match self {
38 EdgeTag::None => EdgeTag::Complemented,
39 EdgeTag::Complemented => EdgeTag::None,
40 }
41 }
42}
43
44impl std::ops::BitXor for EdgeTag {
45 type Output = EdgeTag;
46
47 #[inline]
48 fn bitxor(self, rhs: Self) -> EdgeTag {
49 use EdgeTag::*;
50 match (self, rhs) {
51 (None, None) => None,
52 (None, Complemented) => Complemented,
53 (Complemented, None) => Complemented,
54 (Complemented, Complemented) => None,
55 }
56 }
57}
58
59#[inline]
60#[must_use]
61fn not_owned<E: Edge<Tag = EdgeTag>>(e: E) -> E {
62 let tag = e.tag();
63 e.with_tag_owned(!tag)
64}
65
66#[inline]
67#[must_use]
68fn not<E: Edge<Tag = EdgeTag>>(e: &E) -> Borrowed<E> {
69 let tag = e.tag();
70 e.with_tag(!tag)
71}
72
73pub struct BCDDRules;
77
78impl<E: Edge<Tag = EdgeTag>, N: InnerNode<E>> DiagramRules<E, N, BCDDTerminal> for BCDDRules {
79 type Cofactors<'a>
80 = Cofactors<'a, E, N::ChildrenIter<'a>>
81 where
82 N: 'a,
83 E: 'a;
84
85 #[inline]
86 #[must_use]
87 fn reduce<M: Manager<Edge = E, InnerNode = N>>(
88 manager: &M,
89 level: LevelNo,
90 children: impl IntoIterator<Item = E>,
91 ) -> ReducedOrNew<E, N> {
92 let mut it = children.into_iter();
93 let t = it.next().unwrap();
94 let e = it.next().unwrap();
95 debug_assert!(it.next().is_none());
96
97 if t == e {
98 manager.drop_edge(e);
99 return ReducedOrNew::Reduced(t);
100 }
101
102 let tt = t.tag();
103 if tt == EdgeTag::Complemented {
104 let et = e.tag();
105 let node = N::new(
106 level,
107 [t.with_tag_owned(EdgeTag::None), e.with_tag_owned(!et)],
108 );
109 ReducedOrNew::New(node, EdgeTag::Complemented)
110 } else {
111 let node = N::new(level, [t, e]);
112 ReducedOrNew::New(node, EdgeTag::None)
113 }
114 }
115
116 #[inline]
117 #[must_use]
118 fn cofactors(tag: E::Tag, node: &N) -> Self::Cofactors<'_> {
119 Cofactors {
120 it: node.children(),
121 tag,
122 phantom: PhantomData,
123 }
124 }
125
126 #[inline]
127 fn cofactor(tag: E::Tag, node: &N, n: usize) -> Borrowed<E> {
128 let e = node.child(n);
129 if tag == EdgeTag::None {
130 e
131 } else {
132 let e_tag = e.tag();
133 e.edge_with_tag(!e_tag)
134 }
135 }
136}
137
138pub struct Cofactors<'a, E, I> {
140 it: I,
141 tag: EdgeTag,
142 phantom: PhantomData<Borrowed<'a, E>>,
143}
144
145impl<'a, E: Edge<Tag = EdgeTag> + 'a, I: Iterator<Item = Borrowed<'a, E>>> Iterator
146 for Cofactors<'a, E, I>
147{
148 type Item = Borrowed<'a, E>;
149
150 #[inline]
151 fn next(&mut self) -> Option<Self::Item> {
152 match (self.it.next(), self.tag) {
153 (Some(e), EdgeTag::None) => Some(e),
154 (Some(e), EdgeTag::Complemented) => {
155 let tag = !e.tag();
156 Some(Borrowed::edge_with_tag(e, tag))
157 }
158 (None, _) => None,
159 }
160 }
161
162 #[inline]
163 fn size_hint(&self) -> (usize, Option<usize>) {
164 self.it.size_hint()
165 }
166}
167
168impl<'a, E: Edge<Tag = EdgeTag>, I: FusedIterator<Item = Borrowed<'a, E>>> FusedIterator
169 for Cofactors<'a, E, I>
170{
171}
172
173impl<'a, E: Edge<Tag = EdgeTag>, I: ExactSizeIterator<Item = Borrowed<'a, E>>> ExactSizeIterator
174 for Cofactors<'a, E, I>
175{
176 #[inline]
177 fn len(&self) -> usize {
178 self.it.len()
179 }
180}
181
182#[inline]
184#[must_use]
185fn is_false<M: Manager<EdgeTag = EdgeTag>>(manager: &M, edge: &M::Edge) -> bool {
186 edge.tag() == EdgeTag::Complemented && manager.get_node(edge).is_any_terminal()
187}
188
189#[inline]
192#[must_use]
193fn collect_cofactors<E: Edge<Tag = EdgeTag>, N: InnerNode<E>>(
194 tag: EdgeTag,
195 node: &N,
196) -> (Borrowed<E>, Borrowed<E>) {
197 debug_assert_eq!(N::ARITY, 2);
198 let mut it = BCDDRules::cofactors(tag, node);
199 let ft = it.next().unwrap();
200 let fe = it.next().unwrap();
201 debug_assert!(it.next().is_none());
202 (ft, fe)
203}
204
205#[inline(always)]
207fn reduce<M>(
208 manager: &M,
209 level: LevelNo,
210 t: M::Edge,
211 e: M::Edge,
212 op: BCDDOp,
213) -> AllocResult<M::Edge>
214where
215 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>,
216{
217 if t == e {
220 stat!(reduced op);
221 manager.drop_edge(e);
222 return Ok(t);
223 }
224
225 let tt = t.tag();
226 let (node, tag) = if tt == EdgeTag::Complemented {
227 let et = e.tag();
228 let node = M::InnerNode::new(
229 level,
230 [t.with_tag_owned(EdgeTag::None), e.with_tag_owned(!et)],
231 );
232 (node, EdgeTag::Complemented)
233 } else {
234 (M::InnerNode::new(level, [t, e]), EdgeTag::None)
235 };
236
237 Ok(oxidd_core::LevelView::get_or_insert(&mut manager.level(level), node)?.with_tag_owned(tag))
238}
239
240#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Countable, Debug)]
244pub struct BCDDTerminal;
245
246impl std::str::FromStr for BCDDTerminal {
247 type Err = std::convert::Infallible;
248
249 fn from_str(_s: &str) -> Result<Self, Self::Err> {
250 Ok(BCDDTerminal)
251 }
252}
253
254impl AsciiDisplay for BCDDTerminal {
255 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
256 f.write_str("T")
257 }
258}
259
260impl fmt::Display for BCDDTerminal {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 f.write_str("⊤")
263 }
264}
265
266#[inline]
267#[must_use]
268fn get_terminal<M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>>(
269 manager: &M,
270 val: bool,
271) -> M::Edge {
272 let t = manager.get_terminal(BCDDTerminal).unwrap();
273 if val {
274 t
275 } else {
276 t.with_tag_owned(EdgeTag::Complemented)
277 }
278}
279
280#[inline]
282#[must_use]
283fn terminal_and<'a, M>(
284 manager: &'a M,
285 f: &'a M::Edge,
286 g: &'a M::Edge,
287) -> NodesOrDone<'a, EdgeDropGuard<'a, M>, M::InnerNode>
288where
289 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
290{
291 use EdgeTag::*;
292 use Node::*;
293 use NodesOrDone::*;
294
295 let ft = f.tag();
296 let gt = g.tag();
297 let fu = f.with_tag(None);
298 let gu = g.with_tag(None);
299 if *fu == *gu {
300 if ft == gt {
301 return Done(EdgeDropGuard::new(manager, manager.clone_edge(g)));
302 }
303 return Done(EdgeDropGuard::new(manager, get_terminal(manager, false)));
304 }
305
306 let (h, tag) = match (manager.get_node(f), manager.get_node(g)) {
307 (Inner(fnode), Inner(gnode)) => return Nodes(fnode, gnode),
308 (Inner(_), Terminal(_)) => (f, gt),
309 (Terminal(_), Inner(_)) => (g, ft),
310 (Terminal(_), Terminal(_)) => {
311 let res = get_terminal(manager, ft == None && gt == None);
312 return Done(EdgeDropGuard::new(manager, res));
313 }
314 };
315 let res = if tag == Complemented {
316 get_terminal(manager, false)
317 } else {
318 manager.clone_edge(h)
319 };
320 Done(EdgeDropGuard::new(manager, res))
321}
322
323#[inline]
325#[must_use]
326fn terminal_xor<'a, M>(
327 manager: &'a M,
328 f: &'a M::Edge,
329 g: &'a M::Edge,
330) -> NodesOrDone<'a, EdgeDropGuard<'a, M>, M::InnerNode>
331where
332 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
333{
334 use EdgeTag::*;
335 use Node::*;
336 use NodesOrDone::*;
337
338 let ft = f.tag();
339 let gt = g.tag();
340 let fu = f.with_tag(None);
341 let gu = g.with_tag(None);
342 if *fu == *gu {
343 return Done(EdgeDropGuard::new(manager, get_terminal(manager, ft != gt)));
344 }
345 let (h, tag) = match (manager.get_node(f), manager.get_node(g)) {
346 (Inner(fnode), Inner(gnode)) => return Nodes(fnode, gnode),
347 (Inner(_), Terminal(_)) => (f, gt),
348 (Terminal(_), Inner(_)) => (g, ft),
349 (Terminal(_), Terminal(_)) => {
350 return Done(EdgeDropGuard::new(manager, get_terminal(manager, ft != gt)))
351 }
352 };
353 let h = manager.clone_edge(h);
354 let h = if tag == Complemented { h } else { not_owned(h) };
355 Done(EdgeDropGuard::new(manager, h))
356}
357
358#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Countable, Debug)]
362#[repr(u8)]
363#[allow(missing_docs)]
364pub enum BCDDOp {
365 And,
366 Xor,
367
368 Ite,
370
371 Substitute,
372
373 Restrict,
374 Forall,
376 Exist,
378 Unique,
380
381 ForallAnd,
382 ForallXor,
383 ExistAnd,
384 ExistXor,
385 UniqueAnd,
386 UniqueNand,
391 UniqueXor,
392}
393
394impl BCDDOp {
395 const fn from_apply_quant(q: u8, op: u8) -> Self {
396 if q == BCDDOp::Forall as u8 {
397 match () {
398 _ if op == BCDDOp::And as u8 => BCDDOp::ForallAnd,
399 _ if op == BCDDOp::Xor as u8 => BCDDOp::ForallXor,
400 _ => panic!("invalid OP"),
401 }
402 } else if q == BCDDOp::Exist as u8 {
403 match () {
404 _ if op == BCDDOp::And as u8 => BCDDOp::ExistAnd,
405 _ if op == BCDDOp::Xor as u8 => BCDDOp::ExistXor,
406 _ => panic!("invalid OP"),
407 }
408 } else if q == BCDDOp::Unique as u8 {
409 match () {
410 _ if op == BCDDOp::And as u8 => BCDDOp::UniqueAnd,
411 _ if op == BCDDOp::UniqueNand as u8 => BCDDOp::UniqueNand,
412 _ if op == BCDDOp::Xor as u8 => BCDDOp::UniqueXor,
413 _ => panic!("invalid OP"),
414 }
415 } else {
416 panic!("invalid quantifier");
417 }
418 }
419}
420
421enum NodesOrDone<'a, E, N> {
422 Nodes(&'a N, &'a N),
423 Done(E),
424}
425
426#[cfg(feature = "statistics")]
427static STAT_COUNTERS: [crate::StatCounters; <BCDDOp as oxidd_core::Countable>::MAX_VALUE + 1] =
428 [crate::StatCounters::INIT; <BCDDOp as oxidd_core::Countable>::MAX_VALUE + 1];
429
430#[cfg(feature = "statistics")]
431pub fn print_stats() {
433 eprintln!("[oxidd_rules_bdd::complement_edge]");
434 crate::StatCounters::print::<BCDDOp>(&STAT_COUNTERS);
435}
436
437#[inline]
440fn is_var<M>(manager: &M, node: &M::InnerNode) -> bool
441where
442 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>,
443{
444 let t = node.child(0);
445 let e = node.child(1);
446 t.tag() == EdgeTag::None
447 && e.tag() == EdgeTag::Complemented
448 && manager.get_node(&t).is_any_terminal()
449 && manager.get_node(&e).is_any_terminal()
450}
451
452#[inline]
453#[track_caller]
454fn var_level<M>(manager: &M, e: Borrowed<M::Edge>) -> LevelNo
455where
456 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>,
457 M::InnerNode: HasLevel,
458{
459 let node = manager
460 .get_node(&e)
461 .expect_inner("Expected a variable but got a terminal node");
462 debug_assert!(is_var(manager, node));
463 node.level()
464}
465
466#[inline]
470fn add_literal_to_cube<M>(
471 manager: &M,
472 sub: M::Edge,
473 level: LevelNo,
474 positive: bool,
475) -> AllocResult<M::Edge>
476where
477 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
478 M::InnerNode: HasLevel,
479{
480 let sub = EdgeDropGuard::new(manager, sub);
481 debug_assert!(!is_false(manager, &sub));
482 debug_assert!(manager.get_node(&sub).level() > level);
483
484 let t = manager.get_terminal(BCDDTerminal)?;
485 let sub = sub.into_edge();
486 let (children, tag) = if positive {
487 let tag = sub.tag();
488 if tag == EdgeTag::Complemented {
489 ([sub.with_tag_owned(EdgeTag::None), t], tag)
490 } else {
491 ([sub, t.with_tag_owned(EdgeTag::Complemented)], tag)
492 }
493 } else {
494 ([t, not_owned(sub)], EdgeTag::Complemented)
495 };
496
497 let res = oxidd_core::LevelView::get_or_insert(
498 &mut manager.level(level),
499 M::InnerNode::new(level, children),
500 )?;
501 Ok(res.with_tag_owned(tag))
502}
503
504#[cfg(feature = "multi-threading")]
507pub use apply_rec::mt::BCDDFunctionMT;
508pub use apply_rec::BCDDFunction;