espresso_logic/expression/
operators.rs

1//! Operator overloading and boolean operations for boolean expressions
2
3use super::manager::{FALSE_NODE, TRUE_NODE};
4use super::BoolExpr;
5use std::ops::{Add, Mul, Not};
6use std::sync::{Arc, OnceLock};
7
8/// Logical AND operator for references: `&a * &b`
9///
10/// Implements the `*` operator for boolean expressions using references.
11/// This is the most efficient form as it avoids unnecessary cloning.
12///
13/// # Examples
14///
15/// ```
16/// use espresso_logic::BoolExpr;
17///
18/// let a = BoolExpr::variable("a");
19/// let b = BoolExpr::variable("b");
20/// let result = &a * &b;  // Equivalent to a.and(&b)
21/// ```
22impl Mul for &BoolExpr {
23    type Output = BoolExpr;
24
25    fn mul(self, rhs: &BoolExpr) -> BoolExpr {
26        self.and(rhs)
27    }
28}
29
30/// Logical AND operator: `a * b` (delegates to reference version)
31///
32/// Implements the `*` operator for owned boolean expressions.
33/// Note: Using references (`&a * &b`) is preferred for efficiency.
34///
35/// # Examples
36///
37/// ```
38/// use espresso_logic::BoolExpr;
39///
40/// let a = BoolExpr::variable("a");
41/// let b = BoolExpr::variable("b");
42/// // Both work, but references are preferred
43/// let result1 = a.clone() * b.clone();
44/// let result2 = &a * &b;
45/// ```
46impl Mul for BoolExpr {
47    type Output = BoolExpr;
48
49    fn mul(self, rhs: BoolExpr) -> BoolExpr {
50        self.and(&rhs)
51    }
52}
53
54/// Logical OR operator for references: `&a + &b`
55///
56/// Implements the `+` operator for boolean expressions using references.
57/// This is the most efficient form as it avoids unnecessary cloning.
58///
59/// # Examples
60///
61/// ```
62/// use espresso_logic::BoolExpr;
63///
64/// let a = BoolExpr::variable("a");
65/// let b = BoolExpr::variable("b");
66/// let result = &a + &b;  // Equivalent to a.or(&b)
67/// ```
68impl Add for &BoolExpr {
69    type Output = BoolExpr;
70
71    fn add(self, rhs: &BoolExpr) -> BoolExpr {
72        self.or(rhs)
73    }
74}
75
76/// Logical OR operator: `a + b` (delegates to reference version)
77///
78/// Implements the `+` operator for owned boolean expressions.
79/// Note: Using references (`&a + &b`) is preferred for efficiency.
80///
81/// # Examples
82///
83/// ```
84/// use espresso_logic::BoolExpr;
85///
86/// let a = BoolExpr::variable("a");
87/// let b = BoolExpr::variable("b");
88/// // Both work, but references are preferred
89/// let result1 = a.clone() + b.clone();
90/// let result2 = &a + &b;
91/// ```
92impl Add for BoolExpr {
93    type Output = BoolExpr;
94
95    fn add(self, rhs: BoolExpr) -> BoolExpr {
96        self.or(&rhs)
97    }
98}
99
100/// Logical NOT operator for references: `!&a`
101///
102/// Implements the `!` operator for boolean expressions using references.
103/// This is the most efficient form as it avoids unnecessary cloning.
104///
105/// # Examples
106///
107/// ```
108/// use espresso_logic::BoolExpr;
109///
110/// let a = BoolExpr::variable("a");
111/// let result = !&a;  // Equivalent to a.not()
112/// ```
113impl Not for &BoolExpr {
114    type Output = BoolExpr;
115
116    fn not(self) -> BoolExpr {
117        BoolExpr::not(self)
118    }
119}
120
121/// Logical NOT operator: `!a` (delegates to reference version)
122///
123/// Implements the `!` operator for owned boolean expressions.
124/// Note: Using references (`!&a`) is preferred for efficiency when the
125/// original expression is still needed.
126///
127/// # Examples
128///
129/// ```
130/// use espresso_logic::BoolExpr;
131///
132/// let a = BoolExpr::variable("a");
133/// // Both work, but references are preferred if you need 'a' later
134/// let result1 = !a.clone();
135/// let result2 = !&a;
136/// ```
137impl Not for BoolExpr {
138    type Output = BoolExpr;
139
140    fn not(self) -> BoolExpr {
141        BoolExpr::not(&self)
142    }
143}
144
145// Boolean operation methods
146impl BoolExpr {
147    /// Logical AND: create a new expression that is the conjunction of this and another
148    ///
149    /// Computes the conjunction using the BDD ITE operation:
150    /// `and(f, g) = ite(f, g, false)`
151    pub fn and(&self, other: &BoolExpr) -> BoolExpr {
152        // Use ITE: and(f, g) = ite(f, g, false)
153        let manager = Arc::clone(&self.manager);
154        let result = manager
155            .write()
156            .unwrap()
157            .ite(self.root, other.root, FALSE_NODE);
158        BoolExpr {
159            manager,
160            root: result,
161            dnf_cache: OnceLock::new(),
162            ast_cache: OnceLock::new(),
163        }
164    }
165
166    /// Logical OR: create a new expression that is the disjunction of this and another
167    ///
168    /// Computes the disjunction using the BDD ITE operation:
169    /// `or(f, g) = ite(f, true, g)`
170    pub fn or(&self, other: &BoolExpr) -> BoolExpr {
171        // Use ITE: or(f, g) = ite(f, true, g)
172        let manager = Arc::clone(&self.manager);
173        let result = manager
174            .write()
175            .unwrap()
176            .ite(self.root, TRUE_NODE, other.root);
177        BoolExpr {
178            manager,
179            root: result,
180            dnf_cache: OnceLock::new(),
181            ast_cache: OnceLock::new(),
182        }
183    }
184
185    /// Logical NOT: create a new expression that is the negation of this one
186    ///
187    /// Computes the negation using the BDD ITE operation:
188    /// `not(f) = ite(f, false, true)`
189    pub fn not(&self) -> BoolExpr {
190        // Use ITE: not(f) = ite(f, false, true)
191        let manager = Arc::clone(&self.manager);
192        let result = manager
193            .write()
194            .unwrap()
195            .ite(self.root, FALSE_NODE, TRUE_NODE);
196        BoolExpr {
197            manager,
198            root: result,
199            dnf_cache: OnceLock::new(),
200            ast_cache: OnceLock::new(),
201        }
202    }
203}