taco_bdd/
lib.rs

1//! Common interface for binary decision diagrams (BDDs) and BDD managers
2//!
3//! This package provides a generic interface for BDDs and BDD managers,
4//! allowing the use of multiple BDD libraries, depending on your needs.
5//! Currently, this package includes CUDD and OxiDD:
6//! - [CUDD](https://github.com/cuddorg/cudd)
7//! - [OxiDD](https://github.com/OxiDD/oxidd)
8//!
9//! To interface with BDDs use the [`BDD`] and [`BDDManager`] types, as well as
10//! the [`BDDManagerConfig`] to configure the different managers.
11//!
12//! To add a new BDD library, implement the [`Bdd`]  and [`BddManager`] traits
13//! for the library, add it as new module and add a variant for the new library
14//! to the [`BDD`] and [`BDDManager`] types.
15
16use std::{fmt::Debug, ops};
17
18#[cfg(feature = "cudd")]
19use cudd::{CuddDD, CuddManager};
20#[cfg(feature = "oxidd")]
21use oxidd::{OxiDD, OxiddManager};
22#[cfg(feature = "config_deserialize")]
23use serde::Deserialize;
24
25/// CUDD library
26#[cfg(feature = "cudd")]
27#[allow(unsafe_code)]
28mod cudd;
29
30/// OxiDD library
31#[cfg(feature = "oxidd")]
32mod oxidd;
33
34/// Common representation of a binary decision diagram (BDD)
35///
36/// This enum defines a variant for each library that can be used. Note that
37/// combining BDDs of different libraries and managers will generally result in
38/// panics.
39///
40/// Currently, the supported BDD libraries are:
41/// - [CUDD](https://github.com/ivmai/cudd)
42/// - [OxiDD](https://github.com/OxiDD/oxidd)
43#[derive(Debug, Clone, PartialEq)]
44pub enum BDD {
45    /// A CUDD BDD
46    #[cfg(feature = "cudd")]
47    CuDD(CuddDD),
48    /// An OxiDD BDD
49    #[cfg(feature = "oxidd")]
50    OxiDD(OxiDD),
51}
52
53impl Bdd for BDD {
54    fn not(&self) -> Self {
55        match self {
56            #[cfg(feature = "cudd")]
57            BDD::CuDD(dd) => BDD::CuDD(dd.not()),
58            #[cfg(feature = "oxidd")]
59            BDD::OxiDD(dd) => BDD::OxiDD(dd.not()),
60        }
61    }
62
63    #[allow(unreachable_patterns)]
64    fn and(&self, rhs: &Self) -> Self {
65        match self {
66            #[cfg(feature = "cudd")]
67            BDD::CuDD(dd) => match rhs {
68                BDD::CuDD(rhs_dd) => BDD::CuDD(dd.and(rhs_dd)),
69                _ => panic!("BDDs are from different managers"),
70            },
71            #[cfg(feature = "oxidd")]
72            BDD::OxiDD(dd) => match rhs {
73                BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.and(rhs_dd)),
74                _ => panic!("BDDs are from different managers"),
75            },
76        }
77    }
78
79    #[allow(unreachable_patterns)]
80    fn or(&self, rhs: &Self) -> Self {
81        match self {
82            #[cfg(feature = "cudd")]
83            BDD::CuDD(dd) => match rhs {
84                BDD::CuDD(rhs_dd) => BDD::CuDD(dd.or(rhs_dd)),
85                _ => panic!("BDDs are from different managers"),
86            },
87            #[cfg(feature = "oxidd")]
88            BDD::OxiDD(dd) => match rhs {
89                BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.or(rhs_dd)),
90                _ => panic!("BDDs are from different managers"),
91            },
92        }
93    }
94
95    #[allow(unreachable_patterns)]
96    fn exists<'a, I: IntoIterator<Item = &'a Self>>(&'a self, vars: I) -> Self {
97        match self {
98            #[cfg(feature = "cudd")]
99            BDD::CuDD(dd) => BDD::CuDD(dd.exists(vars.into_iter().map(|v| match v {
100                BDD::CuDD(v) => v,
101                _ => panic!("BDDs are from different managers"),
102            }))),
103            #[cfg(feature = "oxidd")]
104            BDD::OxiDD(dd) => BDD::OxiDD(dd.exists(vars.into_iter().map(|v| match v {
105                BDD::OxiDD(v) => v,
106                _ => panic!("BDDs are from different managers"),
107            }))),
108        }
109    }
110
111    fn satisfiable(&self) -> bool {
112        match self {
113            #[cfg(feature = "cudd")]
114            BDD::CuDD(dd) => dd.satisfiable(),
115            #[cfg(feature = "oxidd")]
116            BDD::OxiDD(dd) => dd.satisfiable(),
117        }
118    }
119
120    #[allow(unreachable_patterns)]
121    fn implies(&self, rhs: &Self) -> Self {
122        match self {
123            #[cfg(feature = "cudd")]
124            BDD::CuDD(dd) => match rhs {
125                BDD::CuDD(rhs_dd) => BDD::CuDD(dd.implies(rhs_dd)),
126                _ => panic!("BDDs are from different managers"),
127            },
128            #[cfg(feature = "oxidd")]
129            BDD::OxiDD(dd) => match rhs {
130                BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.implies(rhs_dd)),
131                _ => panic!("BDDs are from different managers"),
132            },
133        }
134    }
135
136    #[allow(unreachable_patterns)]
137    fn equiv(&self, rhs: &Self) -> Self {
138        match self {
139            #[cfg(feature = "cudd")]
140            BDD::CuDD(dd) => match rhs {
141                BDD::CuDD(rhs_dd) => BDD::CuDD(dd.equiv(rhs_dd)),
142                _ => panic!("BDDs are from different managers"),
143            },
144            #[cfg(feature = "oxidd")]
145            BDD::OxiDD(dd) => match rhs {
146                BDD::OxiDD(rhs_dd) => BDD::OxiDD(dd.equiv(rhs_dd)),
147                _ => panic!("BDDs are from different managers"),
148            },
149        }
150    }
151
152    #[allow(unreachable_patterns)]
153    fn swap<'a, I: IntoIterator<Item = &'a Self>>(&'a self, from: I, to: I) -> Self {
154        match self {
155            #[cfg(feature = "cudd")]
156            BDD::CuDD(dd) => {
157                let to_cudd = |v: &'a BDD| match v {
158                    BDD::CuDD(v) => v,
159                    _ => panic!("BDDs are from different managers"),
160                };
161
162                let to = to.into_iter().map(to_cudd);
163                let from = from.into_iter().map(to_cudd);
164
165                BDD::CuDD(dd.swap(from, to))
166            }
167            #[cfg(feature = "oxidd")]
168            BDD::OxiDD(dd) => {
169                let to_oxidd = |v: &'a BDD| match v {
170                    BDD::OxiDD(v) => v,
171                    _ => panic!("BDDs are from different managers"),
172                };
173
174                let to = to.into_iter().map(to_oxidd);
175                let from = from.into_iter().map(to_oxidd);
176
177                BDD::OxiDD(dd.swap(from, to))
178            }
179        }
180    }
181}
182
183impl ops::Not for &BDD {
184    type Output = BDD;
185
186    fn not(self) -> BDD {
187        Bdd::not(self)
188    }
189}
190
191impl ops::Not for BDD {
192    type Output = BDD;
193
194    fn not(self) -> BDD {
195        Bdd::not(&self)
196    }
197}
198
199impl ops::BitAnd for &BDD {
200    type Output = BDD;
201
202    fn bitand(self, rhs: Self) -> BDD {
203        Bdd::and(self, rhs)
204    }
205}
206
207impl ops::BitAnd for BDD {
208    type Output = BDD;
209
210    fn bitand(self, rhs: Self) -> BDD {
211        Bdd::and(&self, &rhs)
212    }
213}
214
215impl ops::BitAndAssign for BDD {
216    fn bitand_assign(&mut self, rhs: Self) {
217        *self = Bdd::and(self, &rhs);
218    }
219}
220
221impl ops::BitOr for &BDD {
222    type Output = BDD;
223
224    fn bitor(self, rhs: Self) -> BDD {
225        Bdd::or(self, rhs)
226    }
227}
228
229impl ops::BitOr for BDD {
230    type Output = BDD;
231
232    fn bitor(self, rhs: Self) -> BDD {
233        Bdd::or(&self, &rhs)
234    }
235}
236
237impl ops::BitOrAssign for BDD {
238    fn bitor_assign(&mut self, rhs: Self) {
239        *self = Bdd::or(self, &rhs);
240    }
241}
242
243/// Configuration for a BDD manager
244///
245/// This struct is the common interface for configuration of a BDD manager.
246#[derive(Debug, Clone, PartialEq)]
247#[cfg_attr(feature = "config_deserialize", derive(Deserialize))]
248pub enum BDDManagerConfig {
249    /// CUDD manager configuration
250    #[cfg(feature = "cudd")]
251    Cudd(cudd::CuddManagerConfig),
252    /// OxiDD manager configuration
253    #[cfg(feature = "oxidd")]
254    Oxidd(oxidd::OxiddManagerConfig),
255}
256
257impl BDDManagerConfig {
258    /// Create a new BDD manager with the current configuration
259    pub fn mgr_from_config(&self) -> BDDManager {
260        match self {
261            #[cfg(feature = "cudd")]
262            BDDManagerConfig::Cudd(cfg) => BDDManager::new_cudd_with_config(cfg),
263            #[cfg(feature = "oxidd")]
264            BDDManagerConfig::Oxidd(cfg) => BDDManager::new_oxidd_with_config(cfg),
265        }
266    }
267
268    /// Get the default configuration for the cudd BDD manager
269    #[cfg(feature = "cudd")]
270    pub fn new_cudd() -> Self {
271        BDDManagerConfig::Cudd(cudd::CuddManagerConfig::default())
272    }
273
274    /// Get the default configuration for the oxidd BDD manager
275    #[cfg(feature = "oxidd")]
276    pub fn new_oxidd() -> Self {
277        BDDManagerConfig::Oxidd(oxidd::OxiddManagerConfig::default())
278    }
279}
280
281/// The `BDDManager` type allows to interface with BDD Managers from different
282/// libraries.
283///
284/// Currently, the supported BDD libraries are:
285/// - [CUDD](https://github.com/ivmai/cudd)
286/// - [OxiDD](https://github.com/OxiDD/oxidd)
287#[derive(Debug, Clone, PartialEq)]
288pub enum BDDManager {
289    /// CUDD BDD library backend
290    #[cfg(feature = "cudd")]
291    CuDD(CuddManager),
292    /// OxiDD BDD library backend
293    #[cfg(feature = "oxidd")]
294    OxiDD(OxiddManager),
295}
296
297impl BDDManager {
298    /// Create a new BDD manager with the given configuration
299    pub fn new(cfg: &BDDManagerConfig) -> Self {
300        cfg.mgr_from_config()
301    }
302
303    /// Create a new Oxidd BDD manager with default configuration.
304    #[cfg(feature = "oxidd")]
305    pub fn new_oxidd() -> Self {
306        BDDManager::OxiDD(OxiddManager::default())
307    }
308
309    /// Create a new Oxidd BDD manager with custom configuration.
310    #[cfg(feature = "oxidd")]
311    pub fn new_oxidd_with_config(cfg: &oxidd::OxiddManagerConfig) -> Self {
312        BDDManager::OxiDD(OxiddManager::new_with_config(cfg))
313    }
314
315    /// Create a new CUDD BDD manager with default configuration.
316    #[cfg(feature = "cudd")]
317    pub fn new_cudd() -> Self {
318        BDDManager::CuDD(CuddManager::default())
319    }
320
321    /// Create a new CUDD BDD manager with custom configuration.
322    #[cfg(feature = "cudd")]
323    pub fn new_cudd_with_config(cfg: &cudd::CuddManagerConfig) -> Self {
324        BDDManager::CuDD(CuddManager::new_with_config(cfg))
325    }
326}
327
328impl Default for BDDManager {
329    /// Default BDD manager is the CUDD manager, if the `cudd` feature is
330    /// enabled. Otherwise, it will default to the OxiDD manager or panic if no
331    /// BDD library is enabled during compilation.
332    #[allow(unreachable_code)]
333    fn default() -> Self {
334        #[cfg(feature = "cudd")]
335        return BDDManager::CuDD(CuddManager::default());
336        #[cfg(feature = "oxidd")]
337        return BDDManager::OxiDD(OxiddManager::default());
338        panic!("No BDD library enabled during compilation");
339    }
340}
341
342impl BddManager for BDDManager {
343    type DD = BDD;
344
345    fn new_var(&mut self) -> Self::DD {
346        match self {
347            #[cfg(feature = "cudd")]
348            BDDManager::CuDD(mgr) => BDD::CuDD(mgr.new_var()),
349            #[cfg(feature = "oxidd")]
350            BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.new_var()),
351        }
352    }
353
354    fn get_bdd_false(&self) -> Self::DD {
355        match self {
356            #[cfg(feature = "cudd")]
357            BDDManager::CuDD(mgr) => BDD::CuDD(mgr.get_bdd_false()),
358            #[cfg(feature = "oxidd")]
359            BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.get_bdd_false()),
360        }
361    }
362
363    fn get_bdd_true(&self) -> Self::DD {
364        match self {
365            #[cfg(feature = "cudd")]
366            BDDManager::CuDD(mgr) => BDD::CuDD(mgr.get_bdd_true()),
367            #[cfg(feature = "oxidd")]
368            BDDManager::OxiDD(mgr) => BDD::OxiDD(mgr.get_bdd_true()),
369        }
370    }
371
372    #[cfg(feature = "visualize")]
373    fn dump_dot<'a, VN, BN>(
374        &'a self,
375        file: std::fs::File,
376        bdd_names: impl IntoIterator<Item = (&'a Self::DD, BN)>,
377        variable_names: impl IntoIterator<Item = (&'a Self::DD, VN)>,
378    ) -> std::io::Result<()>
379    where
380        VN: std::fmt::Display + Clone,
381        BN: std::fmt::Display,
382    {
383        match self {
384            #[cfg(feature = "cudd")]
385            BDDManager::CuDD(mgr) => mgr.dump_dot(
386                file,
387                bdd_names.into_iter().map(|(bdd, name)| match bdd {
388                    BDD::CuDD(dd) => (dd, name),
389                    _ => panic!("BDDs are from different managers"),
390                }),
391                variable_names.into_iter().map(|(bdd, name)| match bdd {
392                    BDD::CuDD(dd) => (dd, name),
393                    _ => panic!("BDDs are from different managers"),
394                }),
395            ),
396            #[cfg(feature = "oxidd")]
397            BDDManager::OxiDD(mgr) => mgr.dump_dot(
398                file,
399                bdd_names.into_iter().map(|(bdd, name)| match bdd {
400                    BDD::OxiDD(dd) => (dd, name),
401                    _ => panic!("BDDs are from different managers"),
402                }),
403                variable_names.into_iter().map(|(bdd, name)| match bdd {
404                    BDD::OxiDD(dd) => (dd, name),
405                    _ => panic!("BDDs are from different managers"),
406                }),
407            ),
408        }
409    }
410}
411
412/// The `BDD` trait defines the interface for a binary decision diagram.
413pub trait Bdd: Sized + Debug + PartialEq + Clone {
414    /// Negation of a BDD.
415    fn not(&self) -> Self;
416
417    /// Conjunction of two BDDs.
418    fn and(&self, rhs: &Self) -> Self;
419
420    /// Disjunction of two BDDs.
421    fn or(&self, rhs: &Self) -> Self;
422
423    /// Existential quantification over a set of variables `vars`
424    fn exists<'a, I: IntoIterator<Item = &'a Self>>(&'a self, vars: I) -> Self;
425
426    /// Check whether a satisfying assignment exists for the BDD.
427    fn satisfiable(&self) -> bool;
428
429    /// Compute the implication of two BDDs, i.e., the bdd `lhs => rhs`.
430    fn implies(&self, rhs: &Self) -> Self;
431
432    /// Compute the equivalence of two BDDs, i.e., the bdd `lhs <=> rhs`.
433    fn equiv(&self, rhs: &Self) -> Self;
434
435    /// Swap variables according to the permutation.
436    fn swap<'a, I: IntoIterator<Item = &'a Self>>(&'a self, from: I, to: I) -> Self;
437}
438
439/// The `BDDManager` trait defines the interface for a BDD manager. The
440/// associated  type `DD` is the type of BDDs created by this manager.
441pub trait BddManager: Debug + PartialEq + Clone + Default {
442    /// The type of BDDs created by the manager.
443    type DD: Bdd;
444
445    /// Create a new BDD that is not referenced yet.
446    fn new_var(&mut self) -> Self::DD;
447
448    /// Get false BDD
449    fn get_bdd_false(&self) -> Self::DD;
450
451    /// Get true BDD
452    fn get_bdd_true(&self) -> Self::DD;
453
454    #[cfg(feature = "visualize")]
455    /// Write a file in the `dot` format visualizing all BDDs included in
456    /// `bdd_names`
457    ///
458    /// This function will create a file in the `dot` format that visualizes all
459    /// BDDs in `bdd_names`, and associating them with the names given by the
460    /// second argument of the tuple in the iterator.
461    ///
462    /// Optionally, variables can be named in the visualization by supplying
463    /// the variable names in the `variable_names` iterator.
464    /// Note that, when working with CUDD the variable names must be supplied in
465    /// as an iterator according to the index of the variable.
466    ///
467    ///
468    /// # Example
469    ///
470    /// ```ignore
471    /// use bdd::{BDDManager, Bdd, BddManager};
472    ///
473    /// let var1 = mgr.new_var();
474    /// let var2 = mgr.new_var();
475    /// let var3 = mgr.new_var();
476    ///
477    /// let node = var1.and(&var2);
478    /// let node = node.or(&var3);
479    ///
480    /// let file = std::fs::File::create("./test-doc.dot").unwrap();
481    /// mgr.dump_dot(
482    ///        file,
483    ///        vec![(&node, "foo")],
484    ///        vec![(&var1, "var1"), (&var2, "var2"), (&var3, "var3")],
485    ///    ).unwrap();
486    ///
487    /// std::fs::remove_file(file).unwrap();
488    /// ```
489    fn dump_dot<'a, VN, BN>(
490        &'a self,
491        file: std::fs::File,
492        bdd_names: impl IntoIterator<Item = (&'a Self::DD, BN)>,
493        variable_names: impl IntoIterator<Item = (&'a Self::DD, VN)>,
494    ) -> std::io::Result<()>
495    where
496        VN: std::fmt::Display + Clone,
497        BN: std::fmt::Display;
498}
499
500/// Trait for types that can provide a preconfigured BDDManager
501///
502/// This trait is implemented by types that can provide configured BDDManagers.
503/// For example, this trait can be implemented by model checker contexts.
504pub trait ProvidesBDDManager {
505    /// Get a new, already configured bdd manager
506    fn get_new_bdd_manager(&self) -> BDDManager;
507}
508
509impl ProvidesBDDManager for BDDManager {
510    fn get_new_bdd_manager(&self) -> BDDManager {
511        self.clone()
512    }
513}
514
515/// This module defines generic tests for BDD managers and BDDs. These tests can
516/// be used as a sanity check for libraries, but are not exhaustive.
517#[cfg(test)]
518mod bdd_test_utils {
519
520    use crate::Bdd;
521
522    use super::BddManager;
523
524    /// Tests equality between two BDD managers and their clones.
525    pub(crate) fn test_mgr_eq_and_clone<T: BddManager>(mgr1: T, mgr2: T) {
526        assert_ne!(mgr1, mgr2);
527        assert_eq!(mgr1, mgr1.clone());
528    }
529
530    pub(crate) fn test_mgr_get<T: BddManager>(mut mgr: T) {
531        let f = mgr.get_bdd_false();
532        assert!(!f.satisfiable());
533
534        let t = mgr.get_bdd_true();
535        assert!(t.satisfiable());
536
537        let var = mgr.new_var();
538        assert!(var.satisfiable())
539    }
540
541    pub(crate) fn test_not<T: BddManager>(mut mgr: T) {
542        let f = mgr.get_bdd_false();
543        assert!(f.not().satisfiable());
544
545        let t = mgr.get_bdd_true();
546        assert!(!t.not().satisfiable());
547
548        let var = mgr.new_var();
549        assert!(var.not().satisfiable());
550    }
551
552    pub(crate) fn test_and<T: BddManager>(mut mgr: T) {
553        let f = mgr.get_bdd_false();
554        let t = mgr.get_bdd_true();
555
556        assert!(t.and(&t).satisfiable());
557        assert!(!t.and(&f).satisfiable());
558        assert!(!f.and(&t).satisfiable());
559        assert!(!f.and(&f).satisfiable());
560
561        let var = mgr.new_var();
562        assert!(t.and(&var).satisfiable());
563        assert!(var.and(&t).satisfiable());
564
565        assert!(!f.and(&var).satisfiable());
566        assert!(!var.and(&f).satisfiable());
567    }
568
569    pub(crate) fn test_or<T: BddManager>(mut mgr: T) {
570        let f = mgr.get_bdd_false();
571        let t = mgr.get_bdd_true();
572
573        assert!(t.or(&t).satisfiable());
574        assert!(t.or(&f).satisfiable());
575        assert!(f.or(&t).satisfiable());
576        assert!(!f.or(&f).satisfiable());
577
578        let var = mgr.new_var();
579        assert!(t.or(&var).satisfiable());
580        assert!(var.or(&t).satisfiable());
581
582        assert!(f.or(&var).satisfiable());
583        assert!(var.or(&f).satisfiable());
584    }
585
586    pub(crate) fn test_exists<T: BddManager>(mut mgr: T) {
587        let var0 = mgr.new_var();
588        let var1 = mgr.new_var();
589        let var2 = mgr.new_var();
590        let var3 = mgr.new_var();
591
592        let con = var0.and(&var1).and(&var3);
593        assert_eq!(con.exists(vec![&var0, &var1, &var2]), var3)
594    }
595
596    pub(crate) fn test_implies<T: BddManager>(mut mgr: T) {
597        let var1 = mgr.new_var();
598
599        let con1 = &var1;
600        let con2 = &var1.not();
601
602        assert_eq!(con1.implies(con2).and(&var1), mgr.get_bdd_false());
603
604        assert_eq!(con1.implies(&con2.not()), mgr.get_bdd_true());
605    }
606
607    pub(crate) fn test_equiv<T: BddManager>(mut mgr: T) {
608        let var0 = mgr.new_var();
609        let var1 = mgr.new_var();
610        let var2 = mgr.new_var();
611        let var3 = mgr.new_var();
612
613        let con1 = var0.and(&var1).and(&var2);
614        let con2 = var0.and(&var3).and(&var2);
615
616        assert!(con1.equiv(&con2).satisfiable());
617
618        let con3 = var0.and(&var1).and(&var2);
619        let con4 = var0.and(&var1).and(&var2);
620
621        assert_eq!(con3.equiv(&con4), mgr.get_bdd_true());
622
623        let con5 = &var1;
624        let con6 = &var1.not();
625        assert_eq!(con5.equiv(con6), mgr.get_bdd_false());
626    }
627
628    pub(crate) fn test_swap<T: BddManager>(mut mgr: T) {
629        let var0 = mgr.new_var();
630        let var1 = mgr.new_var();
631        let var2 = mgr.new_var();
632        let var3 = mgr.new_var();
633
634        let con1 = var0.and(&var1).and(&var2);
635        let expected_con = var0.and(&var3).and(&var2);
636
637        assert_eq!(con1.swap(vec![&var1], vec![&var3]), expected_con);
638    }
639
640    pub(crate) fn test_swap_behavior_primed_var_in_bdd<T: BddManager>(mut mgr: T) {
641        let l_0 = mgr.new_var();
642        let l_0_prime = mgr.new_var();
643
644        // this should be bitwise
645        let r_0 = mgr.new_var();
646
647        let test_bdd = l_0.and(&r_0.and(&l_0_prime.not()));
648        let test_bdd = test_bdd.swap(&vec![l_0.clone()], &vec![l_0_prime.clone()]);
649
650        let unexpected_bdd = l_0_prime.and(&r_0.and(&l_0_prime.not()));
651        assert_ne!(test_bdd, unexpected_bdd);
652
653        let expected_bdd = l_0_prime.and(&r_0.and(&l_0.not()));
654        assert_eq!(test_bdd, expected_bdd)
655    }
656
657    /// Expected behavior: panic when trying to create a BDD with different
658    /// managers.
659    pub(crate) fn panic_on_bdd_of_mixed_mgrs<T: BddManager>(mut mgr1: T, mut mgr2: T) {
660        let bdd1 = mgr1.new_var();
661        let bdd2 = mgr2.new_var();
662
663        bdd1.and(&bdd2);
664    }
665}
666
667#[cfg(test)]
668mod test {
669    use super::*;
670    use bdd_test_utils::*;
671
672    fn test_all_functions(f: impl Fn() -> BDDManager) {
673        let mgr1 = f();
674        let mgr2 = f();
675
676        test_mgr_eq_and_clone(mgr1, mgr2);
677
678        let mgr = f();
679        test_mgr_get::<BDDManager>(mgr);
680
681        let mgr = f();
682        test_not(mgr);
683
684        let mgr = f();
685        test_and(mgr);
686
687        let mgr = f();
688        test_or(mgr);
689
690        let mgr = f();
691        test_exists(mgr);
692
693        let mgr = f();
694        test_implies(mgr);
695
696        let mgr = f();
697        test_equiv(mgr);
698
699        let mgr = f();
700        test_swap(mgr);
701
702        let mgr = f();
703        test_swap_behavior_primed_var_in_bdd(mgr);
704    }
705
706    fn test_operators(f: impl Fn() -> BDDManager) {
707        let mut mgr = f();
708
709        // Test operator implementation on BDD
710        let bdd1 = mgr.new_var();
711        let bdd2 = mgr.new_var();
712
713        // Test NOT operator
714        assert_eq!(!&bdd1, bdd1.not());
715
716        // Test AND operator
717        assert_eq!(&bdd1 & &bdd2, bdd1.and(&bdd2));
718        assert_eq!(bdd1.clone() & bdd2.clone(), bdd1.and(&bdd2));
719        let mut bdd3 = bdd1.clone();
720        bdd3 &= bdd2.clone();
721        assert_eq!(bdd3, bdd1.and(&bdd2));
722
723        // Test OR operator
724        assert_eq!(&bdd1 | &bdd2, bdd1.or(&bdd2));
725        assert_eq!(bdd1.clone() | bdd2.clone(), bdd1.or(&bdd2));
726        let mut bdd3 = bdd1.clone();
727        bdd3 |= bdd2.clone();
728        assert_eq!(bdd3, bdd1.or(&bdd2));
729    }
730
731    #[cfg(feature = "cudd")]
732    #[test]
733    fn test_cudd_functional() {
734        test_all_functions(BDDManager::new_cudd);
735        test_all_functions(|| BDDManagerConfig::new_cudd().mgr_from_config());
736        test_all_functions(|| BDDManager::new(&BDDManagerConfig::new_cudd()));
737        test_all_functions(BDDManager::default);
738        test_operators(BDDManager::new_cudd);
739    }
740
741    #[cfg(feature = "oxidd")]
742    #[test]
743    fn test_oxidd_functional() {
744        test_all_functions(BDDManager::new_oxidd);
745        test_all_functions(|| BDDManagerConfig::new_oxidd().mgr_from_config());
746        test_all_functions(|| BDDManager::new(&BDDManagerConfig::new_oxidd()));
747        test_all_functions(BDDManager::default);
748        test_operators(BDDManager::new_oxidd);
749    }
750
751    #[test]
752    #[cfg(all(feature = "oxidd", feature = "cudd"))]
753    #[should_panic]
754    fn test_panic_on_bdd_of_mixed_mgrs() {
755        let mgr1 = BDDManager::new_cudd();
756        let mgr2 = BDDManager::new_oxidd();
757        panic_on_bdd_of_mixed_mgrs(mgr1, mgr2);
758    }
759}