taco_threshold_automaton/lia_threshold_automaton/general_to_lia/
remove_boolean_neg.rs1use std::fmt::{Debug, Display};
12
13use crate::expressions::{
14 Atomic, BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression,
15};
16
17impl<T: Atomic> BooleanExpression<T> {
18 pub fn remove_boolean_negations(&self) -> NonNegatedBooleanExpression<T> {
65 match self {
66 BooleanExpression::ComparisonExpression(lhs, comparison_op, rhs) => {
67 NonNegatedBooleanExpression::ComparisonExpression(
68 lhs.clone(),
69 *comparison_op,
70 rhs.clone(),
71 )
72 }
73 BooleanExpression::True => NonNegatedBooleanExpression::True,
74 BooleanExpression::False => NonNegatedBooleanExpression::False,
75 BooleanExpression::BinaryExpression(lhs, op, rhs) => {
76 NonNegatedBooleanExpression::BinaryExpression(
77 Box::new(lhs.remove_boolean_negations()),
78 *op,
79 Box::new(rhs.remove_boolean_negations()),
80 )
81 }
82 BooleanExpression::Not(inner_expr) => inner_expr.negate_expression(),
83 }
84 }
85
86 fn negate_expression(&self) -> NonNegatedBooleanExpression<T> {
92 match self {
93 BooleanExpression::ComparisonExpression(lhs, op, rhs) => {
94 NonNegatedBooleanExpression::ComparisonExpression(
95 lhs.clone(),
96 op.invert(),
97 rhs.clone(),
98 )
99 }
100 BooleanExpression::BinaryExpression(lhs, op, rhs) => {
101 NonNegatedBooleanExpression::BinaryExpression(
102 Box::new(lhs.negate_expression()),
103 op.invert(),
104 Box::new(rhs.negate_expression()),
105 )
106 }
107 BooleanExpression::Not(ex) => ex.remove_boolean_negations(),
108 BooleanExpression::True => NonNegatedBooleanExpression::False,
109 BooleanExpression::False => NonNegatedBooleanExpression::True,
110 }
111 }
112}
113
114impl<T: Atomic> From<BooleanExpression<T>> for NonNegatedBooleanExpression<T> {
115 fn from(val: BooleanExpression<T>) -> Self {
116 val.remove_boolean_negations()
117 }
118}
119
120#[derive(Debug, PartialEq, Clone)]
122pub enum NonNegatedBooleanExpression<T: Atomic> {
123 ComparisonExpression(
125 Box<IntegerExpression<T>>,
126 ComparisonOp,
127 Box<IntegerExpression<T>>,
128 ),
129 BinaryExpression(
131 Box<NonNegatedBooleanExpression<T>>,
132 BooleanConnective,
133 Box<NonNegatedBooleanExpression<T>>,
134 ),
135 True,
137 False,
139}
140
141impl<T: Atomic> Display for NonNegatedBooleanExpression<T> {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 match self {
144 NonNegatedBooleanExpression::ComparisonExpression(lhs, op, rhs) => {
145 write!(f, "{lhs} {op} {rhs}")
146 }
147 NonNegatedBooleanExpression::BinaryExpression(lhs, op, rhs) => {
148 write!(f, "({lhs}) {op} ({rhs})")
149 }
150 NonNegatedBooleanExpression::True => write!(f, "true"),
151 NonNegatedBooleanExpression::False => write!(f, "false"),
152 }
153 }
154}
155
156#[cfg(test)]
157mod test {
158 mod tests_remove_boolean_negations {
161
162 use crate::{
163 expressions::{
164 BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, Variable,
165 },
166 lia_threshold_automaton::general_to_lia::remove_boolean_neg::NonNegatedBooleanExpression,
167 };
168
169 #[test]
170 fn test_remove_negation_comparison_expression1() {
171 let expr = BooleanExpression::Not(Box::new(BooleanExpression::ComparisonExpression(
172 Box::new(IntegerExpression::Atom(Variable::new("x"))),
173 ComparisonOp::Eq,
174 Box::new(IntegerExpression::Atom(Variable::new("y"))),
175 )));
176 let expected_expr = NonNegatedBooleanExpression::ComparisonExpression(
177 Box::new(IntegerExpression::Atom(Variable::new("x"))),
178 ComparisonOp::Neq,
179 Box::new(IntegerExpression::Atom(Variable::new("y"))),
180 );
181
182 let got_expr = expr.remove_boolean_negations();
183
184 assert_eq!(got_expr, expected_expr);
185 }
186
187 #[test]
188 fn test_remove_negation_comparison_expression2() {
189 let expr = BooleanExpression::Not(Box::new(BooleanExpression::ComparisonExpression(
190 Box::new(IntegerExpression::Atom(Variable::new("x"))),
191 ComparisonOp::Neq,
192 Box::new(IntegerExpression::Const(1)),
193 )));
194 let expected_expr = NonNegatedBooleanExpression::ComparisonExpression(
195 Box::new(IntegerExpression::Atom(Variable::new("x"))),
196 ComparisonOp::Eq,
197 Box::new(IntegerExpression::Const(1)),
198 );
199
200 let got_expr = expr.remove_boolean_negations();
201
202 assert_eq!(got_expr, expected_expr);
203 }
204
205 #[test]
206 fn test_remove_negation_boolean_con_inner() {
207 let expr = BooleanExpression::BinaryExpression(
208 Box::new(BooleanExpression::Not(Box::new(
209 BooleanExpression::ComparisonExpression(
210 Box::new(IntegerExpression::Atom(Variable::new("x"))),
211 ComparisonOp::Gt,
212 Box::new(IntegerExpression::Const(5)),
213 ),
214 ))),
215 BooleanConnective::And,
216 Box::new(BooleanExpression::ComparisonExpression(
217 Box::new(IntegerExpression::Atom(Variable::new("y"))),
218 ComparisonOp::Leq,
219 Box::new(IntegerExpression::Atom(Variable::new("z"))),
220 )),
221 );
222
223 let expected_expr = NonNegatedBooleanExpression::BinaryExpression(
224 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
225 Box::new(IntegerExpression::Atom(Variable::new("x"))),
226 ComparisonOp::Leq,
227 Box::new(IntegerExpression::Const(5)),
228 )),
229 BooleanConnective::And,
230 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
231 Box::new(IntegerExpression::Atom(Variable::new("y"))),
232 ComparisonOp::Leq,
233 Box::new(IntegerExpression::Atom(Variable::new("z"))),
234 )),
235 );
236
237 let got_expr = expr.remove_boolean_negations();
238
239 assert_eq!(got_expr, expected_expr);
240 }
241
242 #[test]
243 fn test_remove_negation_boolean_con_outer() {
244 let expr = BooleanExpression::Not(Box::new(BooleanExpression::BinaryExpression(
245 Box::new(BooleanExpression::ComparisonExpression(
246 Box::new(IntegerExpression::Atom(Variable::new("x"))),
247 ComparisonOp::Geq,
248 Box::new(IntegerExpression::Const(5)),
249 )),
250 BooleanConnective::Or,
251 Box::new(BooleanExpression::ComparisonExpression(
252 Box::new(IntegerExpression::Atom(Variable::new("y"))),
253 ComparisonOp::Lt,
254 Box::new(IntegerExpression::Atom(Variable::new("z"))),
255 )),
256 )));
257
258 let expected_expr = NonNegatedBooleanExpression::BinaryExpression(
259 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
260 Box::new(IntegerExpression::Atom(Variable::new("x"))),
261 ComparisonOp::Lt,
262 Box::new(IntegerExpression::Const(5)),
263 )),
264 BooleanConnective::And,
265 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
266 Box::new(IntegerExpression::Atom(Variable::new("y"))),
267 ComparisonOp::Geq,
268 Box::new(IntegerExpression::Atom(Variable::new("z"))),
269 )),
270 );
271
272 let got_expr = expr.remove_boolean_negations();
273
274 assert_eq!(got_expr, expected_expr);
275 }
276
277 #[test]
278 fn test_remove_negation_boolean_con_outer_and_inner() {
279 let expr = BooleanExpression::Not(Box::new(BooleanExpression::BinaryExpression(
280 Box::new(BooleanExpression::Not(Box::new(BooleanExpression::Not(
281 Box::new(BooleanExpression::Not(Box::new(
282 BooleanExpression::ComparisonExpression(
283 Box::new(IntegerExpression::Atom(Variable::new("x"))),
284 ComparisonOp::Neq,
285 Box::new(IntegerExpression::Const(5)),
286 ),
287 ))),
288 )))),
289 BooleanConnective::And,
290 Box::new(BooleanExpression::Not(Box::new(BooleanExpression::Not(
291 Box::new(BooleanExpression::ComparisonExpression(
292 Box::new(IntegerExpression::Atom(Variable::new("y"))),
293 ComparisonOp::Leq,
294 Box::new(IntegerExpression::Atom(Variable::new("z"))),
295 )),
296 )))),
297 )));
298
299 let expected_expr = NonNegatedBooleanExpression::BinaryExpression(
300 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
301 Box::new(IntegerExpression::Atom(Variable::new("x"))),
302 ComparisonOp::Neq,
303 Box::new(IntegerExpression::Const(5)),
304 )),
305 BooleanConnective::Or,
306 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
307 Box::new(IntegerExpression::Atom(Variable::new("y"))),
308 ComparisonOp::Gt,
309 Box::new(IntegerExpression::Atom(Variable::new("z"))),
310 )),
311 );
312
313 let got_expr = NonNegatedBooleanExpression::<Variable>::from(expr);
314
315 assert_eq!(got_expr, expected_expr);
316 }
317
318 #[test]
319 fn test_remove_negation_constants() {
320 let expr: BooleanExpression<Variable> = BooleanExpression::True;
321 assert_eq!(
322 NonNegatedBooleanExpression::<Variable>::from(expr),
323 NonNegatedBooleanExpression::True
324 );
325
326 let expr = BooleanExpression::<Variable>::False;
327 assert_eq!(
328 NonNegatedBooleanExpression::<Variable>::from(expr),
329 NonNegatedBooleanExpression::False
330 );
331
332 let expr = BooleanExpression::Not(Box::new(BooleanExpression::<Variable>::True));
333 assert_eq!(
334 NonNegatedBooleanExpression::<Variable>::from(expr),
335 NonNegatedBooleanExpression::False
336 );
337
338 let expr = BooleanExpression::Not(Box::new(BooleanExpression::<Variable>::False));
339 assert_eq!(
340 NonNegatedBooleanExpression::<Variable>::from(expr),
341 NonNegatedBooleanExpression::True
342 );
343 }
344
345 #[test]
346 fn test_display_non_negated_boolean_expression() {
347 let expr = NonNegatedBooleanExpression::ComparisonExpression(
348 Box::new(IntegerExpression::Atom(Variable::new("x"))),
349 ComparisonOp::Eq,
350 Box::new(IntegerExpression::Const(5)),
351 );
352
353 let expected_str = "x == 5";
354 assert_eq!(expr.to_string(), expected_str);
355
356 let expr = NonNegatedBooleanExpression::BinaryExpression(
357 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
358 Box::new(IntegerExpression::Atom(Variable::new("x"))),
359 ComparisonOp::Eq,
360 Box::new(IntegerExpression::Const(5)),
361 )),
362 BooleanConnective::And,
363 Box::new(NonNegatedBooleanExpression::ComparisonExpression(
364 Box::new(IntegerExpression::Atom(Variable::new("y"))),
365 ComparisonOp::Neq,
366 Box::new(IntegerExpression::Const(3)),
367 )),
368 );
369
370 let expected_str = "(x == 5) && (y != 3)";
371 assert_eq!(expr.to_string(), expected_str);
372
373 let expr: NonNegatedBooleanExpression<Variable> = NonNegatedBooleanExpression::True;
374 let expected_str = "true";
375 assert_eq!(expr.to_string(), expected_str);
376
377 let expr: NonNegatedBooleanExpression<Variable> = NonNegatedBooleanExpression::False;
378 let expected_str = "false";
379 assert_eq!(expr.to_string(), expected_str);
380 }
381 }
382}