oxidd_core/
function.rs

1//! Function traits
2
3use std::fmt::Display;
4use std::hash::{BuildHasher, Hash};
5
6use nanorand::Rng;
7
8use crate::util::num::F64;
9use crate::util::{
10    AllocResult, Borrowed, EdgeDropGuard, NodeSet, OptBool, SatCountCache, SatCountNumber,
11    Substitution,
12};
13use crate::{DiagramRules, Edge, InnerNode, LevelNo, Manager, ManagerRef, Node};
14
15/// Shorthand to get the [`Edge`] type associated with a [`Function`]
16pub type EdgeOfFunc<'id, F> = <<F as Function>::Manager<'id> as Manager>::Edge;
17/// Shorthand to get the edge tag type associated with a [`Function`]
18pub type ETagOfFunc<'id, F> = <<F as Function>::Manager<'id> as Manager>::EdgeTag;
19/// Shorthand to get the [`InnerNode`] type associated with a [`Function`]
20pub type INodeOfFunc<'id, F> = <<F as Function>::Manager<'id> as Manager>::InnerNode;
21
22/// Function in a decision diagram
23///
24/// A function is some kind of external reference to a node as opposed to
25/// [`Edge`]s, which are diagram internal. A function also includes a reference
26/// to the diagram's manager. So one may view a function as an [`Edge`] plus a
27/// [`ManagerRef`].
28///
29/// Functions are what the library's user mostly works with. There may be
30/// subtraits such as `BooleanFunction` in the `oxidd-rules-bdd` crate which
31/// provide more functionality, in this case applying connectives of boolean
32/// logic to other functions.
33///
34/// For some methods of this trait, there are notes on locking behavior. In a
35/// concurrent setting, a manager has some kind of read/write lock, and
36/// [`Self::with_manager_shared()`] / [`Self::with_manager_exclusive()`] acquire
37/// this lock accordingly. In a sequential implementation, a
38/// [`RefCell`][std::cell::RefCell] or the like may be used instead of lock.
39///
40/// # Safety
41///
42/// An implementation must ensure that the "[`Edge`] part" of the function
43/// points to a node that is stored in the manager referenced  by the
44/// "[`ManagerRef`] part" of the function. All functions of this trait must
45/// maintain this link accordingly. In particular, [`Self::as_edge()`] and
46/// [`Self::into_edge()`] must panic as specified there.
47pub unsafe trait Function: Clone + Ord + Hash {
48    /// Type of the associated manager
49    ///
50    /// This type is generic over a lifetime `'id` to permit the "lifetime
51    /// trick" used, e.g., in [`GhostCell`][GhostCell]: The idea is to make the
52    /// [`Manager`], [`Edge`] and [`InnerNode`] types [invariant][variance] over
53    /// `'id`. Any call to one of the
54    /// [`with_manager_shared()`][Function::with_manager_shared] /
55    /// [`with_manager_exclusive()`][Function::with_manager_exclusive] functions
56    /// of the [`Function`] or [`ManagerRef`] trait, which "generate" a fresh
57    /// lifetime `'id`. Now the type system ensures that every edge or node with
58    /// `'id` comes belongs to the manager from the `with_manager_*()` call.
59    /// This means that we can reduce the amount of runtime checks needed to
60    /// uphold the invariant that the children of a node stored in [`Manager`] M
61    /// are stored in M as well.
62    ///
63    /// Note that [`Function`] and [`ManagerRef`] are (typically) outside the
64    /// scope of this lifetime trick to make the library more flexible.
65    ///
66    /// [GhostCell]: https://plv.mpi-sws.org/rustbelt/ghostcell/
67    /// [variance]: https://doc.rust-lang.org/reference/subtyping.html
68    type Manager<'id>: Manager;
69
70    /// [Manager references][ManagerRef] for [`Self::Manager`]
71    type ManagerRef: for<'id> ManagerRef<Manager<'id> = Self::Manager<'id>>;
72
73    /// Create a new function from a manager reference and an edge
74    fn from_edge<'id>(manager: &Self::Manager<'id>, edge: EdgeOfFunc<'id, Self>) -> Self;
75
76    /// Create a new function from a manager reference and an edge reference
77    #[inline(always)]
78    fn from_edge_ref<'id>(manager: &Self::Manager<'id>, edge: &EdgeOfFunc<'id, Self>) -> Self {
79        Self::from_edge(manager, manager.clone_edge(edge))
80    }
81
82    /// Converts this function into the underlying edge (as reference), checking
83    /// that it belongs to the given `manager`
84    ///
85    /// Panics if the function does not belong to `manager`.
86    fn as_edge<'id>(&self, manager: &Self::Manager<'id>) -> &EdgeOfFunc<'id, Self>;
87
88    /// Converts this function into the underlying edge, checking that it
89    /// belongs to the given `manager`
90    ///
91    /// Panics if the function does not belong to `manager`.
92    fn into_edge<'id>(self, manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
93
94    /// Clone the [`ManagerRef`] part
95    fn manager_ref(&self) -> Self::ManagerRef;
96
97    /// Obtain a shared manager reference as well as the underlying edge
98    ///
99    /// Locking behavior: acquires the manager's lock for shared access.
100    ///
101    /// # Example
102    ///
103    /// ```
104    /// # use oxidd_core::function::Function;
105    /// fn my_eq<F: Function>(f: &F, g: &F) -> bool {
106    ///     f.with_manager_shared(|manager, f_edge| {
107    ///         // Do something meaningful with `manager` and `edge` (the following
108    ///         // is better done using `f == g` without `with_manager_shared()`)
109    ///         let g_edge = g.as_edge(manager);
110    ///         f_edge == g_edge
111    ///     })
112    /// }
113    /// ```
114    fn with_manager_shared<F, T>(&self, f: F) -> T
115    where
116        F: for<'id> FnOnce(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>) -> T;
117
118    /// Obtain an exclusive manager reference as well as the underlying edge
119    ///
120    /// Locking behavior: acquires the manager's lock for exclusive access.
121    ///
122    /// # Example
123    ///
124    /// ```
125    /// # use oxidd_core::{*, function::Function, util::AllocResult};
126    /// /// Adds a binary node on a new level with children `f` and `g`
127    /// fn foo<F: Function>(f: &F, g: &F) -> AllocResult<F> {
128    ///     f.with_manager_exclusive(|manager, f_edge| {
129    ///         let fe = manager.clone_edge(f_edge);
130    ///         let ge = manager.clone_edge(g.as_edge(manager));
131    ///         let he = manager.add_level(|level| InnerNode::new(level, [fe, ge]))?;
132    ///         Ok(F::from_edge(manager, he))
133    ///     })
134    /// }
135    /// ```
136    fn with_manager_exclusive<F, T>(&self, f: F) -> T
137    where
138        F: for<'id> FnOnce(&mut Self::Manager<'id>, &EdgeOfFunc<'id, Self>) -> T;
139
140    /// Count the number of nodes in this function, including terminal nodes
141    ///
142    /// Locking behavior: acquires the manager's lock for shared access.
143    fn node_count(&self) -> usize {
144        fn inner<M: Manager>(manager: &M, e: &M::Edge, set: &mut M::NodeSet) {
145            if set.insert(e) {
146                if let Node::Inner(node) = manager.get_node(e) {
147                    for e in node.children() {
148                        inner(manager, &*e, set)
149                    }
150                }
151            }
152        }
153
154        self.with_manager_shared(|manager, edge| {
155            let mut set = Default::default();
156            inner(manager, edge, &mut set);
157            set.len()
158        })
159    }
160}
161
162/// Substitution extension for [`Function`]
163pub trait FunctionSubst: Function {
164    /// Substitute variables in `self` according to `substitution`
165    ///
166    /// The substitution is performed in a parallel fashion, e.g.:
167    /// `(¬x ∧ ¬y)[x ↦ ¬x ∧ ¬y, y ↦ ⊥] = ¬(¬x ∧ ¬y) ∧ ¬⊥ = x ∨ y`
168    ///
169    /// Locking behavior: acquires the manager's lock for shared access.
170    ///
171    /// Panics if `self` and the function in `substitution` don't belong to the
172    /// same manager.
173    fn substitute<'a>(
174        &'a self,
175        substitution: impl Substitution<Var = &'a Self, Replacement = &'a Self>,
176    ) -> AllocResult<Self> {
177        if substitution.pairs().len() == 0 {
178            return Ok(self.clone());
179        }
180        self.with_manager_shared(|manager, edge| {
181            Ok(Self::from_edge(
182                manager,
183                Self::substitute_edge(
184                    manager,
185                    edge,
186                    substitution.map(|(v, r)| {
187                        (v.as_edge(manager).borrowed(), r.as_edge(manager).borrowed())
188                    }),
189                )?,
190            ))
191        })
192    }
193
194    /// `Edge` version of [`Self::substitute()`]
195    #[must_use]
196    fn substitute_edge<'id, 'a>(
197        manager: &'a Self::Manager<'id>,
198        edge: &'a EdgeOfFunc<'id, Self>,
199        substitution: impl Substitution<
200            Var = Borrowed<'a, EdgeOfFunc<'id, Self>>,
201            Replacement = Borrowed<'a, EdgeOfFunc<'id, Self>>,
202        >,
203    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
204}
205
206/// Boolean functions 𝔹ⁿ → 𝔹
207///
208/// As a user of this trait, you are probably most interested in methods like
209/// [`Self::not()`], [`Self::and()`], and [`Self::or()`]. As an implementor, it
210/// suffices to implement the functions operating on edges.
211pub trait BooleanFunction: Function {
212    /// Get the always false function `⊥`
213    fn f<'id>(manager: &Self::Manager<'id>) -> Self {
214        Self::from_edge(manager, Self::f_edge(manager))
215    }
216    /// Get the always true function `⊤`
217    fn t<'id>(manager: &Self::Manager<'id>) -> Self {
218        Self::from_edge(manager, Self::t_edge(manager))
219    }
220
221    /// Get a fresh variable, i.e., a function that is true if and only if the
222    /// variable is true. This adds a new level to a decision diagram.
223    fn new_var<'id>(manager: &mut Self::Manager<'id>) -> AllocResult<Self>;
224
225    /// Get the cofactors `(f_true, f_false)` of `self`
226    ///
227    /// Let f(x₀, …, xₙ) be represented by `self`, where x₀ is (currently) the
228    /// top-most variable. Then f<sub>true</sub>(x₁, …, xₙ) = f(⊤, x₁, …, xₙ)
229    /// and f<sub>false</sub>(x₁, …, xₙ) = f(⊥, x₁, …, xₙ).
230    ///
231    /// Note that the domain of f is 𝔹ⁿ⁺¹ while the domain of f<sub>true</sub>
232    /// and f<sub>false</sub> is 𝔹ⁿ. This is irrelevant in case of BDDs and
233    /// BCDDs, but not for ZBDDs: For instance, g(x₀) = x₀ and g'(x₀, x₁) = x₀
234    /// have the same representation as BDDs or BCDDs, but different
235    /// representations as ZBDDs.
236    ///
237    /// Structurally, the cofactors are simply the children in case of BDDs and
238    /// ZBDDs. (For BCDDs, the edge tags are adjusted accordingly.) On these
239    /// representations, runtime is thus in O(1).
240    ///
241    /// Returns `None` iff `self` references a terminal node. If you only need
242    /// `f_true` or `f_false`, [`Self::cofactor_true`] or
243    /// [`Self::cofactor_false`] are slightly more efficient.
244    ///
245    /// Locking behavior: acquires the manager's lock for shared access.
246    fn cofactors(&self) -> Option<(Self, Self)> {
247        self.with_manager_shared(|manager, f| {
248            let (ft, ff) = Self::cofactors_edge(manager, f)?;
249            Some((
250                Self::from_edge_ref(manager, &ft),
251                Self::from_edge_ref(manager, &ff),
252            ))
253        })
254    }
255
256    /// Get the cofactor `f_true` of `self`
257    ///
258    /// This method is slightly more efficient than [`Self::cofactors`] in case
259    /// `f_false` is not needed.
260    ///
261    /// For a more detailed description, see [`Self::cofactors`].
262    ///
263    /// Returns `None` iff `self` references a terminal node.
264    ///
265    /// Locking behavior: acquires the manager's lock for shared access.
266    fn cofactor_true(&self) -> Option<Self> {
267        self.with_manager_shared(|manager, f| {
268            let (ft, _) = Self::cofactors_edge(manager, f)?;
269            Some(Self::from_edge_ref(manager, &ft))
270        })
271    }
272
273    /// Get the cofactor `f_false` of `self`
274    ///
275    /// This method is slightly more efficient than [`Self::cofactors`] in case
276    /// `f_true` is not needed.
277    ///
278    /// For a more detailed description, see [`Self::cofactors`].
279    ///
280    /// Returns `None` iff `self` references a terminal node.
281    ///
282    /// Locking behavior: acquires the manager's lock for shared access.
283    fn cofactor_false(&self) -> Option<Self> {
284        self.with_manager_shared(|manager, f| {
285            let (_, ff) = Self::cofactors_edge(manager, f)?;
286            Some(Self::from_edge_ref(manager, &ff))
287        })
288    }
289
290    /// Compute the negation `¬self`
291    ///
292    /// Locking behavior: acquires the manager's lock for shared access.
293    fn not(&self) -> AllocResult<Self> {
294        self.with_manager_shared(|manager, edge| {
295            Ok(Self::from_edge(manager, Self::not_edge(manager, edge)?))
296        })
297    }
298    /// Compute the negation `¬self`, owned version
299    ///
300    /// Compared to [`Self::not()`], this method does not need to clone the
301    /// function, so when the implementation is using (e.g.) complemented edges,
302    /// this might be a little bit faster than [`Self::not()`].
303    ///
304    /// Locking behavior: acquires the manager's lock for shared access.
305    fn not_owned(self) -> AllocResult<Self> {
306        self.not()
307    }
308    /// Compute the conjunction `self ∧ rhs`
309    ///
310    /// Locking behavior: acquires the manager's lock for shared access.
311    ///
312    /// Panics if `self` and `rhs` don't belong to the same manager.
313    fn and(&self, rhs: &Self) -> AllocResult<Self> {
314        self.with_manager_shared(|manager, lhs| {
315            let e = Self::and_edge(manager, lhs, rhs.as_edge(manager))?;
316            Ok(Self::from_edge(manager, e))
317        })
318    }
319    /// Compute the disjunction `self ∨ rhs`
320    ///
321    /// Locking behavior: acquires the manager's lock for shared access.
322    ///
323    /// Panics if `self` and `rhs` don't belong to the same manager.
324    fn or(&self, rhs: &Self) -> AllocResult<Self> {
325        self.with_manager_shared(|manager, lhs| {
326            let e = Self::or_edge(manager, lhs, rhs.as_edge(manager))?;
327            Ok(Self::from_edge(manager, e))
328        })
329    }
330    /// Compute the negated conjunction `self ⊼ rhs`
331    ///
332    /// Locking behavior: acquires the manager's lock for shared access.
333    ///
334    /// Panics if `self` and `rhs` don't belong to the same manager.
335    fn nand(&self, rhs: &Self) -> AllocResult<Self> {
336        self.with_manager_shared(|manager, lhs| {
337            let e = Self::nand_edge(manager, lhs, rhs.as_edge(manager))?;
338            Ok(Self::from_edge(manager, e))
339        })
340    }
341    /// Compute the negated disjunction `self ⊽ rhs`
342    ///
343    /// Locking behavior: acquires the manager's lock for shared access.
344    ///
345    /// Panics if `self` and `rhs` don't belong to the same manager.
346    fn nor(&self, rhs: &Self) -> AllocResult<Self> {
347        self.with_manager_shared(|manager, lhs| {
348            let e = Self::nor_edge(manager, lhs, rhs.as_edge(manager))?;
349            Ok(Self::from_edge(manager, e))
350        })
351    }
352    /// Compute the exclusive disjunction `self ⊕ rhs`
353    ///
354    /// Locking behavior: acquires the manager's lock for shared access.
355    ///
356    /// Panics if `self` and `rhs` don't belong to the same manager.
357    fn xor(&self, rhs: &Self) -> AllocResult<Self> {
358        self.with_manager_shared(|manager, lhs| {
359            let e = Self::xor_edge(manager, lhs, rhs.as_edge(manager))?;
360            Ok(Self::from_edge(manager, e))
361        })
362    }
363    /// Compute the equivalence `self ↔ rhs`
364    ///
365    /// Locking behavior: acquires the manager's lock for shared access.
366    ///
367    /// Panics if `self` and `rhs` don't belong to the same manager.
368    fn equiv(&self, rhs: &Self) -> AllocResult<Self> {
369        self.with_manager_shared(|manager, lhs| {
370            let e = Self::equiv_edge(manager, lhs, rhs.as_edge(manager))?;
371            Ok(Self::from_edge(manager, e))
372        })
373    }
374    /// Compute the implication `self → rhs` (or `self ≤ rhs`)
375    ///
376    /// Locking behavior: acquires the manager's lock for shared access.
377    ///
378    /// Panics if `self` and `rhs` don't belong to the same manager.
379    fn imp(&self, rhs: &Self) -> AllocResult<Self> {
380        self.with_manager_shared(|manager, lhs| {
381            let e = Self::imp_edge(manager, lhs, rhs.as_edge(manager))?;
382            Ok(Self::from_edge(manager, e))
383        })
384    }
385    /// Compute the strict implication `self < rhs`
386    ///
387    /// Locking behavior: acquires the manager's lock for shared access.
388    ///
389    /// Panics if `self` and `rhs` don't belong to the same manager.
390    fn imp_strict(&self, rhs: &Self) -> AllocResult<Self> {
391        self.with_manager_shared(|manager, lhs| {
392            let e = Self::imp_strict_edge(manager, lhs, rhs.as_edge(manager))?;
393            Ok(Self::from_edge(manager, e))
394        })
395    }
396
397    /// Get the always false function `⊥` as edge
398    fn f_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
399    /// Get the always true function `⊤` as edge
400    fn t_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
401
402    /// Get the cofactors `(f_true, f_false)` of `f`, edge version
403    ///
404    /// Returns `None` iff `f` references a terminal node. For more details on
405    /// the semantics of `f_true` and `f_false`, see [`Self::cofactors`].
406    #[inline]
407    #[allow(clippy::type_complexity)]
408    fn cofactors_edge<'a, 'id>(
409        manager: &'a Self::Manager<'id>,
410        f: &'a EdgeOfFunc<'id, Self>,
411    ) -> Option<(
412        Borrowed<'a, EdgeOfFunc<'id, Self>>,
413        Borrowed<'a, EdgeOfFunc<'id, Self>>,
414    )> {
415        if let Node::Inner(node) = manager.get_node(f) {
416            Some(Self::cofactors_node(f.tag(), node))
417        } else {
418            None
419        }
420    }
421
422    /// Get the cofactors `(f_true, f_false)` of `node`, assuming an incoming
423    /// edge with `EdgeTag`
424    ///
425    /// Returns `None` iff `f` references a terminal node. For more details on
426    /// the semantics of `f_true` and `f_false`, see [`Self::cofactors`].
427    ///
428    /// Implementation note: The default implementation assumes that
429    /// [cofactor 0][DiagramRules::cofactor] corresponds to `f_true` and
430    /// [cofactor 1][DiagramRules::cofactor] corresponds to `f_false`.
431    #[inline]
432    fn cofactors_node<'a, 'id>(
433        tag: ETagOfFunc<'id, Self>,
434        node: &'a INodeOfFunc<'id, Self>,
435    ) -> (
436        Borrowed<'a, EdgeOfFunc<'id, Self>>,
437        Borrowed<'a, EdgeOfFunc<'id, Self>>,
438    ) {
439        let cofactor = <<Self::Manager<'id> as Manager>::Rules as DiagramRules<_, _, _>>::cofactor;
440        (cofactor(tag, node, 0), cofactor(tag, node, 1))
441    }
442
443    /// Compute the negation `¬edge`, edge version
444    #[must_use]
445    fn not_edge<'id>(
446        manager: &Self::Manager<'id>,
447        edge: &EdgeOfFunc<'id, Self>,
448    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
449
450    /// Compute the negation `¬edge`, owned edge version
451    ///
452    /// Compared to [`Self::not_edge()`], this method does not need to clone the
453    /// edge, so when the implementation is using (e.g.) complemented edges,
454    /// this might be a little bit faster than [`Self::not_edge()`].
455    #[must_use]
456    fn not_edge_owned<'id>(
457        manager: &Self::Manager<'id>,
458        edge: EdgeOfFunc<'id, Self>,
459    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
460        Self::not_edge(manager, &edge)
461    }
462
463    /// Compute the conjunction `lhs ∧ rhs`, edge version
464    #[must_use]
465    fn and_edge<'id>(
466        manager: &Self::Manager<'id>,
467        lhs: &EdgeOfFunc<'id, Self>,
468        rhs: &EdgeOfFunc<'id, Self>,
469    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
470    /// Compute the disjunction `lhs ∨ rhs`, edge version
471    #[must_use]
472    fn or_edge<'id>(
473        manager: &Self::Manager<'id>,
474        lhs: &EdgeOfFunc<'id, Self>,
475        rhs: &EdgeOfFunc<'id, Self>,
476    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
477    /// Compute the negated conjunction `lhs ⊼ rhs`, edge version
478    #[must_use]
479    fn nand_edge<'id>(
480        manager: &Self::Manager<'id>,
481        lhs: &EdgeOfFunc<'id, Self>,
482        rhs: &EdgeOfFunc<'id, Self>,
483    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
484    /// Compute the negated disjunction `lhs ⊽ rhs`, edge version
485    #[must_use]
486    fn nor_edge<'id>(
487        manager: &Self::Manager<'id>,
488        lhs: &EdgeOfFunc<'id, Self>,
489        rhs: &EdgeOfFunc<'id, Self>,
490    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
491    /// Compute the exclusive disjunction `lhs ⊕ rhs`, edge version
492    #[must_use]
493    fn xor_edge<'id>(
494        manager: &Self::Manager<'id>,
495        lhs: &EdgeOfFunc<'id, Self>,
496        rhs: &EdgeOfFunc<'id, Self>,
497    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
498    /// Compute the equivalence `lhs ↔ rhs`, edge version
499    #[must_use]
500    fn equiv_edge<'id>(
501        manager: &Self::Manager<'id>,
502        lhs: &EdgeOfFunc<'id, Self>,
503        rhs: &EdgeOfFunc<'id, Self>,
504    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
505    /// Compute the implication `lhs → rhs`, edge version
506    #[must_use]
507    fn imp_edge<'id>(
508        manager: &Self::Manager<'id>,
509        lhs: &EdgeOfFunc<'id, Self>,
510        rhs: &EdgeOfFunc<'id, Self>,
511    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
512    /// Compute the strict implication `lhs < rhs`, edge version
513    #[must_use]
514    fn imp_strict_edge<'id>(
515        manager: &Self::Manager<'id>,
516        lhs: &EdgeOfFunc<'id, Self>,
517        rhs: &EdgeOfFunc<'id, Self>,
518    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
519
520    /// Returns `true` iff `self` is satisfiable, i.e. is not `⊥`
521    ///
522    /// Locking behavior: acquires the manager's lock for shared access.
523    fn satisfiable(&self) -> bool {
524        self.with_manager_shared(|manager, edge| {
525            let f = EdgeDropGuard::new(manager, Self::f_edge(manager));
526            edge != &*f
527        })
528    }
529
530    /// Returns `true` iff `self` is valid, i.e. is `⊤`
531    ///
532    /// Locking behavior: acquires the manager's lock for shared access.
533    fn valid(&self) -> bool {
534        self.with_manager_shared(|manager, edge| {
535            let t = EdgeDropGuard::new(manager, Self::t_edge(manager));
536            edge == &*t
537        })
538    }
539
540    /// Compute `if self { then_case } else { else_case }`
541    ///
542    /// This is equivalent to `(self ∧ then_case) ∨ (¬self ∧ else_case)` but
543    /// possibly more efficient than computing all the
544    /// conjunctions/disjunctions.
545    ///
546    /// Locking behavior: acquires the manager's lock for shared access.
547    ///
548    /// Panics if `self`, `then_case`, and `else_case` don't belong to the same
549    /// manager.
550    fn ite(&self, then_case: &Self, else_case: &Self) -> AllocResult<Self> {
551        self.with_manager_shared(|manager, if_edge| {
552            let then_edge = then_case.as_edge(manager);
553            let else_edge = else_case.as_edge(manager);
554            let res = Self::ite_edge(manager, if_edge, then_edge, else_edge)?;
555            Ok(Self::from_edge(manager, res))
556        })
557    }
558
559    /// Compute `if if_edge { then_edge } else { else_edge }` (edge version)
560    ///
561    /// This is equivalent to `(self ∧ then_case) ∨ (¬self ∧ else_case)` but
562    /// possibly more efficient than computing all the
563    /// conjunctions/disjunctions.
564    #[must_use]
565    fn ite_edge<'id>(
566        manager: &Self::Manager<'id>,
567        if_edge: &EdgeOfFunc<'id, Self>,
568        then_edge: &EdgeOfFunc<'id, Self>,
569        else_edge: &EdgeOfFunc<'id, Self>,
570    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
571        let f = EdgeDropGuard::new(manager, Self::and_edge(manager, if_edge, then_edge)?);
572        let g = EdgeDropGuard::new(manager, Self::imp_strict_edge(manager, if_edge, else_edge)?);
573        Self::or_edge(manager, &*f, &*g)
574    }
575
576    /// Count the number of satisfying assignments, assuming `vars` input
577    /// variables
578    ///
579    /// The `cache` can be used to speed up multiple model counting operations
580    /// for functions in the same decision diagram. If the model counts of just
581    /// one function are of interest, one may simply pass an empty
582    /// [`SatCountCache`] (using `&mut SatCountCache::default()`). The cache
583    /// will automatically be invalidated in case there have been reordering
584    /// operations or `vars` changed since the last query (see
585    /// [`SatCountCache::clear_if_invalid()`]). Still, it is the caller's
586    /// responsibility to not use the cache for different managers.
587    ///
588    /// Locking behavior: acquires the manager's lock for shared access.
589    fn sat_count<N: SatCountNumber, S: std::hash::BuildHasher>(
590        &self,
591        vars: LevelNo,
592        cache: &mut SatCountCache<N, S>,
593    ) -> N {
594        self.with_manager_shared(|manager, edge| Self::sat_count_edge(manager, edge, vars, cache))
595    }
596
597    /// `Edge` version of [`Self::sat_count()`]
598    fn sat_count_edge<'id, N: SatCountNumber, S: std::hash::BuildHasher>(
599        manager: &Self::Manager<'id>,
600        edge: &EdgeOfFunc<'id, Self>,
601        vars: LevelNo,
602        cache: &mut SatCountCache<N, S>,
603    ) -> N;
604
605    /// Pick a cube of this function
606    ///
607    /// A cube `c` of a function `f` is a satisfying assignment, i.e., `c → f`
608    /// holds, and can be represented as a conjunction of literals. It does
609    /// not necessarily define all variables in the function's domain (it is
610    /// not necessarily a canonical minterm). For most (if not all) kinds of
611    /// decision diagrams, cubes have at most one node per level.
612    ///
613    /// `order` is a list of variables. If it is non-empty, it must contain as
614    /// many variables as there are levels.
615    ///
616    /// Returns `None` if the function is false. Otherwise, this method returns
617    /// a vector where the i-th entry indicates if the i-th variable of `order`
618    /// (or the variable currently at the i-th level in case `order` is empty)
619    /// is true, false, or "don't care".
620    ///
621    /// Whenever a value for a variable needs to be chosen (i.e., it cannot be
622    /// left as a don't care), `choice` is called to determine the valuation for
623    /// this variable. The argument of type [`LevelNo`] is the level
624    /// corresponding to that variable. It is guaranteed that `choice` will
625    /// only be called at most once for each level. The [`Edge`] argument is
626    /// guaranteed to point to an inner node at the respective level. (We
627    /// pass an [`Edge`] and not an [`InnerNode`] reference since [`Edge`]s
628    /// provide more information, e.g., the [`NodeID`][Edge::node_id()].)
629    ///
630    /// Locking behavior: acquires the manager's lock for shared access.
631    fn pick_cube<'a, I: ExactSizeIterator<Item = &'a Self>>(
632        &'a self,
633        order: impl IntoIterator<IntoIter = I>,
634        choice: impl for<'id> FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
635    ) -> Option<Vec<OptBool>> {
636        self.with_manager_shared(|manager, edge| {
637            Self::pick_cube_edge(
638                manager,
639                edge,
640                order.into_iter().map(|f| f.as_edge(manager)),
641                choice,
642            )
643        })
644    }
645
646    /// Pick a symbolic cube of this function, i.e., as decision diagram
647    ///
648    /// A cube `c` of a function `f` is a satisfying assignment, i.e., `c → f`
649    /// holds, and can be represented as a conjunction of literals. It does
650    /// not necessarily define all variables in the function's domain (it is
651    /// not necessarily a canonical minterm). For most (if not all) kinds of
652    /// decision diagrams, cubes have at most one node per level.
653    ///
654    /// Whenever a value for a variable needs to be chosen (i.e., it cannot be
655    /// left as a don't care), `choice` is called to determine the valuation for
656    /// this variable. The argument of type [`LevelNo`] is the level
657    /// corresponding to that variable. It is guaranteed that `choice` will
658    /// only be called at most once for each level. The [`Edge`] argument is
659    /// guaranteed to point to an inner node at the respective level. (We
660    /// pass an [`Edge`] and not an [`InnerNode`] reference since [`Edge`]s
661    /// provide more information, e.g., the [`NodeID`][Edge::node_id()].)
662    ///
663    /// If `self` is `⊥`, then the return value will be `⊥`.
664    ///
665    /// Locking behavior: acquires the manager's lock for shared access.
666    fn pick_cube_dd(
667        &self,
668        choice: impl for<'id> FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
669    ) -> AllocResult<Self> {
670        self.with_manager_shared(|manager, edge| {
671            let res = Self::pick_cube_dd_edge(manager, edge, choice)?;
672            Ok(Self::from_edge(manager, res))
673        })
674    }
675
676    /// Pick a symbolic cube of this function, i.e., as decision diagram, using
677    /// the literals in `literal_set` if there is a choice
678    ///
679    /// A cube `c` of a function `f` is a satisfying assignment, i.e., `c → f`
680    /// holds, and can be represented as a conjunction of literals. It does
681    /// not necessarily define all variables in the function's domain (it is
682    /// not necessarily a canonical minterm). For most (if not all) kinds of
683    /// decision diagrams, cubes have at most one node per level.
684    ///
685    /// `literal_set` is represented as a conjunction of literals. Whenever
686    /// there is a choice for a variable, it will be set to true if the variable
687    /// has a positive occurrence in `literal_set`, and set to false if it
688    /// occurs negated in `literal_set`. If the variable does not occur in
689    /// `literal_set`, then it will be left as don't care if possible, otherwise
690    /// an arbitrary (not necessarily random) choice will be performed.
691    ///
692    /// If `self` is `⊥`, then the return value will be `⊥`.
693    ///
694    /// Locking behavior: acquires the manager's lock for shared access.
695    fn pick_cube_dd_set(&self, literal_set: &Self) -> AllocResult<Self> {
696        self.with_manager_shared(|manager, edge| {
697            let res = Self::pick_cube_dd_set_edge(manager, edge, literal_set.as_edge(manager))?;
698            Ok(Self::from_edge(manager, res))
699        })
700    }
701
702    /// `Edge` version of [`Self::pick_cube()`]
703    fn pick_cube_edge<'id, 'a, I>(
704        manager: &'a Self::Manager<'id>,
705        edge: &'a EdgeOfFunc<'id, Self>,
706        order: impl IntoIterator<IntoIter = I>,
707        choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
708    ) -> Option<Vec<OptBool>>
709    where
710        I: ExactSizeIterator<Item = &'a EdgeOfFunc<'id, Self>>;
711
712    /// `Edge` version of [`Self::pick_cube_dd()`]
713    fn pick_cube_dd_edge<'id>(
714        manager: &Self::Manager<'id>,
715        edge: &EdgeOfFunc<'id, Self>,
716        choice: impl FnMut(&Self::Manager<'id>, &EdgeOfFunc<'id, Self>, LevelNo) -> bool,
717    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
718
719    /// `Edge` version of [`Self::pick_cube_dd_set()`]
720    fn pick_cube_dd_set_edge<'id>(
721        manager: &Self::Manager<'id>,
722        edge: &EdgeOfFunc<'id, Self>,
723        literal_set: &EdgeOfFunc<'id, Self>,
724    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
725
726    /// Pick a random cube of this function, where each cube has the same
727    /// probability of being chosen
728    ///
729    /// `order` is a list of variables. If it is non-empty, it must contain as
730    /// many variables as there are levels.
731    ///
732    /// Returns `None` if the function is false. Otherwise, this method returns
733    /// a vector where the i-th entry indicates if the i-th variable of `order`
734    /// (or the variable currently at the i-th level in case `order` is empty)
735    /// is true, false, or "don't care". To obtain a total valuation from this
736    /// partial valuation, it suffices to pick true or false with probability ½.
737    /// (Note that this function returns a partial valuation with n "don't
738    /// cares" with a probability that is 2<sup>n</sup> as high as the
739    /// probability of any total valuation.)
740    ///
741    /// Locking behavior: acquires the manager's lock for shared access.
742    fn pick_cube_uniform<'a, I: ExactSizeIterator<Item = &'a Self>, S: BuildHasher>(
743        &'a self,
744        order: impl IntoIterator<IntoIter = I>,
745        cache: &mut SatCountCache<F64, S>,
746        rng: &mut crate::util::Rng,
747    ) -> Option<Vec<OptBool>> {
748        self.with_manager_shared(|manager, edge| {
749            Self::pick_cube_uniform_edge(
750                manager,
751                edge,
752                order.into_iter().map(|f| f.as_edge(manager)),
753                cache,
754                rng,
755            )
756        })
757    }
758
759    /// `Edge` version of [`Self::pick_cube_uniform()`]
760    fn pick_cube_uniform_edge<'id, 'a, I, S>(
761        manager: &'a Self::Manager<'id>,
762        edge: &'a EdgeOfFunc<'id, Self>,
763        order: impl IntoIterator<IntoIter = I>,
764        cache: &mut SatCountCache<F64, S>,
765        rng: &mut crate::util::Rng,
766    ) -> Option<Vec<OptBool>>
767    where
768        I: ExactSizeIterator<Item = &'a EdgeOfFunc<'id, Self>>,
769        S: BuildHasher,
770    {
771        let vars = manager.num_levels();
772        Self::pick_cube_edge(manager, edge, order, |manager, edge, _| {
773            let tag = edge.tag();
774            // `edge` is guaranteed to point to an inner node
775            let node = manager.get_node(edge).unwrap_inner();
776            let (t, e) = Self::cofactors_node(tag, node);
777            let t_count = Self::sat_count_edge(manager, &*t, vars, cache).0;
778            let e_count = Self::sat_count_edge(manager, &*e, vars, cache).0;
779            rng.generate::<f64>() < t_count / (t_count + e_count)
780        })
781    }
782
783    /// Evaluate this Boolean function
784    ///
785    /// `args` consists of pairs `(variable, value)` and determines the
786    /// valuation for all variables. Missing values are assumed to be false.
787    /// However, note that the arguments may also determine the domain,
788    /// e.g., in case of ZBDDs. If values are specified multiple times, the
789    /// last one counts.
790    ///
791    /// Note that all variables in `args` must be handles for the respective
792    /// decision diagram levels, i.e., the Boolean function representing the
793    /// variable in case of B(C)DDs, and a singleton set for ZBDDs.
794    ///
795    /// Locking behavior: acquires the manager's lock for shared access.
796    ///
797    /// Panics if any function in `args` refers to a terminal node.
798    fn eval<'a>(&'a self, args: impl IntoIterator<Item = (&'a Self, bool)>) -> bool {
799        self.with_manager_shared(|manager, edge| {
800            Self::eval_edge(
801                manager,
802                edge,
803                args.into_iter()
804                    .map(|(f, b)| (f.as_edge(manager).borrowed(), b)),
805            )
806        })
807    }
808
809    /// `Edge` version of [`Self::eval()`]
810    fn eval_edge<'id, 'a>(
811        manager: &'a Self::Manager<'id>,
812        edge: &'a EdgeOfFunc<'id, Self>,
813        args: impl IntoIterator<Item = (Borrowed<'a, EdgeOfFunc<'id, Self>>, bool)>,
814    ) -> bool;
815}
816
817// The `cfg_attr` below is used such that cbindgen does not output the
818// Rust-specific documentation.
819
820/// Binary operators on Boolean functions
821#[cfg_attr(
822    all(),
823    doc = "
824
825The operators are used by the combined apply and quantification operations of
826the [`BooleanFunctionQuant`] trait. The operators themselves correspond to the
827ones defined in [`BooleanFunction`]."
828)]
829#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
830#[repr(u8)]
831pub enum BooleanOperator {
832    /// Conjunction `lhs ∧ rhs`
833    And,
834    /// Disjunction `lhs ∨ rhs`
835    Or,
836    /// Exclusive disjunction `lhs ⊕ rhs`
837    Xor,
838    /// Equivalence `lhs ↔ rhs`
839    Equiv,
840    /// Negated conjunction `lhs ⊼ rhs`
841    Nand,
842    /// Negated disjunction `lhs ⊽ rhs`
843    Nor,
844    /// Implication `lhs → rhs`
845    Imp,
846    /// Strict implication `lhs < rhs`
847    ImpStrict,
848}
849
850/// cbindgen:ignore
851unsafe impl crate::Countable for BooleanOperator {
852    const MAX_VALUE: usize = BooleanOperator::ImpStrict as usize;
853
854    fn as_usize(self) -> usize {
855        self as usize
856    }
857
858    fn from_usize(value: usize) -> Self {
859        use BooleanOperator::*;
860        match () {
861            _ if value == And as usize => And,
862            _ if value == Or as usize => Or,
863            _ if value == Xor as usize => Xor,
864            _ if value == Equiv as usize => Equiv,
865            _ if value == Nand as usize => Nand,
866            _ if value == Nor as usize => Nor,
867            _ if value == Imp as usize => Imp,
868            _ if value == ImpStrict as usize => ImpStrict,
869            _ => panic!("{value} does not correspond to a Boolean operator"),
870        }
871    }
872}
873
874/// cbindgen:ignore
875impl Display for BooleanOperator {
876    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
877        use BooleanOperator::*;
878        match self {
879            And => write!(f, "∧"),
880            Or => write!(f, "∨"),
881            Xor => write!(f, "⊕"),
882            Equiv => write!(f, "↔"),
883            Nand => write!(f, "⊼"),
884            Nor => write!(f, "⊽"),
885            Imp => write!(f, "→"),
886            ImpStrict => write!(f, "<"),
887        }
888    }
889}
890
891/// Quantification extension for [`BooleanFunction`]
892pub trait BooleanFunctionQuant: BooleanFunction {
893    /// Restrict a set of `vars` to constant values
894    ///
895    /// `vars` conceptually is a partial assignment, represented as the
896    /// conjunction of positive or negative literals, depending on whether the
897    /// variable should be mapped to true or false.
898    ///
899    /// Locking behavior: acquires the manager's lock for shared access.
900    ///
901    /// Panics if `self` and `vars` don't belong to the same manager.
902    fn restrict(&self, vars: &Self) -> AllocResult<Self> {
903        self.with_manager_shared(|manager, root| {
904            let e = Self::restrict_edge(manager, root, vars.as_edge(manager))?;
905            Ok(Self::from_edge(manager, e))
906        })
907    }
908
909    /// Compute the universal quantification over `vars`
910    ///
911    /// `vars` is a set of variables, which in turn is just the conjunction of
912    /// the variables. This operation removes all occurrences of the variables
913    /// by universal quantification. Universal quantification `∀x. f(…, x, …)`
914    /// of a Boolean function `f(…, x, …)` over a single variable `x` is
915    /// `f(…, 0, …) ∧ f(…, 1, …)`.
916    ///
917    /// Locking behavior: acquires the manager's lock for shared access.
918    ///
919    /// Panics if `self` and `vars` don't belong to the same manager.
920    fn forall(&self, vars: &Self) -> AllocResult<Self> {
921        self.with_manager_shared(|manager, root| {
922            let e = Self::forall_edge(manager, root, vars.as_edge(manager))?;
923            Ok(Self::from_edge(manager, e))
924        })
925    }
926
927    /// Compute the existential quantification over `vars`
928    ///
929    /// `vars` is a set of variables, which in turn is just the conjunction of
930    /// the variables. This operation removes all occurrences of the variables
931    /// by existential quantification. Existential quantification
932    /// `∃x. f(…, x, …)` of a Boolean function `f(…, x, …)` over a single
933    /// variable `x` is `f(…, 0, …) ∨ f(…, 1, …)`.
934    ///
935    /// Locking behavior: acquires the manager's lock for shared access.
936    ///
937    /// Panics if `self` and `vars` don't belong to the same manager.
938    fn exists(&self, vars: &Self) -> AllocResult<Self> {
939        self.with_manager_shared(|manager, root| {
940            let e = Self::exists_edge(manager, root, vars.as_edge(manager))?;
941            Ok(Self::from_edge(manager, e))
942        })
943    }
944    /// Deprecated alias for [`Self::exists()`]
945    #[deprecated]
946    fn exist(&self, vars: &Self) -> AllocResult<Self> {
947        self.exists(vars)
948    }
949
950    /// Compute the unique quantification over `vars`
951    ///
952    /// `vars` is a set of variables, which in turn is just the conjunction of
953    /// the variables. This operation removes all occurrences of the variables
954    /// by unique quantification. Unique quantification `∃!x. f(…, x, …)` of a
955    /// Boolean function `f(…, x, …)` over a single variable `x` is
956    /// `f(…, 0, …) ⊕ f(…, 1, …)`.
957    ///
958    /// Unique quantification is also known as the
959    /// [Boolean difference](https://en.wikipedia.org/wiki/Boole%27s_expansion_theorem#Operations_with_cofactors)
960    /// or
961    /// [Boolean derivative](https://en.wikipedia.org/wiki/Boolean_differential_calculus).
962    ///
963    /// Locking behavior: acquires the manager's lock for shared access.
964    ///
965    /// Panics if `self` and `vars` don't belong to the same manager.
966    fn unique(&self, vars: &Self) -> AllocResult<Self> {
967        self.with_manager_shared(|manager, root| {
968            let e = Self::unique_edge(manager, root, vars.as_edge(manager))?;
969            Ok(Self::from_edge(manager, e))
970        })
971    }
972
973    /// Combined application of `op` and quantification `∀x. self <op> rhs`,
974    /// where `<op>` is any of the operations from [`BooleanOperator`]
975    ///
976    /// See also [`Self::forall()`] and the trait [`BooleanFunction`] for more
977    /// details.
978    ///
979    /// Locking behavior: acquires the manager's lock for shared access.
980    ///
981    /// Panics if `self` and `rhs` and `vars` don't belong to the same manager.
982    fn apply_forall(&self, op: BooleanOperator, rhs: &Self, vars: &Self) -> AllocResult<Self> {
983        self.with_manager_shared(|manager, root| {
984            let e = Self::apply_forall_edge(
985                manager,
986                op,
987                root,
988                rhs.as_edge(manager),
989                vars.as_edge(manager),
990            )?;
991            Ok(Self::from_edge(manager, e))
992        })
993    }
994
995    /// Combined application of `op` and quantification `∃x. self <op> rhs`,
996    /// where `<op>` is any of the operations from [`BooleanOperator`]
997    ///
998    /// See also [`Self::exist()`] and the trait [`BooleanFunction`] for more
999    /// details.
1000    ///
1001    /// Panics if `self` and `rhs` and `vars` don't belong to the same manager.
1002    fn apply_exists(&self, op: BooleanOperator, rhs: &Self, vars: &Self) -> AllocResult<Self> {
1003        self.with_manager_shared(|manager, root| {
1004            let e = Self::apply_exists_edge(
1005                manager,
1006                op,
1007                root,
1008                rhs.as_edge(manager),
1009                vars.as_edge(manager),
1010            )?;
1011            Ok(Self::from_edge(manager, e))
1012        })
1013    }
1014    /// Deprecated alias for [`Self::apply_exists()`]
1015    #[deprecated]
1016    #[must_use]
1017    fn apply_exist(&self, op: BooleanOperator, rhs: &Self, vars: &Self) -> AllocResult<Self> {
1018        self.apply_exists(op, rhs, vars)
1019    }
1020
1021    /// Combined application of `op` and quantification `∃!x. self <op> rhs`,
1022    /// where `<op>` is any of the operations from [`BooleanOperator`]
1023    ///
1024    /// See also [`Self::unique()`] and the trait [`BooleanFunction`] for more
1025    /// details.
1026    ///
1027    /// Panics if `self` and `rhs` and `vars` don't belong to the same manager.
1028    fn apply_unique(&self, op: BooleanOperator, rhs: &Self, vars: &Self) -> AllocResult<Self> {
1029        self.with_manager_shared(|manager, root| {
1030            let e = Self::apply_unique_edge(
1031                manager,
1032                op,
1033                root,
1034                rhs.as_edge(manager),
1035                vars.as_edge(manager),
1036            )?;
1037            Ok(Self::from_edge(manager, e))
1038        })
1039    }
1040
1041    /// Restrict a set of `vars` to constant values, edge version
1042    ///
1043    /// See [`Self::restrict()`] for more details.
1044    #[must_use]
1045    fn restrict_edge<'id>(
1046        manager: &Self::Manager<'id>,
1047        root: &EdgeOfFunc<'id, Self>,
1048        vars: &EdgeOfFunc<'id, Self>,
1049    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1050
1051    /// Compute the universal quantification of `root` over `vars`, edge
1052    /// version
1053    ///
1054    /// See [`Self::forall()`] for more details.
1055    #[must_use]
1056    fn forall_edge<'id>(
1057        manager: &Self::Manager<'id>,
1058        root: &EdgeOfFunc<'id, Self>,
1059        vars: &EdgeOfFunc<'id, Self>,
1060    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1061
1062    /// Compute the existential quantification of `root` over `vars`, edge
1063    /// version
1064    ///
1065    /// See [`Self::exists()`] for more details.
1066    #[must_use]
1067    fn exists_edge<'id>(
1068        manager: &Self::Manager<'id>,
1069        root: &EdgeOfFunc<'id, Self>,
1070        vars: &EdgeOfFunc<'id, Self>,
1071    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1072    /// Deprecated alias for [`Self::exists_edge()`]
1073    #[must_use]
1074    #[deprecated]
1075    fn exist_edge<'id>(
1076        manager: &Self::Manager<'id>,
1077        root: &EdgeOfFunc<'id, Self>,
1078        vars: &EdgeOfFunc<'id, Self>,
1079    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1080        Self::exists_edge(manager, root, vars)
1081    }
1082
1083    /// Compute the unique quantification of `root` over `vars`, edge version
1084    ///
1085    /// See [`Self::unique()`] for more details.
1086    #[must_use]
1087    fn unique_edge<'id>(
1088        manager: &Self::Manager<'id>,
1089        root: &EdgeOfFunc<'id, Self>,
1090        vars: &EdgeOfFunc<'id, Self>,
1091    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1092
1093    /// Combined application of `op` and forall quantification, edge version
1094    ///
1095    /// See [`Self::apply_forall()`] for more details.
1096    #[must_use]
1097    fn apply_forall_edge<'id>(
1098        manager: &Self::Manager<'id>,
1099        op: BooleanOperator,
1100        lhs: &EdgeOfFunc<'id, Self>,
1101        rhs: &EdgeOfFunc<'id, Self>,
1102        vars: &EdgeOfFunc<'id, Self>,
1103    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1104        // Naive default implementation
1105        use BooleanOperator::*;
1106        let inner = EdgeDropGuard::new(
1107            manager,
1108            match op {
1109                And => Self::and_edge(manager, lhs, rhs),
1110                Or => Self::or_edge(manager, lhs, rhs),
1111                Xor => Self::xor_edge(manager, lhs, rhs),
1112                Equiv => Self::equiv_edge(manager, lhs, rhs),
1113                Nand => Self::nand_edge(manager, lhs, rhs),
1114                Nor => Self::nor_edge(manager, lhs, rhs),
1115                Imp => Self::imp_edge(manager, lhs, rhs),
1116                ImpStrict => Self::imp_strict_edge(manager, lhs, rhs),
1117            }?,
1118        );
1119
1120        Self::forall_edge(manager, &inner, vars)
1121    }
1122
1123    /// Combined application of `op` and existential quantification, edge
1124    /// version
1125    ///
1126    /// See [`Self::apply_exist()`] for more details.
1127    #[must_use]
1128    fn apply_exists_edge<'id>(
1129        manager: &Self::Manager<'id>,
1130        op: BooleanOperator,
1131        lhs: &EdgeOfFunc<'id, Self>,
1132        rhs: &EdgeOfFunc<'id, Self>,
1133        vars: &EdgeOfFunc<'id, Self>,
1134    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1135        // Naive default implementation
1136        use BooleanOperator::*;
1137        let inner = EdgeDropGuard::new(
1138            manager,
1139            match op {
1140                And => Self::and_edge(manager, lhs, rhs),
1141                Or => Self::or_edge(manager, lhs, rhs),
1142                Xor => Self::xor_edge(manager, lhs, rhs),
1143                Equiv => Self::equiv_edge(manager, lhs, rhs),
1144                Nand => Self::nand_edge(manager, lhs, rhs),
1145                Nor => Self::nor_edge(manager, lhs, rhs),
1146                Imp => Self::imp_edge(manager, lhs, rhs),
1147                ImpStrict => Self::imp_strict_edge(manager, lhs, rhs),
1148            }?,
1149        );
1150
1151        Self::exists_edge(manager, &inner, vars)
1152    }
1153    /// Deprecated alias for [`Self::apply_exists_edge()`]
1154    #[deprecated]
1155    #[must_use]
1156    fn apply_exist_edge<'id>(
1157        manager: &Self::Manager<'id>,
1158        op: BooleanOperator,
1159        lhs: &EdgeOfFunc<'id, Self>,
1160        rhs: &EdgeOfFunc<'id, Self>,
1161        vars: &EdgeOfFunc<'id, Self>,
1162    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1163        Self::apply_exists_edge(manager, op, lhs, rhs, vars)
1164    }
1165
1166    /// Combined application of `op` and unique quantification, edge version
1167    ///
1168    /// See [`Self::apply_unique()`] for more details.
1169    #[must_use]
1170    fn apply_unique_edge<'id>(
1171        manager: &Self::Manager<'id>,
1172        op: BooleanOperator,
1173        lhs: &EdgeOfFunc<'id, Self>,
1174        rhs: &EdgeOfFunc<'id, Self>,
1175        vars: &EdgeOfFunc<'id, Self>,
1176    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1177        // Naive default implementation
1178        use BooleanOperator::*;
1179        let inner = EdgeDropGuard::new(
1180            manager,
1181            match op {
1182                And => Self::and_edge(manager, lhs, rhs),
1183                Or => Self::or_edge(manager, lhs, rhs),
1184                Xor => Self::xor_edge(manager, lhs, rhs),
1185                Equiv => Self::equiv_edge(manager, lhs, rhs),
1186                Nand => Self::nand_edge(manager, lhs, rhs),
1187                Nor => Self::nor_edge(manager, lhs, rhs),
1188                Imp => Self::imp_edge(manager, lhs, rhs),
1189                ImpStrict => Self::imp_strict_edge(manager, lhs, rhs),
1190            }?,
1191        );
1192
1193        Self::unique_edge(manager, &inner, vars)
1194    }
1195}
1196
1197/// Set of Boolean vectors
1198///
1199/// A Boolean function f: 𝔹ⁿ → 𝔹 may also be regarded as a set S ∈ 𝒫(𝔹ⁿ), where
1200/// S = {v ∈ 𝔹ⁿ | f(v) = 1}. f is also called the characteristic function of S.
1201/// We can even view a Boolean vector as a subset of some "Universe" U, so we
1202/// also have S ∈ 𝒫(𝒫(U)). For example, let U = {a, b, c}. The function a is
1203/// the set of all sets containing a, {a, ab, abc, ac} (for the sake of
1204/// readability, we write ab for the set {a, b}). Conversely, the set {a} is the
1205/// function a ∧ ¬b ∧ ¬c.
1206///
1207/// Counting the number of elements in a `BoolVecSet` is equivalent to counting
1208/// the number of satisfying assignments of its characteristic function. Hence,
1209/// you may use [`BooleanFunction::sat_count()`] for this task.
1210///
1211/// The functions of this trait can be implemented efficiently for ZBDDs.
1212///
1213/// As a user of this trait, you are probably most interested in methods like
1214/// [`Self::union()`], [`Self::intsec()`], and [`Self::diff()`]. As an
1215/// implementor, it suffices to implement the functions operating on edges.
1216pub trait BooleanVecSet: Function {
1217    /// Add a new variable to the manager and get the corresponding singleton
1218    /// set
1219    ///
1220    /// This adds a new level to the decision diagram.
1221    fn new_singleton<'id>(manager: &mut Self::Manager<'id>) -> AllocResult<Self>;
1222
1223    /// Get the empty set ∅
1224    ///
1225    /// This corresponds to the Boolean function ⊥.
1226    fn empty<'id>(manager: &Self::Manager<'id>) -> Self {
1227        Self::from_edge(manager, Self::empty_edge(manager))
1228    }
1229
1230    /// Get the set {∅}
1231    ///
1232    /// This corresponds to the Boolean function ¬x₁ ∧ … ∧ ¬xₙ
1233    fn base<'id>(manager: &Self::Manager<'id>) -> Self {
1234        Self::from_edge(manager, Self::base_edge(manager))
1235    }
1236
1237    /// Get the subset of `self` not containing `var`, formally
1238    /// `{s ∈ self | var ∉ s}`
1239    ///
1240    /// `var` must be a singleton set, otherwise the result is unspecified.
1241    /// Ideally, the implementation panics.
1242    ///
1243    /// Locking behavior: acquires a shared manager lock
1244    ///
1245    /// Panics if `self` and `var` do not belong to the same manager.
1246    fn subset0(&self, var: &Self) -> AllocResult<Self> {
1247        self.with_manager_shared(|manager, set| {
1248            let e = Self::subset0_edge(manager, set, var.as_edge(manager))?;
1249            Ok(Self::from_edge(manager, e))
1250        })
1251    }
1252
1253    /// Get the subset of `self` containing `var` with `var` removed afterwards,
1254    /// formally `{s ∖ {var} | s ∈ self ∧ var ∈ s}`
1255    ///
1256    /// `var` must be a singleton set, otherwise the result is unspecified.
1257    /// Ideally, the implementation panics.
1258    ///
1259    /// Locking behavior: acquires a shared manager lock
1260    ///
1261    /// Panics if `self` and `var` do not belong to the same manager.
1262    fn subset1(&self, var: &Self) -> AllocResult<Self> {
1263        self.with_manager_shared(|manager, set| {
1264            let e = Self::subset1_edge(manager, set, var.as_edge(manager))?;
1265            Ok(Self::from_edge(manager, e))
1266        })
1267    }
1268
1269    /// Swap [`subset0`][Self::subset0] and [`subset1`][Self::subset1] with
1270    /// respect to `var`, formally
1271    /// `{s ∪ {var} | s ∈ self ∧ var ∉ s} ∪ {s ∖ {var} | s ∈ self ∧ var ∈ s}`
1272    ///
1273    /// `var` must be a singleton set, otherwise the result is unspecified.
1274    /// Ideally, the implementation panics.
1275    ///
1276    /// Locking behavior: acquires a shared manager lock
1277    ///
1278    /// Panics if `self` and `var` do not belong to the same manager.
1279    fn change(&self, var: &Self) -> AllocResult<Self> {
1280        self.with_manager_shared(|manager, set| {
1281            let e = Self::change_edge(manager, set, var.as_edge(manager))?;
1282            Ok(Self::from_edge(manager, e))
1283        })
1284    }
1285
1286    /// Compute the union `self ∪ rhs`
1287    ///
1288    /// Locking behavior: acquires a shared manager lock
1289    ///
1290    /// Panics if `self` and `rhs` do not belong to the same manager.
1291    fn union(&self, rhs: &Self) -> AllocResult<Self> {
1292        self.with_manager_shared(|manager, lhs| {
1293            let e = Self::union_edge(manager, lhs, rhs.as_edge(manager))?;
1294            Ok(Self::from_edge(manager, e))
1295        })
1296    }
1297
1298    /// Compute the intersection `self ∩ rhs`
1299    ///
1300    /// Locking behavior: acquires a shared manager lock
1301    ///
1302    /// Panics if `self` and `rhs` do not belong to the same manager.
1303    fn intsec(&self, rhs: &Self) -> AllocResult<Self> {
1304        self.with_manager_shared(|manager, lhs| {
1305            let e = Self::intsec_edge(manager, lhs, rhs.as_edge(manager))?;
1306            Ok(Self::from_edge(manager, e))
1307        })
1308    }
1309
1310    /// Compute the set difference `self ∖ rhs`
1311    ///
1312    /// Locking behavior: acquires a shared manager lock
1313    ///
1314    /// Panics if `self` and `rhs` do not belong to the same manager.
1315    fn diff(&self, rhs: &Self) -> AllocResult<Self> {
1316        self.with_manager_shared(|manager, lhs| {
1317            let e = Self::diff_edge(manager, lhs, rhs.as_edge(manager))?;
1318            Ok(Self::from_edge(manager, e))
1319        })
1320    }
1321
1322    /// Edge version of [`Self::empty()`]
1323    fn empty_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
1324
1325    /// Edge version of [`Self::base()`]
1326    fn base_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
1327
1328    /// Edge version of [`Self::subset0()`]
1329    fn subset0_edge<'id>(
1330        manager: &Self::Manager<'id>,
1331        set: &EdgeOfFunc<'id, Self>,
1332        var: &EdgeOfFunc<'id, Self>,
1333    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1334
1335    /// Edge version of [`Self::subset1()`]
1336    fn subset1_edge<'id>(
1337        manager: &Self::Manager<'id>,
1338        set: &EdgeOfFunc<'id, Self>,
1339        var: &EdgeOfFunc<'id, Self>,
1340    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1341
1342    /// Edge version of [`Self::change()`]
1343    fn change_edge<'id>(
1344        manager: &Self::Manager<'id>,
1345        set: &EdgeOfFunc<'id, Self>,
1346        var: &EdgeOfFunc<'id, Self>,
1347    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1348
1349    /// Compute the union `lhs ∪ rhs`, edge version
1350    fn union_edge<'id>(
1351        manager: &Self::Manager<'id>,
1352        lhs: &EdgeOfFunc<'id, Self>,
1353        rhs: &EdgeOfFunc<'id, Self>,
1354    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1355
1356    /// Compute the intersection `lhs ∩ rhs`, edge version
1357    fn intsec_edge<'id>(
1358        manager: &Self::Manager<'id>,
1359        lhs: &EdgeOfFunc<'id, Self>,
1360        rhs: &EdgeOfFunc<'id, Self>,
1361    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1362
1363    /// Compute the set difference `lhs ∖ rhs`, edge version
1364    fn diff_edge<'id>(
1365        manager: &Self::Manager<'id>,
1366        lhs: &EdgeOfFunc<'id, Self>,
1367        rhs: &EdgeOfFunc<'id, Self>,
1368    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1369}
1370
1371/// Basic trait for numbers
1372///
1373/// [`zero()`][Self::zero], [`one()`][Self::one], and [`nan()`][Self::nan] are
1374/// implemented as functions because depending on the number underlying type,
1375/// it can be hard/impossible to obtain a `const` for these values.
1376/// This trait also includes basic arithmetic methods. This is to avoid cloning
1377/// of big integers. We could also require `&Self: Add<&Self, Output = Self>`
1378/// etc., but this would lead to ugly trait bounds.
1379///
1380/// Used by [`PseudoBooleanFunction::Number`]
1381pub trait NumberBase: Clone + Eq + Hash + PartialOrd {
1382    /// Get the value 0
1383    fn zero() -> Self;
1384    /// Get the value 1
1385    fn one() -> Self;
1386
1387    /// Get the value "not a number," e.g. the result of a division 0/0.
1388    ///
1389    /// `Self::nan() == Self::nan()` should evaluate to `true`.
1390    fn nan() -> Self;
1391
1392    /// Returns `true` iff `self == Self::zero()`
1393    fn is_zero(&self) -> bool {
1394        self == &Self::zero()
1395    }
1396    /// Returns `true` iff `self == Self::one()`
1397    fn is_one(&self) -> bool {
1398        self == &Self::one()
1399    }
1400    /// Returns `true` iff `self == Self::nan()`
1401    fn is_nan(&self) -> bool {
1402        self == &Self::nan()
1403    }
1404
1405    /// Compute `self + rhs`
1406    fn add(&self, rhs: &Self) -> Self;
1407    /// Compute `self - rhs`
1408    fn sub(&self, rhs: &Self) -> Self;
1409    /// Compute `self * rhs`
1410    fn mul(&self, rhs: &Self) -> Self;
1411    /// Compute `self / rhs`
1412    fn div(&self, rhs: &Self) -> Self;
1413}
1414
1415/// Pseudo-Boolean function 𝔹ⁿ → ℝ
1416pub trait PseudoBooleanFunction: Function {
1417    /// The number type used for the functions' target set.
1418    type Number: NumberBase;
1419
1420    /// Get the constant `value`
1421    fn constant<'id>(manager: &Self::Manager<'id>, value: Self::Number) -> AllocResult<Self> {
1422        Ok(Self::from_edge(
1423            manager,
1424            Self::constant_edge(manager, value)?,
1425        ))
1426    }
1427
1428    /// Get a fresh variable, i.e. a function that is 1 if the variable is true
1429    /// and 0 otherwise. This adds a new level to a decision diagram.
1430    fn new_var<'id>(manager: &mut Self::Manager<'id>) -> AllocResult<Self>;
1431
1432    /// Point-wise addition `self + rhs`
1433    ///
1434    /// Locking behavior: acquires a shared manager lock
1435    ///
1436    /// Panics if `self` and `rhs` do not belong to the same manager.
1437    fn add(&self, rhs: &Self) -> AllocResult<Self> {
1438        self.with_manager_shared(|manager, lhs| {
1439            let e = Self::add_edge(manager, lhs, rhs.as_edge(manager))?;
1440            Ok(Self::from_edge(manager, e))
1441        })
1442    }
1443
1444    /// Point-wise subtraction `self - rhs`
1445    ///
1446    /// Locking behavior: acquires a shared manager lock
1447    ///
1448    /// Panics if `self` and `rhs` do not belong to the same manager.
1449    fn sub(&self, rhs: &Self) -> AllocResult<Self> {
1450        self.with_manager_shared(|manager, lhs| {
1451            let e = Self::sub_edge(manager, lhs, rhs.as_edge(manager))?;
1452            Ok(Self::from_edge(manager, e))
1453        })
1454    }
1455
1456    /// Point-wise multiplication `self * rhs`
1457    ///
1458    /// Locking behavior: acquires a shared manager lock
1459    ///
1460    /// Panics if `self` and `rhs` do not belong to the same manager.
1461    fn mul(&self, rhs: &Self) -> AllocResult<Self> {
1462        self.with_manager_shared(|manager, lhs| {
1463            let e = Self::mul_edge(manager, lhs, rhs.as_edge(manager))?;
1464            Ok(Self::from_edge(manager, e))
1465        })
1466    }
1467
1468    /// Point-wise division `self / rhs`
1469    ///
1470    /// Locking behavior: acquires a shared manager lock
1471    ///
1472    /// Panics if `self` and `rhs` do not belong to the same manager.
1473    fn div(&self, rhs: &Self) -> AllocResult<Self> {
1474        self.with_manager_shared(|manager, lhs| {
1475            let e = Self::div_edge(manager, lhs, rhs.as_edge(manager))?;
1476            Ok(Self::from_edge(manager, e))
1477        })
1478    }
1479
1480    /// Point-wise minimum `min(self, rhs)`
1481    ///
1482    /// Locking behavior: acquires a shared manager lock
1483    ///
1484    /// Panics if `self` and `rhs` do not belong to the same manager.
1485    fn min(&self, rhs: &Self) -> AllocResult<Self> {
1486        self.with_manager_shared(|manager, lhs| {
1487            let e = Self::min_edge(manager, lhs, rhs.as_edge(manager))?;
1488            Ok(Self::from_edge(manager, e))
1489        })
1490    }
1491
1492    /// Point-wise maximum `max(self, rhs)`
1493    ///
1494    /// Locking behavior: acquires a shared manager lock
1495    ///
1496    /// Panics if `self` and `rhs` do not belong to the same manager.
1497    fn max(&self, rhs: &Self) -> AllocResult<Self> {
1498        self.with_manager_shared(|manager, lhs| {
1499            let e = Self::max_edge(manager, lhs, rhs.as_edge(manager))?;
1500            Ok(Self::from_edge(manager, e))
1501        })
1502    }
1503
1504    /// Get the constant `value`, edge version
1505    fn constant_edge<'id>(
1506        manager: &Self::Manager<'id>,
1507        value: Self::Number,
1508    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1509
1510    /// Point-wise addition `self + rhs`, edge version
1511    fn add_edge<'id>(
1512        manager: &Self::Manager<'id>,
1513        lhs: &EdgeOfFunc<'id, Self>,
1514        rhs: &EdgeOfFunc<'id, Self>,
1515    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1516
1517    /// Point-wise subtraction `self - rhs`, edge version
1518    fn sub_edge<'id>(
1519        manager: &Self::Manager<'id>,
1520        lhs: &EdgeOfFunc<'id, Self>,
1521        rhs: &EdgeOfFunc<'id, Self>,
1522    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1523
1524    /// Point-wise multiplication `self * rhs`, edge version
1525    fn mul_edge<'id>(
1526        manager: &Self::Manager<'id>,
1527        lhs: &EdgeOfFunc<'id, Self>,
1528        rhs: &EdgeOfFunc<'id, Self>,
1529    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1530
1531    /// Point-wise division `self / rhs`, edge version
1532    fn div_edge<'id>(
1533        manager: &Self::Manager<'id>,
1534        lhs: &EdgeOfFunc<'id, Self>,
1535        rhs: &EdgeOfFunc<'id, Self>,
1536    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1537
1538    /// Point-wise minimum `min(self, rhs)`, edge version
1539    fn min_edge<'id>(
1540        manager: &Self::Manager<'id>,
1541        lhs: &EdgeOfFunc<'id, Self>,
1542        rhs: &EdgeOfFunc<'id, Self>,
1543    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1544
1545    /// Point-wise maximum `max(self, rhs)`, edge version
1546    fn max_edge<'id>(
1547        manager: &Self::Manager<'id>,
1548        lhs: &EdgeOfFunc<'id, Self>,
1549        rhs: &EdgeOfFunc<'id, Self>,
1550    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1551}
1552
1553/// Function of three valued logic
1554pub trait TVLFunction: Function {
1555    /// Get the always false function `⊥`
1556    fn f<'id>(manager: &Self::Manager<'id>) -> Self {
1557        Self::from_edge(manager, Self::f_edge(manager))
1558    }
1559    /// Get the always true function `⊤`
1560    fn t<'id>(manager: &Self::Manager<'id>) -> Self {
1561        Self::from_edge(manager, Self::t_edge(manager))
1562    }
1563    /// Get the "unknown" function `U`
1564    fn u<'id>(manager: &Self::Manager<'id>) -> Self {
1565        Self::from_edge(manager, Self::t_edge(manager))
1566    }
1567
1568    /// Get the cofactors `(f_true, f_unknown, f_false)` of `self`
1569    ///
1570    /// Let f(x₀, …, xₙ) be represented by `self`, where x₀ is (currently) the
1571    /// top-most variable. Then f<sub>true</sub>(x₁, …, xₙ) = f(⊤, x₁, …, xₙ)
1572    /// and f<sub>false</sub>(x₁, …, xₙ) = f(⊥, x₁, …, xₙ).
1573    ///
1574    /// Note that the domain of f is 𝔹ⁿ⁺¹ while the domain of f<sub>true</sub>
1575    /// and f<sub>false</sub> is 𝔹ⁿ.
1576    ///
1577    /// Returns `None` iff `self` references a terminal node. If you only need
1578    /// `f_true`, `f_unknown`, or `f_false`, [`Self::cofactor_true`],
1579    /// [`Self::cofactor_unknown`], or [`Self::cofactor_false`] are slightly
1580    /// more efficient.
1581    ///
1582    /// Locking behavior: acquires the manager's lock for shared access.
1583    fn cofactors(&self) -> Option<(Self, Self, Self)> {
1584        self.with_manager_shared(|manager, f| {
1585            let (ft, fu, ff) = Self::cofactors_edge(manager, f)?;
1586            Some((
1587                Self::from_edge_ref(manager, &ft),
1588                Self::from_edge_ref(manager, &fu),
1589                Self::from_edge_ref(manager, &ff),
1590            ))
1591        })
1592    }
1593
1594    /// Get the cofactor `f_true` of `self`
1595    ///
1596    /// This method is slightly more efficient than [`Self::cofactors`] in case
1597    /// `f_unknown` and `f_false` are not needed.
1598    ///
1599    /// For a more detailed description, see [`Self::cofactors`].
1600    ///
1601    /// Returns `None` iff `self` references a terminal node.
1602    ///
1603    /// Locking behavior: acquires the manager's lock for shared access.
1604    fn cofactor_true(&self) -> Option<Self> {
1605        self.with_manager_shared(|manager, f| {
1606            let (ft, _, _) = Self::cofactors_edge(manager, f)?;
1607            Some(Self::from_edge_ref(manager, &ft))
1608        })
1609    }
1610    /// Get the cofactor `f_unknown` of `self`
1611    ///
1612    /// This method is slightly more efficient than [`Self::cofactors`] in case
1613    /// `f_true` and `f_false` are not needed.
1614    ///
1615    /// For a more detailed description, see [`Self::cofactors`].
1616    ///
1617    /// Returns `None` iff `self` references a terminal node.
1618    ///
1619    /// Locking behavior: acquires the manager's lock for shared access.
1620    fn cofactor_unknown(&self) -> Option<Self> {
1621        self.with_manager_shared(|manager, f| {
1622            let (_, fu, _) = Self::cofactors_edge(manager, f)?;
1623            Some(Self::from_edge_ref(manager, &fu))
1624        })
1625    }
1626    /// Get the cofactor `f_false` of `self`
1627    ///
1628    /// This method is slightly more efficient than [`Self::cofactors`] in case
1629    /// `f_true` and `f_unknown` are not needed.
1630    ///
1631    /// For a more detailed description, see [`Self::cofactors`].
1632    ///
1633    /// Returns `None` iff `self` references a terminal node.
1634    ///
1635    /// Locking behavior: acquires the manager's lock for shared access.
1636    fn cofactor_false(&self) -> Option<Self> {
1637        self.with_manager_shared(|manager, f| {
1638            let (_, _, ff) = Self::cofactors_edge(manager, f)?;
1639            Some(Self::from_edge_ref(manager, &ff))
1640        })
1641    }
1642
1643    /// Get a fresh variable, i.e. a function that is true if the variable is
1644    /// true, false if the variable is false, and undefined otherwise. This adds
1645    /// a new level to a decision diagram.
1646    fn new_var<'id>(manager: &mut Self::Manager<'id>) -> AllocResult<Self>;
1647
1648    /// Compute the negation `¬self`
1649    ///
1650    /// Locking behavior: acquires the manager's lock for shared access.
1651    fn not(&self) -> AllocResult<Self> {
1652        self.with_manager_shared(|manager, edge| {
1653            Ok(Self::from_edge(manager, Self::not_edge(manager, edge)?))
1654        })
1655    }
1656    /// Compute the negation `¬self`, owned version
1657    ///
1658    /// Compared to [`Self::not()`], this method does not need to clone the
1659    /// function, so when the implementation is using (e.g.) complemented edges,
1660    /// this might be a little bit faster than [`Self::not()`].
1661    ///
1662    /// Locking behavior: acquires the manager's lock for shared access.
1663    fn not_owned(self) -> AllocResult<Self> {
1664        self.not()
1665    }
1666    /// Compute the conjunction `self ∧ rhs`
1667    ///
1668    /// Locking behavior: acquires the manager's lock for shared access.
1669    ///
1670    /// Panics if `self` and `rhs` don't belong to the same manager.
1671    fn and(&self, rhs: &Self) -> AllocResult<Self> {
1672        self.with_manager_shared(|manager, lhs| {
1673            let e = Self::and_edge(manager, lhs, rhs.as_edge(manager))?;
1674            Ok(Self::from_edge(manager, e))
1675        })
1676    }
1677    /// Compute the disjunction `self ∨ rhs`
1678    ///
1679    /// Locking behavior: acquires the manager's lock for shared access.
1680    ///
1681    /// Panics if `self` and `rhs` don't belong to the same manager.
1682    fn or(&self, rhs: &Self) -> AllocResult<Self> {
1683        self.with_manager_shared(|manager, lhs| {
1684            let e = Self::or_edge(manager, lhs, rhs.as_edge(manager))?;
1685            Ok(Self::from_edge(manager, e))
1686        })
1687    }
1688    /// Compute the negated conjunction `self ⊼ rhs`
1689    ///
1690    /// Locking behavior: acquires the manager's lock for shared access.
1691    ///
1692    /// Panics if `self` and `rhs` don't belong to the same manager.
1693    fn nand(&self, rhs: &Self) -> AllocResult<Self> {
1694        self.with_manager_shared(|manager, lhs| {
1695            let e = Self::nand_edge(manager, lhs, rhs.as_edge(manager))?;
1696            Ok(Self::from_edge(manager, e))
1697        })
1698    }
1699    /// Compute the negated disjunction `self ⊽ rhs`
1700    ///
1701    /// Locking behavior: acquires the manager's lock for shared access.
1702    ///
1703    /// Panics if `self` and `rhs` don't belong to the same manager.
1704    fn nor(&self, rhs: &Self) -> AllocResult<Self> {
1705        self.with_manager_shared(|manager, lhs| {
1706            let e = Self::nor_edge(manager, lhs, rhs.as_edge(manager))?;
1707            Ok(Self::from_edge(manager, e))
1708        })
1709    }
1710    /// Compute the exclusive disjunction `self ⊕ rhs`
1711    ///
1712    /// Locking behavior: acquires the manager's lock for shared access.
1713    ///
1714    /// Panics if `self` and `rhs` don't belong to the same manager.
1715    fn xor(&self, rhs: &Self) -> AllocResult<Self> {
1716        self.with_manager_shared(|manager, lhs| {
1717            let e = Self::xor_edge(manager, lhs, rhs.as_edge(manager))?;
1718            Ok(Self::from_edge(manager, e))
1719        })
1720    }
1721    /// Compute the equivalence `self ↔ rhs`
1722    ///
1723    /// Locking behavior: acquires the manager's lock for shared access.
1724    ///
1725    /// Panics if `self` and `rhs` don't belong to the same manager.
1726    fn equiv(&self, rhs: &Self) -> AllocResult<Self> {
1727        self.with_manager_shared(|manager, lhs| {
1728            let e = Self::equiv_edge(manager, lhs, rhs.as_edge(manager))?;
1729            Ok(Self::from_edge(manager, e))
1730        })
1731    }
1732    /// Compute the implication `self → rhs` (or `self ≤ rhs`)
1733    ///
1734    /// Locking behavior: acquires the manager's lock for shared access.
1735    ///
1736    /// Panics if `self` and `rhs` don't belong to the same manager.
1737    fn imp(&self, rhs: &Self) -> AllocResult<Self> {
1738        self.with_manager_shared(|manager, lhs| {
1739            let e = Self::imp_edge(manager, lhs, rhs.as_edge(manager))?;
1740            Ok(Self::from_edge(manager, e))
1741        })
1742    }
1743    /// Compute the strict implication `self < rhs`
1744    ///
1745    /// Locking behavior: acquires the manager's lock for shared access.
1746    ///
1747    /// Panics if `self` and `rhs` don't belong to the same manager.
1748    fn imp_strict(&self, rhs: &Self) -> AllocResult<Self> {
1749        self.with_manager_shared(|manager, lhs| {
1750            let e = Self::imp_strict_edge(manager, lhs, rhs.as_edge(manager))?;
1751            Ok(Self::from_edge(manager, e))
1752        })
1753    }
1754
1755    /// Get the always false function `⊥` as edge
1756    fn f_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
1757    /// Get the always true function `⊤` as edge
1758    fn t_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
1759    /// Get the "unknown" function `U` as edge
1760    fn u_edge<'id>(manager: &Self::Manager<'id>) -> EdgeOfFunc<'id, Self>;
1761
1762    /// Get the cofactors `(f_true, f_unknown, f_false)` of `f`, edge version
1763    ///
1764    /// Returns `None` iff `f` references a terminal node. For more details on
1765    /// the semantics of `f_true` and `f_false`, see [`Self::cofactors`].
1766    #[inline]
1767    #[allow(clippy::type_complexity)]
1768    fn cofactors_edge<'a, 'id>(
1769        manager: &'a Self::Manager<'id>,
1770        f: &'a EdgeOfFunc<'id, Self>,
1771    ) -> Option<(
1772        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1773        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1774        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1775    )> {
1776        if let Node::Inner(node) = manager.get_node(f) {
1777            Some(Self::cofactors_node(f.tag(), node))
1778        } else {
1779            None
1780        }
1781    }
1782
1783    /// Get the cofactors `(f_true, f_unknown, f_false)` of `node`, assuming an
1784    /// incoming edge with `EdgeTag`
1785    ///
1786    /// Returns `None` iff `f` references a terminal node. For more details on
1787    /// the semantics of `f_true` and `f_false`, see [`Self::cofactors`].
1788    ///
1789    /// Implementation note: The default implementation assumes that
1790    /// [cofactor 0][DiagramRules::cofactor] corresponds to `f_true`,
1791    /// [cofactor 1][DiagramRules::cofactor] corresponds to `f_unknown`, and
1792    /// [cofactor 2][DiagramRules::cofactor] corresponds to `f_false`.
1793    #[inline]
1794    #[allow(clippy::type_complexity)]
1795    fn cofactors_node<'a, 'id>(
1796        tag: ETagOfFunc<'id, Self>,
1797        node: &'a INodeOfFunc<'id, Self>,
1798    ) -> (
1799        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1800        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1801        Borrowed<'a, EdgeOfFunc<'id, Self>>,
1802    ) {
1803        let cofactor = <<Self::Manager<'id> as Manager>::Rules as DiagramRules<_, _, _>>::cofactor;
1804        (
1805            cofactor(tag, node, 0),
1806            cofactor(tag, node, 1),
1807            cofactor(tag, node, 2),
1808        )
1809    }
1810
1811    /// Compute the negation `¬edge`, edge version
1812    #[must_use]
1813    fn not_edge<'id>(
1814        manager: &Self::Manager<'id>,
1815        edge: &EdgeOfFunc<'id, Self>,
1816    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1817
1818    /// Compute the negation `¬edge`, owned edge version
1819    ///
1820    /// Compared to [`Self::not_edge()`], this method does not need to clone the
1821    /// edge, so when the implementation is using (e.g.) complemented edges,
1822    /// this might be a little bit faster than [`Self::not_edge()`].
1823    #[must_use]
1824    fn not_edge_owned<'id>(
1825        manager: &Self::Manager<'id>,
1826        edge: EdgeOfFunc<'id, Self>,
1827    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1828        Self::not_edge(manager, &edge)
1829    }
1830
1831    /// Compute the conjunction `lhs ∧ rhs`, edge version
1832    #[must_use]
1833    fn and_edge<'id>(
1834        manager: &Self::Manager<'id>,
1835        lhs: &EdgeOfFunc<'id, Self>,
1836        rhs: &EdgeOfFunc<'id, Self>,
1837    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1838    /// Compute the disjunction `lhs ∨ rhs`, edge version
1839    #[must_use]
1840    fn or_edge<'id>(
1841        manager: &Self::Manager<'id>,
1842        lhs: &EdgeOfFunc<'id, Self>,
1843        rhs: &EdgeOfFunc<'id, Self>,
1844    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1845    /// Compute the negated conjunction `lhs ⊼ rhs`, edge version
1846    #[must_use]
1847    fn nand_edge<'id>(
1848        manager: &Self::Manager<'id>,
1849        lhs: &EdgeOfFunc<'id, Self>,
1850        rhs: &EdgeOfFunc<'id, Self>,
1851    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1852    /// Compute the negated disjunction `lhs ⊽ rhs`, edge version
1853    #[must_use]
1854    fn nor_edge<'id>(
1855        manager: &Self::Manager<'id>,
1856        lhs: &EdgeOfFunc<'id, Self>,
1857        rhs: &EdgeOfFunc<'id, Self>,
1858    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1859    /// Compute the exclusive disjunction `lhs ⊕ rhs`, edge version
1860    #[must_use]
1861    fn xor_edge<'id>(
1862        manager: &Self::Manager<'id>,
1863        lhs: &EdgeOfFunc<'id, Self>,
1864        rhs: &EdgeOfFunc<'id, Self>,
1865    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1866    /// Compute the equivalence `lhs ↔ rhs`, edge version
1867    #[must_use]
1868    fn equiv_edge<'id>(
1869        manager: &Self::Manager<'id>,
1870        lhs: &EdgeOfFunc<'id, Self>,
1871        rhs: &EdgeOfFunc<'id, Self>,
1872    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1873    /// Compute the implication `lhs → rhs`, edge version
1874    #[must_use]
1875    fn imp_edge<'id>(
1876        manager: &Self::Manager<'id>,
1877        lhs: &EdgeOfFunc<'id, Self>,
1878        rhs: &EdgeOfFunc<'id, Self>,
1879    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1880    /// Compute the strict implication `lhs < rhs`, edge version
1881    #[must_use]
1882    fn imp_strict_edge<'id>(
1883        manager: &Self::Manager<'id>,
1884        lhs: &EdgeOfFunc<'id, Self>,
1885        rhs: &EdgeOfFunc<'id, Self>,
1886    ) -> AllocResult<EdgeOfFunc<'id, Self>>;
1887
1888    /// Compute `if self { then_case } else { else_case }`
1889    ///
1890    /// This is equivalent to `(self ∧ then_case) ∨ (¬self ∧ else_case)` but
1891    /// possibly more efficient than computing all the
1892    /// conjunctions/disjunctions.
1893    ///
1894    /// Locking behavior: acquires the manager's lock for shared access.
1895    ///
1896    /// Panics if `self`, `then_case`, and `else_case` don't belong to the same
1897    /// manager.
1898    fn ite(&self, then_case: &Self, else_case: &Self) -> AllocResult<Self> {
1899        self.with_manager_shared(|manager, if_edge| {
1900            let then_edge = then_case.as_edge(manager);
1901            let else_edge = else_case.as_edge(manager);
1902            let res = Self::ite_edge(manager, if_edge, then_edge, else_edge)?;
1903            Ok(Self::from_edge(manager, res))
1904        })
1905    }
1906
1907    /// Compute `if if_edge { then_edge } else { else_edge }` (edge version)
1908    ///
1909    /// This is equivalent to `(self ∧ then_case) ∨ (¬self ∧ else_case)` but
1910    /// possibly more efficient than computing all the
1911    /// conjunctions/disjunctions.
1912    #[must_use]
1913    fn ite_edge<'id>(
1914        manager: &Self::Manager<'id>,
1915        if_edge: &EdgeOfFunc<'id, Self>,
1916        then_edge: &EdgeOfFunc<'id, Self>,
1917        else_edge: &EdgeOfFunc<'id, Self>,
1918    ) -> AllocResult<EdgeOfFunc<'id, Self>> {
1919        let f = EdgeDropGuard::new(manager, Self::and_edge(manager, if_edge, then_edge)?);
1920        let g = EdgeDropGuard::new(manager, Self::imp_strict_edge(manager, if_edge, else_edge)?);
1921        Self::or_edge(manager, &*f, &*g)
1922    }
1923}