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;
11
12use crate::stat;
13
14mod apply_rec;
17
18#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Countable)]
22#[repr(u8)]
23pub enum EdgeTag {
24 #[default]
26 None,
27 Complemented,
29}
30
31impl std::ops::Not for EdgeTag {
32 type Output = EdgeTag;
33
34 #[inline]
35 fn not(self) -> EdgeTag {
36 match self {
37 EdgeTag::None => EdgeTag::Complemented,
38 EdgeTag::Complemented => EdgeTag::None,
39 }
40 }
41}
42
43impl std::ops::BitXor for EdgeTag {
44 type Output = EdgeTag;
45
46 #[inline]
47 fn bitxor(self, rhs: Self) -> EdgeTag {
48 use EdgeTag::*;
49 match (self, rhs) {
50 (None, None) => None,
51 (None, Complemented) => Complemented,
52 (Complemented, None) => Complemented,
53 (Complemented, Complemented) => None,
54 }
55 }
56}
57
58#[inline]
59#[must_use]
60fn not_owned<E: Edge<Tag = EdgeTag>>(e: E) -> E {
61 let tag = e.tag();
62 e.with_tag_owned(!tag)
63}
64
65#[inline]
66#[must_use]
67fn not<E: Edge<Tag = EdgeTag>>(e: &E) -> Borrowed<'_, E> {
68 let tag = e.tag();
69 e.with_tag(!tag)
70}
71
72pub struct BCDDRules;
76
77impl<E: Edge<Tag = EdgeTag>, N: InnerNode<E>> DiagramRules<E, N, BCDDTerminal> for BCDDRules {
78 type Cofactors<'a>
79 = Cofactors<'a, E, N::ChildrenIter<'a>>
80 where
81 N: 'a,
82 E: 'a;
83
84 #[inline]
85 fn reduce<M: Manager<Edge = E, InnerNode = N>>(
86 manager: &M,
87 level: LevelNo,
88 children: impl IntoIterator<Item = E>,
89 ) -> ReducedOrNew<E, N> {
90 let mut it = children.into_iter();
91 let t = it.next().unwrap();
92 let e = it.next().unwrap();
93 debug_assert!(it.next().is_none());
94
95 if t == e {
96 manager.drop_edge(e);
97 return ReducedOrNew::Reduced(t);
98 }
99
100 let tt = t.tag();
101 if tt == EdgeTag::Complemented {
102 let et = e.tag();
103 let node = N::new(
104 level,
105 [t.with_tag_owned(EdgeTag::None), e.with_tag_owned(!et)],
106 );
107 ReducedOrNew::New(node, EdgeTag::Complemented)
108 } else {
109 let node = N::new(level, [t, e]);
110 ReducedOrNew::New(node, EdgeTag::None)
111 }
112 }
113
114 #[inline]
115 fn cofactors(tag: E::Tag, node: &N) -> Self::Cofactors<'_> {
116 Cofactors {
117 it: node.children(),
118 tag,
119 phantom: PhantomData,
120 }
121 }
122
123 #[inline]
124 fn cofactor(tag: E::Tag, node: &N, n: usize) -> Borrowed<'_, E> {
125 let e = node.child(n);
126 if tag == EdgeTag::None {
127 e
128 } else {
129 let e_tag = e.tag();
130 e.edge_with_tag(!e_tag)
131 }
132 }
133}
134
135pub struct Cofactors<'a, E, I> {
137 it: I,
138 tag: EdgeTag,
139 phantom: PhantomData<Borrowed<'a, E>>,
140}
141
142impl<'a, E: Edge<Tag = EdgeTag> + 'a, I: Iterator<Item = Borrowed<'a, E>>> Iterator
143 for Cofactors<'a, E, I>
144{
145 type Item = Borrowed<'a, E>;
146
147 #[inline]
148 fn next(&mut self) -> Option<Self::Item> {
149 match (self.it.next(), self.tag) {
150 (Some(e), EdgeTag::None) => Some(e),
151 (Some(e), EdgeTag::Complemented) => {
152 let tag = !e.tag();
153 Some(Borrowed::edge_with_tag(e, tag))
154 }
155 (None, _) => None,
156 }
157 }
158
159 #[inline]
160 fn size_hint(&self) -> (usize, Option<usize>) {
161 self.it.size_hint()
162 }
163}
164
165impl<'a, E: Edge<Tag = EdgeTag>, I: FusedIterator<Item = Borrowed<'a, E>>> FusedIterator
166 for Cofactors<'a, E, I>
167{
168}
169
170impl<'a, E: Edge<Tag = EdgeTag>, I: ExactSizeIterator<Item = Borrowed<'a, E>>> ExactSizeIterator
171 for Cofactors<'a, E, I>
172{
173 #[inline]
174 fn len(&self) -> usize {
175 self.it.len()
176 }
177}
178
179#[inline]
181#[must_use]
182fn is_false<M: Manager<EdgeTag = EdgeTag>>(manager: &M, edge: &M::Edge) -> bool {
183 edge.tag() == EdgeTag::Complemented && manager.get_node(edge).is_any_terminal()
184}
185
186#[inline]
189#[must_use]
190fn collect_cofactors<E: Edge<Tag = EdgeTag>, N: InnerNode<E>>(
191 tag: EdgeTag,
192 node: &N,
193) -> (Borrowed<'_, E>, Borrowed<'_, E>) {
194 debug_assert_eq!(N::ARITY, 2);
195 let mut it = BCDDRules::cofactors(tag, node);
196 let ft = it.next().unwrap();
197 let fe = it.next().unwrap();
198 debug_assert!(it.next().is_none());
199 (ft, fe)
200}
201
202#[inline(always)]
204fn reduce<M>(
205 manager: &M,
206 level: LevelNo,
207 t: M::Edge,
208 e: M::Edge,
209 op: BCDDOp,
210) -> AllocResult<M::Edge>
211where
212 M: Manager<Terminal = BCDDTerminal, EdgeTag = EdgeTag>,
213{
214 if t == e {
217 stat!(reduced op);
218 manager.drop_edge(e);
219 return Ok(t);
220 }
221
222 let tt = t.tag();
223 let (node, tag) = if tt == EdgeTag::Complemented {
224 let et = e.tag();
225 let node = M::InnerNode::new(
226 level,
227 [t.with_tag_owned(EdgeTag::None), e.with_tag_owned(!et)],
228 );
229 (node, EdgeTag::Complemented)
230 } else {
231 (M::InnerNode::new(level, [t, e]), EdgeTag::None)
232 };
233
234 Ok(oxidd_core::LevelView::get_or_insert(&mut manager.level(level), node)?.with_tag_owned(tag))
235}
236
237#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Countable, Debug)]
241pub struct BCDDTerminal;
242
243impl oxidd_dump::ParseTagged<EdgeTag> for BCDDTerminal {
244 fn parse(s: &str) -> Option<(Self, EdgeTag)> {
245 let tag = match s {
246 "t" | "T" | "true" | "True" | "TRUE" | "⊤" | "1" => EdgeTag::None,
247 "f" | "F" | "false" | "False" | "FALSE" | "⊥" | "0" => EdgeTag::Complemented,
248 _ => return None,
249 };
250 Some((BCDDTerminal, tag))
251 }
252}
253
254impl oxidd_dump::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 Exists,
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::Exists 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]
443fn add_literal_to_cube<M>(
444 manager: &M,
445 sub: M::Edge,
446 level: LevelNo,
447 positive: bool,
448) -> AllocResult<M::Edge>
449where
450 M: Manager<EdgeTag = EdgeTag, Terminal = BCDDTerminal>,
451 M::InnerNode: HasLevel,
452{
453 let sub = EdgeDropGuard::new(manager, sub);
454 debug_assert!(!is_false(manager, &sub));
455 debug_assert!(manager.get_node(&sub).level() > level);
456
457 let t = manager.get_terminal(BCDDTerminal)?;
458 let sub = sub.into_edge();
459 let (children, tag) = if positive {
460 let tag = sub.tag();
461 if tag == EdgeTag::Complemented {
462 ([sub.with_tag_owned(EdgeTag::None), t], tag)
463 } else {
464 ([sub, t.with_tag_owned(EdgeTag::Complemented)], tag)
465 }
466 } else {
467 ([t, not_owned(sub)], EdgeTag::Complemented)
468 };
469
470 let res = oxidd_core::LevelView::get_or_insert(
471 &mut manager.level(level),
472 M::InnerNode::new(level, children),
473 )?;
474 Ok(res.with_tag_owned(tag))
475}
476
477#[cfg(feature = "multi-threading")]
480pub use apply_rec::mt::BCDDFunctionMT;
481pub use apply_rec::BCDDFunction;