1use std::fmt;
14
15use taco_threshold_automaton::expressions::{
16 ComparisonOp, IntegerExpression, Location, Parameter, Variable,
17};
18
19use super::ELTLExpression;
20
21#[derive(Debug, PartialEq, Clone)]
23pub enum NonNegatedELTLExpression {
24 Globally(Box<NonNegatedELTLExpression>),
26 Eventually(Box<NonNegatedELTLExpression>),
28 And(Box<NonNegatedELTLExpression>, Box<NonNegatedELTLExpression>),
30 Or(Box<NonNegatedELTLExpression>, Box<NonNegatedELTLExpression>),
32 LocationExpr(
34 Box<IntegerExpression<Location>>,
35 ComparisonOp,
36 Box<IntegerExpression<Location>>,
37 ),
38 VariableExpr(
40 Box<IntegerExpression<Variable>>,
41 ComparisonOp,
42 Box<IntegerExpression<Variable>>,
43 ),
44 ParameterExpr(
46 Box<IntegerExpression<Parameter>>,
47 ComparisonOp,
48 Box<IntegerExpression<Parameter>>,
49 ),
50 True,
52 False,
54}
55
56impl ELTLExpression {
57 pub fn remove_negations(&self) -> NonNegatedELTLExpression {
104 match self {
105 ELTLExpression::Implies(lhs, rhs) => {
106 let lhs = lhs.negate_expression();
108 let rhs = rhs.remove_negations();
109
110 NonNegatedELTLExpression::Or(Box::new(lhs), Box::new(rhs))
111 }
112 ELTLExpression::Globally(expr) => {
113 let expr = expr.remove_negations();
114 NonNegatedELTLExpression::Globally(Box::new(expr))
115 }
116 ELTLExpression::Eventually(expr) => {
117 let expr = expr.remove_negations();
118 NonNegatedELTLExpression::Eventually(Box::new(expr))
119 }
120 ELTLExpression::And(lhs, rhs) => {
121 let lhs = lhs.remove_negations();
122 let rhs = rhs.remove_negations();
123 NonNegatedELTLExpression::And(Box::new(lhs), Box::new(rhs))
124 }
125 ELTLExpression::Or(lhs, rhs) => {
126 let lhs = lhs.remove_negations();
127 let rhs = rhs.remove_negations();
128 NonNegatedELTLExpression::Or(Box::new(lhs), Box::new(rhs))
129 }
130 ELTLExpression::Not(expr) => expr.negate_expression(),
131 ELTLExpression::LocationExpr(lhs, comparison_op, rhs) => {
132 let lhs = lhs.clone();
133 let rhs = rhs.clone();
134 NonNegatedELTLExpression::LocationExpr(lhs, *comparison_op, rhs)
135 }
136 ELTLExpression::VariableExpr(lhs, comparison_op, rhs) => {
137 let lhs = lhs.clone();
138 let rhs = rhs.clone();
139 NonNegatedELTLExpression::VariableExpr(lhs, *comparison_op, rhs)
140 }
141 ELTLExpression::ParameterExpr(lhs, comparison_op, rhs) => {
142 let lhs = lhs.clone();
143 let rhs = rhs.clone();
144 NonNegatedELTLExpression::ParameterExpr(lhs, *comparison_op, rhs)
145 }
146 ELTLExpression::True => NonNegatedELTLExpression::True,
147 ELTLExpression::False => NonNegatedELTLExpression::False,
148 }
149 }
150
151 fn negate_expression(&self) -> NonNegatedELTLExpression {
152 match self {
153 ELTLExpression::Implies(lhs, rhs) => {
154 let lhs = lhs.remove_negations();
156 let rhs = rhs.negate_expression();
157
158 NonNegatedELTLExpression::And(Box::new(lhs), Box::new(rhs))
159 }
160 ELTLExpression::Globally(expr) => {
161 let expr = expr.negate_expression();
162 NonNegatedELTLExpression::Eventually(Box::new(expr))
163 }
164 ELTLExpression::Eventually(expr) => {
165 let expr = expr.negate_expression();
166 NonNegatedELTLExpression::Globally(Box::new(expr))
167 }
168 ELTLExpression::And(lhs, rhs) => {
169 let lhs = lhs.negate_expression();
170 let rhs = rhs.negate_expression();
171 NonNegatedELTLExpression::Or(Box::new(lhs), Box::new(rhs))
172 }
173 ELTLExpression::Or(lhs, rhs) => {
174 let lhs = lhs.negate_expression();
175 let rhs = rhs.negate_expression();
176 NonNegatedELTLExpression::And(Box::new(lhs), Box::new(rhs))
177 }
178 ELTLExpression::Not(expr) => expr.remove_negations(),
179 ELTLExpression::LocationExpr(lhs, op, rhs) => {
180 let lhs = lhs.clone();
181 let rhs = rhs.clone();
182 NonNegatedELTLExpression::LocationExpr(lhs, op.invert(), rhs)
183 }
184 ELTLExpression::VariableExpr(lhs, op, rhs) => {
185 let lhs = lhs.clone();
186 let rhs = rhs.clone();
187 NonNegatedELTLExpression::VariableExpr(lhs, op.invert(), rhs)
188 }
189 ELTLExpression::ParameterExpr(lhs, op, rhs) => {
190 let lhs = lhs.clone();
191 let rhs = rhs.clone();
192 NonNegatedELTLExpression::ParameterExpr(lhs, op.invert(), rhs)
193 }
194 ELTLExpression::True => NonNegatedELTLExpression::False,
195 ELTLExpression::False => NonNegatedELTLExpression::True,
196 }
197 }
198}
199
200impl From<ELTLExpression> for NonNegatedELTLExpression {
201 fn from(value: ELTLExpression) -> Self {
202 value.remove_negations()
203 }
204}
205
206impl fmt::Display for NonNegatedELTLExpression {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 match self {
209 NonNegatedELTLExpression::Globally(expression) => write!(f, "[]({expression})"),
210 NonNegatedELTLExpression::Eventually(expression) => write!(f, "<>({expression})"),
211 NonNegatedELTLExpression::And(lhs, rhs) => write!(f, "({lhs}) && ({rhs})"),
212 NonNegatedELTLExpression::Or(lhs, rhs) => write!(f, "({lhs}) || ({rhs})"),
213 NonNegatedELTLExpression::LocationExpr(lhs, op, rhs) => write!(f, "{lhs} {op} {rhs}"),
214 NonNegatedELTLExpression::VariableExpr(lhs, op, rhs) => write!(f, "{lhs} {op} {rhs}"),
215 NonNegatedELTLExpression::ParameterExpr(lhs, op, rhs) => write!(f, "{lhs} {op} {rhs}"),
216 NonNegatedELTLExpression::True => write!(f, "true"),
217 NonNegatedELTLExpression::False => write!(f, "false"),
218 }
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use taco_threshold_automaton::expressions::{ComparisonOp, IntegerExpression, Variable};
226
227 #[test]
228 fn test_remove_negations_trivial() {
229 let ltl = ELTLExpression::True;
230 let non_negated_ltl = ltl.remove_negations();
231 let expected = NonNegatedELTLExpression::True;
232 assert_eq!(non_negated_ltl, expected);
233
234 let ltl = ELTLExpression::False;
235 let non_negated_ltl = ltl.remove_negations();
236 let expected = NonNegatedELTLExpression::False;
237 assert_eq!(non_negated_ltl, expected);
238
239 let ltl = ELTLExpression::Not(Box::new(ELTLExpression::False));
240 let non_negated_ltl = ltl.remove_negations();
241 let expected = NonNegatedELTLExpression::True;
242 assert_eq!(non_negated_ltl, expected);
243
244 let ltl = ELTLExpression::Not(Box::new(ELTLExpression::True));
245 let non_negated_ltl = ltl.remove_negations();
246 let expected = NonNegatedELTLExpression::False;
247 assert_eq!(non_negated_ltl, expected);
248 }
249
250 #[test]
251 fn test_remove_negations() {
252 let ltl = ELTLExpression::Implies(
254 Box::new(ELTLExpression::LocationExpr(
255 Box::new(IntegerExpression::Const(1)),
256 ComparisonOp::Eq,
257 Box::new(IntegerExpression::Const(2)),
258 )),
259 Box::new(ELTLExpression::VariableExpr(
260 Box::new(IntegerExpression::Atom(Variable::new("x"))),
261 ComparisonOp::Eq,
262 Box::new(IntegerExpression::Const(1)),
263 )),
264 );
265
266 let non_negated_ltl = ltl.remove_negations();
267
268 let expected = NonNegatedELTLExpression::Or(
270 Box::new(NonNegatedELTLExpression::LocationExpr(
271 Box::new(IntegerExpression::Const(1)),
272 ComparisonOp::Neq,
273 Box::new(IntegerExpression::Const(2)),
274 )),
275 Box::new(NonNegatedELTLExpression::VariableExpr(
276 Box::new(IntegerExpression::Atom(Variable::new("x"))),
277 ComparisonOp::Eq,
278 Box::new(IntegerExpression::Const(1)),
279 )),
280 );
281
282 assert_eq!(non_negated_ltl, expected);
283 }
284
285 #[test]
286 fn test_remove_negations_nested() {
287 let ltl = ELTLExpression::Not(Box::new(ELTLExpression::And(
289 Box::new(ELTLExpression::Globally(Box::new(
290 ELTLExpression::LocationExpr(
291 Box::new(IntegerExpression::Const(1)),
292 ComparisonOp::Eq,
293 Box::new(IntegerExpression::Const(2)),
294 ),
295 ))),
296 Box::new(ELTLExpression::Eventually(Box::new(
297 ELTLExpression::VariableExpr(
298 Box::new(IntegerExpression::Atom(Variable::new("x"))),
299 ComparisonOp::Eq,
300 Box::new(IntegerExpression::Const(1)),
301 ),
302 ))),
303 )));
304
305 let non_negated_ltl = ltl.remove_negations();
306
307 let expected = NonNegatedELTLExpression::Or(
309 Box::new(NonNegatedELTLExpression::Eventually(Box::new(
310 NonNegatedELTLExpression::LocationExpr(
311 Box::new(IntegerExpression::Const(1)),
312 ComparisonOp::Neq,
313 Box::new(IntegerExpression::Const(2)),
314 ),
315 ))),
316 Box::new(NonNegatedELTLExpression::Globally(Box::new(
317 NonNegatedELTLExpression::VariableExpr(
318 Box::new(IntegerExpression::Atom(Variable::new("x"))),
319 ComparisonOp::Neq,
320 Box::new(IntegerExpression::Const(1)),
321 ),
322 ))),
323 );
324
325 assert_eq!(non_negated_ltl, expected);
326 }
327
328 #[test]
329 fn test_negated_or() {
330 let ltl = ELTLExpression::Globally(Box::new(ELTLExpression::Eventually(Box::new(
332 ELTLExpression::Not(Box::new(ELTLExpression::Or(
333 Box::new(ELTLExpression::LocationExpr(
334 Box::new(IntegerExpression::Const(1)),
335 ComparisonOp::Eq,
336 Box::new(IntegerExpression::Const(2)),
337 )),
338 Box::new(ELTLExpression::Not(Box::new(ELTLExpression::LocationExpr(
339 Box::new(IntegerExpression::Atom(Location::new("x"))),
340 ComparisonOp::Eq,
341 Box::new(IntegerExpression::Const(1)),
342 )))),
343 ))),
344 ))));
345 let non_negated_ltl = ltl.remove_negations();
346
347 let expected = NonNegatedELTLExpression::Globally(Box::new(
349 NonNegatedELTLExpression::Eventually(Box::new(NonNegatedELTLExpression::And(
350 Box::new(NonNegatedELTLExpression::LocationExpr(
351 Box::new(IntegerExpression::Const(1)),
352 ComparisonOp::Neq,
353 Box::new(IntegerExpression::Const(2)),
354 )),
355 Box::new(NonNegatedELTLExpression::LocationExpr(
356 Box::new(IntegerExpression::Atom(Location::new("x"))),
357 ComparisonOp::Eq,
358 Box::new(IntegerExpression::Const(1)),
359 )),
360 ))),
361 ));
362
363 assert_eq!(non_negated_ltl, expected);
364 }
365
366 #[test]
367 fn test_and_or() {
368 let ltl = ELTLExpression::Or(
370 Box::new(ELTLExpression::And(
371 Box::new(ELTLExpression::LocationExpr(
372 Box::new(IntegerExpression::Const(1)),
373 ComparisonOp::Eq,
374 Box::new(IntegerExpression::Const(2)),
375 )),
376 Box::new(ELTLExpression::VariableExpr(
377 Box::new(IntegerExpression::Atom(Variable::new("x"))),
378 ComparisonOp::Eq,
379 Box::new(IntegerExpression::Const(1)),
380 )),
381 )),
382 Box::new(ELTLExpression::LocationExpr(
383 Box::new(IntegerExpression::Atom(Location::new("y"))),
384 ComparisonOp::Eq,
385 Box::new(IntegerExpression::Const(2)),
386 )),
387 );
388
389 let non_negated_ltl = ltl.remove_negations();
390
391 let expected = NonNegatedELTLExpression::Or(
393 Box::new(NonNegatedELTLExpression::And(
394 Box::new(NonNegatedELTLExpression::LocationExpr(
395 Box::new(IntegerExpression::Const(1)),
396 ComparisonOp::Eq,
397 Box::new(IntegerExpression::Const(2)),
398 )),
399 Box::new(NonNegatedELTLExpression::VariableExpr(
400 Box::new(IntegerExpression::Atom(Variable::new("x"))),
401 ComparisonOp::Eq,
402 Box::new(IntegerExpression::Const(1)),
403 )),
404 )),
405 Box::new(NonNegatedELTLExpression::LocationExpr(
406 Box::new(IntegerExpression::Atom(Location::new("y"))),
407 ComparisonOp::Eq,
408 Box::new(IntegerExpression::Const(2)),
409 )),
410 );
411
412 assert_eq!(non_negated_ltl, expected);
413 }
414
415 #[test]
416 fn test_double_neg() {
417 let eltl = ELTLExpression::Not(Box::new(ELTLExpression::Not(Box::new(
419 ELTLExpression::And(
420 Box::new(ELTLExpression::LocationExpr(
421 Box::new(IntegerExpression::Atom(Location::new("l"))),
422 ComparisonOp::Lt,
423 Box::new(IntegerExpression::Const(1)),
424 )),
425 Box::new(ELTLExpression::VariableExpr(
426 Box::new(IntegerExpression::Atom(Variable::new("v"))),
427 ComparisonOp::Lt,
428 Box::new(IntegerExpression::Const(3)),
429 )),
430 ),
431 ))));
432
433 let non_negated_ltl = eltl.remove_negations();
434
435 let expected = NonNegatedELTLExpression::And(
437 Box::new(NonNegatedELTLExpression::LocationExpr(
438 Box::new(IntegerExpression::Atom(Location::new("l"))),
439 ComparisonOp::Lt,
440 Box::new(IntegerExpression::Const(1)),
441 )),
442 Box::new(NonNegatedELTLExpression::VariableExpr(
443 Box::new(IntegerExpression::Atom(Variable::new("v"))),
444 ComparisonOp::Lt,
445 Box::new(IntegerExpression::Const(3)),
446 )),
447 );
448
449 assert_eq!(non_negated_ltl, expected);
450 }
451}