espresso_logic/expression/
conversions.rs

1//! Conversions for BoolExpr
2//!
3//! This module provides conversion implementations between `BoolExpr` and `Dnf`.
4//!
5//! **Note (v3.1.1+):** `BoolExpr` and `Bdd` were unified—`Bdd` is now a type alias for `BoolExpr`.
6//! All boolean expressions use BDD as their internal representation. The conversions extract
7//! DNF from the internal BDD or create new BDD-backed expressions from DNF.
8
9use crate::cover::Dnf;
10use crate::expression::BoolExpr;
11
12// ============================================================================
13// Blanket Conversions TO Dnf
14// ============================================================================
15
16/// Convert BoolExpr to DNF
17///
18/// **Note (v3.1.1+):** BoolExpr IS a BDD internally. This extracts DNF from the internal
19/// BDD representation, ensuring canonical form and automatic optimisations.
20/// Uses caching to avoid expensive BDD traversal.
21impl From<BoolExpr> for Dnf {
22    fn from(expr: BoolExpr) -> Self {
23        expr.get_or_create_dnf()
24    }
25}
26
27/// Convert &BoolExpr to DNF
28///
29/// **Note (v3.1.1+):** BoolExpr IS a BDD internally. This extracts DNF from the internal
30/// BDD representation. Uses caching to avoid expensive BDD traversal.
31impl From<&BoolExpr> for Dnf {
32    fn from(expr: &BoolExpr) -> Self {
33        expr.get_or_create_dnf()
34    }
35}
36
37// Note (v3.1.1+): Bdd is a type alias for BoolExpr (unified architecture).
38// From<Bdd> implementations are covered by From<BoolExpr> above.
39
40// ============================================================================
41// Conversions FROM Dnf
42// ============================================================================
43
44/// Convert DNF to BoolExpr
45///
46/// **Note (v3.1.1+):** Creates a new BoolExpr with BDD as internal representation.
47/// Reconstructs a boolean expression in DNF form (OR of AND terms), which is then
48/// converted to BDD representation internally.
49///
50/// # Examples
51///
52/// ```
53/// use espresso_logic::{BoolExpr, Dnf};
54///
55/// let a = BoolExpr::variable("a");
56/// let b = BoolExpr::variable("b");
57/// let expr = a.and(&b);
58///
59/// let dnf = Dnf::from(&expr);
60/// let expr2 = BoolExpr::from(dnf);
61///
62/// assert!(expr.equivalent_to(&expr2));
63/// ```
64impl From<Dnf> for BoolExpr {
65    fn from(dnf: Dnf) -> Self {
66        if dnf.is_empty() {
67            return BoolExpr::constant(false);
68        }
69
70        // Convert each cube to a product term
71        let mut terms = Vec::new();
72        for cube in dnf.cubes() {
73            if cube.is_empty() {
74                // Empty cube means tautology (all variables are don't-care)
75                terms.push(BoolExpr::constant(true));
76            } else {
77                // Build AND of all literals in this cube
78                let factors: Vec<BoolExpr> = cube
79                    .iter()
80                    .map(|(var, &polarity)| {
81                        let v = BoolExpr::variable(var);
82                        if polarity {
83                            v
84                        } else {
85                            v.not()
86                        }
87                    })
88                    .collect();
89
90                let product = factors.into_iter().reduce(|acc, f| acc.and(&f)).unwrap();
91                terms.push(product);
92            }
93        }
94
95        // OR all terms together
96        let expr = terms.into_iter().reduce(|acc, t| acc.or(&t)).unwrap();
97
98        // Cache the source DNF (likely minimised from Espresso)
99        let _ = expr.dnf_cache.set(dnf);
100
101        expr
102    }
103}
104
105// Note: Bdd is now a type alias for BoolExpr, so From<Dnf> for Bdd
106// is covered by the From<Dnf> for BoolExpr implementation above.