Skip to main content

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