taco_threshold_automaton/lia_threshold_automaton/
properties.rs

1//! Implementation of functionality for [`LIAThresholdAutomaton`], [`LIARule`]
2//! and the guard types
3//!
4//! This module contains the structs and traits for the linear integer arithmetic
5//! threshold automaton representation.
6
7use std::collections::HashSet;
8
9use std::error::Error;
10use std::fmt::Display;
11
12use taco_display_utils::{
13    display_iterator_stable_order, indent_all, join_iterator, join_iterator_and_add_back,
14};
15
16use crate::expressions::fraction::Fraction;
17use crate::expressions::{
18    Atomic, BooleanConnective, BooleanExpression, IntegerExpression, Parameter, Variable,
19};
20use crate::expressions::{IsDeclared, Location};
21use crate::general_threshold_automaton::{Action, GeneralThresholdAutomaton, Rule};
22use crate::lia_threshold_automaton::integer_thresholds::{
23    IntoNoDivBooleanExpr, Threshold, ThresholdConstraint, ThresholdConstraintOver, WeightedSum,
24};
25use crate::{LocationConstraint, RuleDefinition, ThresholdAutomaton, VariableConstraint};
26
27use super::{
28    ComparisonConstraint, ComparisonConstraintCreationError, LIARule, LIAThresholdAutomaton,
29    LIATransformationError, LIAVariableConstraint, SingleAtomConstraint, SumAtomConstraint,
30    SumVarConstraintCreationError,
31};
32
33impl LIAThresholdAutomaton {
34    /// Get the maximum number of thresholds in a single rule of the threshold
35    /// automaton
36    pub fn get_max_number_of_thresholds_per_rule(&self) -> u32 {
37        self.rules
38            .values()
39            .flatten()
40            .map(|rule| rule.number_of_thresholds())
41            .max()
42            .unwrap_or(0)
43    }
44
45    /// Get a reference to the [`GeneralThresholdAutomaton`] this automaton has
46    /// been derived from
47    pub fn get_ta(&self) -> &GeneralThresholdAutomaton {
48        &self.ta
49    }
50
51    /// Check if the canonical threshold automaton contains a guard over as sum
52    /// of variables, i.e., some guard contains a [`SumAtomConstraint`]
53    pub fn has_sum_var_threshold_guard(&self) -> bool {
54        self.rules
55            .values()
56            .flatten()
57            .any(|r| r.guard().has_sum_var())
58    }
59
60    /// Check if the canonical threshold automaton has a comparison guard,i.e.,
61    /// some guard contains a [`ComparisonConstraint`]
62    pub fn has_comparison_guard(&self) -> bool {
63        self.rules
64            .values()
65            .flatten()
66            .any(|r| r.guard().has_comparison_guard())
67    }
68
69    /// Get all appearing [`SingleAtomConstraint`]s
70    ///
71    /// This function returns all appearing [`SingleAtomConstraint`]s in the
72    /// rules of a threshold automaton. The constraints are deduplicated.
73    pub fn get_single_var_constraints_rules(&self) -> HashSet<SingleAtomConstraint<Variable>> {
74        self.rules()
75            .flat_map(|r| r.guard().get_single_var_constraints())
76            .collect()
77    }
78
79    /// Get all appearing [`SumAtomConstraint`]s
80    ///
81    /// This function returns all appearing [`SumAtomConstraint`]s in the
82    /// rules of a threshold automaton. The constraints are deduplicated.
83    pub fn get_sum_var_constraints(&self) -> HashSet<SumAtomConstraint<Variable>> {
84        self.rules()
85            .flat_map(|r| r.guard().get_multi_variable_guards())
86            .collect()
87    }
88
89    /// Get all appearing [`ComparisonConstraint`]s
90    ///
91    /// This function returns all appearing [`ComparisonConstraint`]s in the
92    ///rules of a threshold automaton. The constraints are deduplicated.
93    pub fn get_comparison_guards(&self) -> HashSet<ComparisonConstraint<Variable>> {
94        self.rules()
95            .flat_map(|r| r.guard().get_comparison_guards())
96            .collect()
97    }
98
99    /// Get the number of distinct thresholds appearing in the definition of the
100    /// threshold automaton
101    pub fn get_thresholds(&self) -> HashSet<Threshold> {
102        self.rules()
103            .flat_map(|r| r.get_distinct_thresholds())
104            .collect()
105    }
106
107    /// Get the [`Rule`] this rule has been derived from
108    pub fn get_derived_rule(&self, r: &LIARule) -> Rule {
109        self.ta
110            .rules()
111            .find(|rule| {
112                rule.id() == r.id() && rule.source() == r.source() && rule.target() == r.target()
113            })
114            .expect("Could not find derived rule")
115            .clone()
116    }
117}
118
119impl ThresholdAutomaton for LIAThresholdAutomaton {
120    type Rule = LIARule;
121    type InitialVariableConstraintType = LIAVariableConstraint;
122
123    fn name(&self) -> &str {
124        self.ta.name()
125    }
126
127    fn variables(&self) -> impl Iterator<Item = &Variable> {
128        // TODO: remove as soon as proper ordering is implemented
129        self.ta
130            .variables()
131            .chain(self.additional_vars_for_sums.keys())
132    }
133
134    fn parameters(&self) -> impl Iterator<Item = &Parameter> {
135        self.ta.parameters()
136    }
137
138    fn locations(&self) -> impl Iterator<Item = &Location> {
139        self.ta.locations()
140    }
141
142    fn resilience_conditions(
143        &self,
144    ) -> impl Iterator<Item = &crate::expressions::BooleanExpression<Parameter>> {
145        self.ta.resilience_conditions()
146    }
147
148    fn can_be_initial_location(&self, location: &Location) -> bool {
149        self.ta.can_be_initial_location(location)
150    }
151
152    fn rules(&self) -> impl Iterator<Item = &Self::Rule> {
153        self.rules.values().flatten()
154    }
155
156    fn incoming_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
157        self.rules().filter(move |r| r.target() == location)
158    }
159
160    fn outgoing_rules(&self, location: &Location) -> impl Iterator<Item = &Self::Rule> {
161        self.rules.get(location).into_iter().flatten()
162    }
163
164    fn initial_location_constraints(&self) -> impl Iterator<Item = &LocationConstraint> {
165        self.ta.initial_location_conditions()
166    }
167
168    fn initial_variable_constraints(&self) -> impl Iterator<Item = &LIAVariableConstraint> {
169        self.init_variable_constr.iter()
170    }
171}
172
173// Here we decided to use the bymc format to display a threshold automaton,
174// since it is well documented and easy to read.
175impl Display for LIAThresholdAutomaton {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        let variables = format!(
178            "shared {};",
179            display_iterator_stable_order(self.ta.variables())
180        );
181        let parameters = format!(
182            "parameters {};",
183            display_iterator_stable_order(self.ta.parameters())
184        );
185
186        let rc = format!(
187            "assumptions ({}) {{\n{}}}",
188            self.ta.resilience_conditions().count(),
189            indent_all(join_iterator_and_add_back(
190                self.ta.resilience_conditions(),
191                ";\n"
192            ))
193        );
194
195        let mut loc_str = String::new();
196        let mut locations = self.ta.locations().collect::<Vec<_>>();
197        locations.sort();
198        for (i, loc) in locations.into_iter().enumerate() {
199            loc_str += &format!("{loc}:[{i}];\n");
200        }
201        let locations = format!(
202            "locations ({}) {{\n{}}}",
203            self.ta.locations().count(),
204            indent_all(loc_str)
205        );
206
207        let initial_cond = format!(
208            "inits ({}) {{\n{}}}",
209            self.ta.initial_location_conditions().count() + self.init_variable_constr.len(),
210            indent_all(
211                join_iterator_and_add_back(self.ta.initial_location_conditions(), ";\n")
212                    + &join_iterator_and_add_back(
213                        self.init_variable_constr
214                            .iter()
215                            .map(|cstr| cstr.as_boolean_expr()),
216                        ";\n"
217                    )
218            )
219        );
220
221        let mut sorted_rules_by_id = self.rules.values().flatten().collect::<Vec<_>>();
222        sorted_rules_by_id.sort_by_key(|r| &r.id);
223        let rules = format!(
224            "rules ({}) {{\n{}}}",
225            self.rules.len(),
226            indent_all(join_iterator_and_add_back(
227                sorted_rules_by_id.into_iter(),
228                ";\n"
229            ))
230        );
231
232        let ta_body = format!(
233            "{variables}\n\n{parameters}\n\n{rc}\n\n{locations}\n\n{initial_cond}\n\n{rules}"
234        );
235
236        write!(
237            f,
238            "thresholdAutomaton {} {{\n{}\n}}\n",
239            self.ta.name(),
240            indent_all(ta_body)
241        )
242    }
243}
244
245impl IsDeclared<Parameter> for LIAThresholdAutomaton {
246    fn is_declared(&self, obj: &Parameter) -> bool {
247        self.ta.is_declared(obj)
248    }
249}
250
251impl IsDeclared<Variable> for LIAThresholdAutomaton {
252    fn is_declared(&self, obj: &Variable) -> bool {
253        self.ta.is_declared(obj)
254    }
255}
256
257impl IsDeclared<Location> for LIAThresholdAutomaton {
258    fn is_declared(&self, obj: &Location) -> bool {
259        self.ta.is_declared(obj)
260    }
261}
262
263impl LIARule {
264    /// Get all distinct thresholds appearing in the guard of the rule
265    fn get_distinct_thresholds(&self) -> HashSet<Threshold> {
266        self.guard.get_distinct_thresholds()
267    }
268
269    /// Get the number of thresholds appearing in the guard
270    pub fn number_of_thresholds(&self) -> u32 {
271        self.guard.count_number_of_thresholds()
272    }
273
274    /// Returns the number of updates in the rule
275    pub fn number_of_updates(&self) -> u32 {
276        self.actions.len() as u32
277    }
278}
279
280impl RuleDefinition for LIARule {
281    type Action = Action;
282
283    type Guard = LIAVariableConstraint;
284
285    fn id(&self) -> u32 {
286        self.id
287    }
288
289    fn source(&self) -> &Location {
290        &self.source
291    }
292
293    fn target(&self) -> &Location {
294        &self.target
295    }
296
297    fn guard(&self) -> &Self::Guard {
298        &self.guard
299    }
300
301    fn actions(&self) -> impl Iterator<Item = &Self::Action> {
302        self.actions.iter()
303    }
304}
305
306impl Display for LIARule {
307    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308        let guard_string = self.guard.to_string();
309        let update_string = join_iterator(self.actions.iter(), "; ");
310
311        let rule_body = indent_all(format!("when ( {guard_string} )\ndo {{ {update_string} }}"));
312
313        write!(
314            f,
315            "{}: {} -> {}\n{}",
316            self.id, self.source, self.target, rule_body
317        )
318    }
319}
320
321impl LIAVariableConstraint {
322    /// Get all [`Threshold`]s appearing in the constraint
323    fn get_distinct_thresholds(&self) -> HashSet<Threshold> {
324        match self {
325            LIAVariableConstraint::ComparisonConstraint(cg) => {
326                HashSet::from([cg.get_threshold().clone()])
327            }
328            LIAVariableConstraint::SingleVarConstraint(svg) => {
329                HashSet::from([svg.get_threshold().clone()])
330            }
331            LIAVariableConstraint::SumVarConstraint(mvg) => {
332                HashSet::from([mvg.get_threshold().clone()])
333            }
334            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
335                let mut l_thr = lhs.get_distinct_thresholds();
336                let r_thr = rhs.get_distinct_thresholds();
337                l_thr.extend(r_thr);
338                l_thr
339            }
340            LIAVariableConstraint::True | LIAVariableConstraint::False => HashSet::new(),
341        }
342    }
343
344    /// Count the number of thresholds appearing in the constraint
345    pub fn count_number_of_thresholds(&self) -> u32 {
346        match self {
347            LIAVariableConstraint::ComparisonConstraint(_)
348            | LIAVariableConstraint::SingleVarConstraint(_)
349            | LIAVariableConstraint::SumVarConstraint(_) => 1,
350            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
351                lhs.count_number_of_thresholds() + rhs.count_number_of_thresholds()
352            }
353            LIAVariableConstraint::True => 0,
354            LIAVariableConstraint::False => 0,
355        }
356    }
357
358    /// Check if the guard contains a [`SumAtomConstraint`] in its constraint
359    pub fn has_sum_var(&self) -> bool {
360        match self {
361            LIAVariableConstraint::SumVarConstraint(_) => true,
362            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
363                lhs.has_sum_var() || rhs.has_sum_var()
364            }
365            _ => false,
366        }
367    }
368
369    /// Check if the guard contains a  [`ComparisonConstraint`] in its
370    /// constraint
371    pub fn has_comparison_guard(&self) -> bool {
372        match self {
373            LIAVariableConstraint::ComparisonConstraint(_) => true,
374            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
375                lhs.has_comparison_guard() || rhs.has_comparison_guard()
376            }
377            _ => false,
378        }
379    }
380
381    /// Get all [`SingleAtomConstraint`]s appearing in the constraint
382    pub fn get_single_var_constraints(&self) -> Vec<SingleAtomConstraint<Variable>> {
383        match self {
384            LIAVariableConstraint::SingleVarConstraint(s) => vec![s.clone()],
385            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
386                let mut guards = lhs.get_single_var_constraints();
387                guards.extend(rhs.get_single_var_constraints());
388                guards
389            }
390            LIAVariableConstraint::SumVarConstraint(_)
391            | LIAVariableConstraint::ComparisonConstraint(_)
392            | LIAVariableConstraint::True
393            | LIAVariableConstraint::False => vec![],
394        }
395    }
396
397    /// Get all [`SumAtomConstraint`] appearing in the constraint
398    pub fn get_multi_variable_guards(&self) -> Vec<SumAtomConstraint<Variable>> {
399        match self {
400            LIAVariableConstraint::SumVarConstraint(m) => vec![m.clone()],
401            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
402                let mut guards = lhs.get_multi_variable_guards();
403                guards.extend(rhs.get_multi_variable_guards());
404                guards
405            }
406            LIAVariableConstraint::SingleVarConstraint(_)
407            | LIAVariableConstraint::ComparisonConstraint(_)
408            | LIAVariableConstraint::True
409            | LIAVariableConstraint::False => vec![],
410        }
411    }
412
413    /// Get all [`ComparisonConstraint`] appearing in the constraint
414    pub fn get_comparison_guards(&self) -> Vec<ComparisonConstraint<Variable>> {
415        match self {
416            LIAVariableConstraint::ComparisonConstraint(c) => vec![c.clone()],
417            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
418                let mut guards = lhs.get_comparison_guards();
419                guards.extend(rhs.get_comparison_guards());
420                guards
421            }
422            LIAVariableConstraint::SingleVarConstraint(_)
423            | LIAVariableConstraint::SumVarConstraint(_)
424            | LIAVariableConstraint::True
425            | LIAVariableConstraint::False => vec![],
426        }
427    }
428
429    /// Check if the constraint is an upper guard
430    pub fn is_upper_guard(&self) -> bool {
431        match self {
432            LIAVariableConstraint::ComparisonConstraint(c) => c.is_upper_guard(),
433            LIAVariableConstraint::SingleVarConstraint(s) => s.is_upper_guard(),
434            LIAVariableConstraint::SumVarConstraint(m) => m.is_upper_guard(),
435            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
436                lhs.is_upper_guard() || rhs.is_upper_guard()
437            }
438            LIAVariableConstraint::True | LIAVariableConstraint::False => false,
439        }
440    }
441
442    /// Check if the constraint is a lower guard
443    pub fn is_lower_guard(&self) -> bool {
444        match self {
445            LIAVariableConstraint::ComparisonConstraint(c) => c.is_lower_guard(),
446            LIAVariableConstraint::SingleVarConstraint(s) => s.is_lower_guard(),
447            LIAVariableConstraint::SumVarConstraint(m) => m.is_lower_guard(),
448            LIAVariableConstraint::BinaryGuard(lhs, _, rhs) => {
449                lhs.is_lower_guard() || rhs.is_lower_guard()
450            }
451            LIAVariableConstraint::True | LIAVariableConstraint::False => false,
452        }
453    }
454}
455
456impl VariableConstraint for LIAVariableConstraint {
457    fn as_boolean_expr(&self) -> crate::expressions::BooleanExpression<Variable> {
458        match self {
459            LIAVariableConstraint::ComparisonConstraint(guard) => guard.to_boolean_expr(),
460            LIAVariableConstraint::SingleVarConstraint(guard) => guard.to_boolean_expr(),
461            LIAVariableConstraint::SumVarConstraint(guard) => guard.to_boolean_expr(),
462            LIAVariableConstraint::BinaryGuard(lhs, boolean_connective, rhs) => {
463                let lhs = lhs.as_boolean_expr();
464                let rhs = rhs.as_boolean_expr();
465                BooleanExpression::BinaryExpression(
466                    Box::new(lhs),
467                    *boolean_connective,
468                    Box::new(rhs),
469                )
470            }
471            LIAVariableConstraint::True => BooleanExpression::True,
472            LIAVariableConstraint::False => BooleanExpression::False,
473        }
474    }
475}
476
477impl std::ops::BitAnd for LIAVariableConstraint {
478    type Output = LIAVariableConstraint;
479
480    fn bitand(self, rhs: Self) -> Self::Output {
481        LIAVariableConstraint::BinaryGuard(Box::new(self), BooleanConnective::And, Box::new(rhs))
482    }
483}
484
485impl std::ops::BitOr for LIAVariableConstraint {
486    type Output = LIAVariableConstraint;
487
488    fn bitor(self, rhs: Self) -> Self::Output {
489        LIAVariableConstraint::BinaryGuard(Box::new(self), BooleanConnective::Or, Box::new(rhs))
490    }
491}
492
493impl<T: Atomic> SingleAtomConstraint<T> {
494    /// Create a new single variable constraint
495    pub fn new(atom: T, thr: ThresholdConstraint) -> Self {
496        Self(ThresholdConstraintOver::new(atom, thr))
497    }
498
499    /// Check if the constraint is an upper guard
500    pub fn is_upper_guard(&self) -> bool {
501        self.0.is_upper_guard()
502    }
503
504    /// Check if the guard is a lower guard
505    pub fn is_lower_guard(&self) -> bool {
506        self.0.is_lower_guard()
507    }
508
509    /// Get the variable the constraint constrains
510    pub fn get_atom(&self) -> &T {
511        self.0.get_variable()
512    }
513
514    /// Transform the constraint into a [`BooleanExpression`]
515    pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
516    where
517        T: IntoNoDivBooleanExpr<S>,
518        IntegerExpression<S>: From<T>,
519        S: Atomic,
520    {
521        self.0.encode_to_boolean_expr()
522    }
523
524    /// Get the threshold of the guard
525    pub fn get_threshold(&self) -> &Threshold {
526        self.0.get_threshold()
527    }
528
529    /// Get the threshold constraint of the guard
530    pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
531        self.0.get_threshold_constraint()
532    }
533}
534
535impl<T: Atomic> Display for SingleAtomConstraint<T> {
536    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
537        write!(f, "{}", self.0)
538    }
539}
540
541impl<T: Atomic> SumAtomConstraint<T> {
542    /// Try to create a new [`SumAtomConstraint`]
543    ///
544    /// A [`SumAtomConstraint`] constraints a (weighted) sum of **multiple**
545    /// variables, where all variable weights are positive (or all negative, in
546    /// this case the constraint is transformed such that all variable are
547    /// positive). Any other form would result in a [`ComparisonConstraint`] or
548    /// if there is only a single variable a [`SingleAtomConstraint`].
549    ///
550    /// Returns an [`SumVarConstraintCreationError`] if the coefficients are
551    /// mixed or only a single variable is present.
552    pub fn try_new<F: Into<Fraction>, I: IntoIterator<Item = (T, F)> + Clone>(
553        atoms: I,
554        mut thr: ThresholdConstraint,
555    ) -> Result<Self, SumVarConstraintCreationError> {
556        let mut variables = WeightedSum::new(atoms);
557
558        if (&variables).into_iter().count() == 1 {
559            return Err(SumVarConstraintCreationError::IsSingleAtomConstraint);
560        }
561
562        if (&variables).into_iter().any(|(_, f)| f.is_negative())
563            && (&variables).into_iter().any(|(_, f)| !f.is_negative())
564        {
565            return Err(SumVarConstraintCreationError::IsComparisonConstraint);
566        }
567
568        if (&variables).into_iter().all(|(_, f)| f.is_negative()) {
569            variables.scale(-Fraction::from(1));
570            thr.scale(-Fraction::from(1));
571        }
572
573        let constr = ThresholdConstraintOver::new(variables, thr);
574
575        Ok(Self(constr))
576    }
577
578    /// Check if the guard is an upper guard
579    pub fn is_upper_guard(&self) -> bool {
580        self.0.is_upper_guard()
581    }
582
583    /// Check if the guard is a lower guard
584    pub fn is_lower_guard(&self) -> bool {
585        self.0.is_lower_guard()
586    }
587
588    /// Get the variable
589    pub fn get_atoms(&self) -> &WeightedSum<T> {
590        self.0.get_variable()
591    }
592
593    /// Get boolean expression of the guard
594    pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
595    where
596        T: IntoNoDivBooleanExpr<S>,
597        IntegerExpression<S>: From<T>,
598        S: Atomic,
599    {
600        self.0.encode_to_boolean_expr()
601    }
602
603    /// Get the threshold of the guard
604    pub fn get_threshold(&self) -> &Threshold {
605        self.0.get_threshold()
606    }
607
608    /// Get the threshold constraint of the guard
609    pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
610        self.0.get_threshold_constraint()
611    }
612}
613
614impl<T: Atomic> Display for SumAtomConstraint<T> {
615    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
616        write!(f, "{}", self.0)
617    }
618}
619
620impl Error for SumVarConstraintCreationError {}
621
622impl Display for SumVarConstraintCreationError {
623    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624        match self {
625            SumVarConstraintCreationError::IsComparisonConstraint => {
626                write!(f, "Guard is a ComparisonGuard not a MultiVarThresholdGuard")
627            }
628            SumVarConstraintCreationError::IsSingleAtomConstraint => {
629                write!(
630                    f,
631                    "Guard is a SingleVarThresholdGuard not a MultiVarThresholdGuard"
632                )
633            }
634        }
635    }
636}
637
638impl<T: Atomic> ComparisonConstraint<T> {
639    /// Create a [`ComparisonConstraint`]
640    ///
641    /// A comparison constraint constrains a weighted sum over **multiple**
642    /// variables where at least one has a negative and at least one has a
643    /// positive coefficient.
644    /// In any other case the guard would be a [`SingleAtomConstraint`] or a
645    /// [`SumAtomConstraint`]. In this case a
646    /// [`ComparisonConstraintCreationError`] is returned.
647    pub fn try_new<I: IntoIterator<Item = (T, Fraction)> + Clone>(
648        atoms: I,
649        thr: ThresholdConstraint,
650    ) -> Result<Self, ComparisonConstraintCreationError> {
651        if atoms.clone().into_iter().all(|(_, f)| f.is_negative())
652            || atoms.clone().into_iter().all(|(_, f)| !f.is_negative())
653        {
654            return Err(ComparisonConstraintCreationError::IsSumVarConstraint);
655        }
656
657        let atoms = WeightedSum::new(atoms);
658        let symbolic_constraint = ThresholdConstraintOver::new(atoms, thr);
659
660        Ok(Self(symbolic_constraint))
661    }
662
663    /// Check if the guard is an upper guard
664    pub fn is_upper_guard(&self) -> bool {
665        self.0.is_upper_guard()
666    }
667
668    /// Check if the guard is a lower guard
669    pub fn is_lower_guard(&self) -> bool {
670        self.0.is_lower_guard()
671    }
672
673    /// Get the boolean expression representing the threshold
674    pub fn to_boolean_expr<S>(&self) -> BooleanExpression<S>
675    where
676        T: IntoNoDivBooleanExpr<S>,
677        IntegerExpression<S>: From<T>,
678        S: Atomic,
679    {
680        self.0.encode_to_boolean_expr()
681    }
682
683    /// Get the threshold of the constraint
684    pub fn get_threshold(&self) -> &Threshold {
685        self.0.get_threshold()
686    }
687
688    /// Get the threshold constraint of the constraint
689    pub fn get_threshold_constraint(&self) -> &ThresholdConstraint {
690        self.0.get_threshold_constraint()
691    }
692}
693
694impl<T: Atomic> Display for ComparisonConstraint<T> {
695    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
696        write!(f, "{}", self.0)?;
697
698        Ok(())
699    }
700}
701
702impl Error for ComparisonConstraintCreationError {}
703
704impl Display for ComparisonConstraintCreationError {
705    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
706        match self {
707            ComparisonConstraintCreationError::IsSumVarConstraint => {
708                write!(f, "Guard is a MultiVarThresholdGuard not a ComparisonGuard")
709            }
710        }
711    }
712}
713
714impl Display for LIAVariableConstraint {
715    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
716        match self {
717            LIAVariableConstraint::SumVarConstraint(g) => write!(f, "{g}"),
718            LIAVariableConstraint::BinaryGuard(lhs, op, rhs) => {
719                write!(f, "({lhs}) {op} ({rhs})")
720            }
721            LIAVariableConstraint::True => write!(f, "true"),
722            LIAVariableConstraint::False => write!(f, "false"),
723            LIAVariableConstraint::ComparisonConstraint(g) => write!(f, "{g}"),
724            LIAVariableConstraint::SingleVarConstraint(g) => write!(f, "{g}"),
725        }
726    }
727}
728
729impl Error for LIATransformationError {}
730
731impl Display for LIATransformationError {
732    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
733        match self {
734            LIATransformationError::GuardError(rule, guard, err) => write!(
735                f,
736                "Error while transforming guard {guard} of rule {rule}: {err}"
737            ),
738            LIATransformationError::InitialConstraintError(
739                boolean_expression,
740                guard_rewrite_error,
741            ) => {
742                write!(
743                    f,
744                    "Error while transforming initial constraint {boolean_expression} into linear arithmetic: {guard_rewrite_error}"
745                )
746            }
747        }
748    }
749}
750
751#[cfg(test)]
752mod tests {
753    use std::{
754        collections::{BTreeMap, HashMap, HashSet},
755        vec,
756    };
757
758    use crate::{
759        BooleanVarConstraint, LocationConstraint, ParameterConstraint, RuleDefinition,
760        ThresholdAutomaton, VariableConstraint,
761        expressions::{
762            BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, IntegerOp,
763            Location, Parameter, Variable, fraction::Fraction,
764        },
765        general_threshold_automaton::{
766            Action,
767            builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
768        },
769        lia_threshold_automaton::{
770            ComparisonConstraint, ComparisonConstraintCreationError, ConstraintRewriteError,
771            LIARule, LIAThresholdAutomaton, LIATransformationError, LIAVariableConstraint,
772            SingleAtomConstraint, SumAtomConstraint, SumVarConstraintCreationError, WeightedSum,
773            integer_thresholds::{
774                Threshold, ThresholdCompOp, ThresholdConstraint, ThresholdConstraintOver,
775            },
776        },
777    };
778
779    #[test]
780    fn test_lia_threshold_automaton_getters() {
781        let rule = RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build();
782
783        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
784            .with_parameters(vec![
785                Parameter::new("n"),
786                Parameter::new("t"),
787                Parameter::new("f"),
788            ])
789            .unwrap()
790            .with_variables(vec![
791                Variable::new("var1"),
792                Variable::new("var2"),
793                Variable::new("var3"),
794            ])
795            .unwrap()
796            .with_locations(vec![
797                Location::new("loc1"),
798                Location::new("loc2"),
799                Location::new("loc3"),
800            ])
801            .unwrap()
802            .initialize()
803            .with_rules(vec![
804                rule.clone(),
805                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
806                    .with_guard(
807                        BooleanVarConstraint::ComparisonExpression(
808                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
809                            ComparisonOp::Eq,
810                            Box::new(IntegerExpression::Const(1)),
811                        ) & BooleanVarConstraint::ComparisonExpression(
812                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
813                            ComparisonOp::Eq,
814                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
815                        ),
816                    )
817                    .with_action(Action::new_reset(Variable::new("var3")))
818                    .with_action(
819                        Action::new(
820                            Variable::new("var1"),
821                            IntegerExpression::Atom(Variable::new("var1"))
822                                + IntegerExpression::Const(1),
823                        )
824                        .unwrap(),
825                    )
826                    .build(),
827            ])
828            .unwrap()
829            .with_initial_location_constraint(
830                LocationConstraint::ComparisonExpression(
831                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
832                    ComparisonOp::Eq,
833                    Box::new(
834                        IntegerExpression::Param(Parameter::new("n"))
835                            - IntegerExpression::Param(Parameter::new("f")),
836                    ),
837                ) & LocationConstraint::ComparisonExpression(
838                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
839                    ComparisonOp::Eq,
840                    Box::new(IntegerExpression::Const(0)),
841                ),
842            )
843            .unwrap()
844            .with_initial_variable_constraint(
845                BooleanVarConstraint::ComparisonExpression(
846                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
847                    ComparisonOp::Eq,
848                    Box::new(IntegerExpression::Const(0)),
849                ) & BooleanVarConstraint::ComparisonExpression(
850                    Box::new(IntegerExpression::Atom(Variable::new("var2"))),
851                    ComparisonOp::Eq,
852                    Box::new(IntegerExpression::Const(0)),
853                ),
854            )
855            .unwrap()
856            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
857                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
858                ComparisonOp::Gt,
859                Box::new(
860                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
861                ),
862            ))
863            .unwrap()
864            .build();
865
866        let abstract_rule = LIARule {
867            id: 0,
868            source: Location::new("loc1"),
869            target: Location::new("loc2"),
870            guard: LIAVariableConstraint::True,
871            actions: vec![],
872        };
873
874        let lta = LIAThresholdAutomaton {
875            ta: ta.clone(),
876            rules: HashMap::from([
877                (Location::new("loc1"), vec![abstract_rule.clone()]),
878                (
879                    Location::new("loc2"),
880                    vec![LIARule {
881                        id: 1,
882                        source: Location::new("loc2"),
883                        target: Location::new("loc3"),
884                        guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
885                            ThresholdConstraintOver::new(
886                                WeightedSum::new([(Variable::new("var1"), Fraction::from(1))]),
887                                ThresholdConstraint::new(
888                                    ThresholdCompOp::Lt,
889                                    Vec::<(Parameter, Fraction)>::new(),
890                                    2,
891                                ),
892                            ),
893                        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
894                            ThresholdConstraintOver::new(
895                                WeightedSum::new([(Variable::new("var1"), Fraction::from(1))]),
896                                ThresholdConstraint::new(
897                                    ThresholdCompOp::Geq,
898                                    Vec::<(Parameter, Fraction)>::new(),
899                                    1,
900                                ),
901                            ),
902                        ))) & (LIAVariableConstraint::SingleVarConstraint(
903                            SingleAtomConstraint(ThresholdConstraintOver::new(
904                                Variable::new("var2"),
905                                ThresholdConstraint::new(
906                                    ThresholdCompOp::Lt,
907                                    [(Parameter::new("n"), 1)],
908                                    1,
909                                ),
910                            )),
911                        ) & LIAVariableConstraint::SingleVarConstraint(
912                            SingleAtomConstraint(ThresholdConstraintOver::new(
913                                Variable::new("var2"),
914                                ThresholdConstraint::new(
915                                    ThresholdCompOp::Geq,
916                                    [(Parameter::new("n"), 1)],
917                                    0,
918                                ),
919                            )),
920                        )) | (LIAVariableConstraint::ComparisonConstraint(
921                            ComparisonConstraint(ThresholdConstraintOver::new(
922                                WeightedSum::new([
923                                    (Variable::new("var1"), Fraction::from(1)),
924                                    (Variable::new("var2"), -Fraction::from(1)),
925                                ]),
926                                ThresholdConstraint::new(
927                                    ThresholdCompOp::Lt,
928                                    Vec::<(Parameter, Fraction)>::new(),
929                                    2,
930                                ),
931                            )),
932                        ) & LIAVariableConstraint::ComparisonConstraint(
933                            ComparisonConstraint(ThresholdConstraintOver::new(
934                                WeightedSum::new([
935                                    (Variable::new("var1"), Fraction::from(1)),
936                                    (Variable::new("var2"), -Fraction::from(1)),
937                                ]),
938                                ThresholdConstraint::new(
939                                    ThresholdCompOp::Geq,
940                                    Vec::<(Parameter, Fraction)>::new(),
941                                    1,
942                                ),
943                            )),
944                        )),
945                        actions: vec![
946                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
947                                .unwrap(),
948                            Action::new(
949                                Variable::new("var1"),
950                                IntegerExpression::BinaryExpr(
951                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
952                                    IntegerOp::Add,
953                                    Box::new(IntegerExpression::Const(1)),
954                                ),
955                            )
956                            .unwrap(),
957                        ],
958                    }],
959                ),
960            ]),
961            init_variable_constr: vec![LIAVariableConstraint::BinaryGuard(
962                Box::new(
963                    (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
964                        Variable::new("var1"),
965                        ThresholdConstraint::new(
966                            ThresholdCompOp::Lt,
967                            Vec::<(Parameter, Fraction)>::new(),
968                            1,
969                        ),
970                    ))) & LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
971                        Variable::new("var1"),
972                        ThresholdConstraint::new(
973                            ThresholdCompOp::Geq,
974                            Vec::<(Parameter, Fraction)>::new(),
975                            0,
976                        ),
977                    )),
978                ),
979                BooleanConnective::And,
980                Box::new(
981                    LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
982                        Variable::new("var2"),
983                        ThresholdConstraint::new(
984                            ThresholdCompOp::Lt,
985                            Vec::<(Parameter, Fraction)>::new(),
986                            1,
987                        ),
988                    )) & LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint::new(
989                        Variable::new("var2"),
990                        ThresholdConstraint::new(
991                            ThresholdCompOp::Geq,
992                            Vec::<(Parameter, Fraction)>::new(),
993                            0,
994                        ),
995                    )),
996                ),
997            )],
998            additional_vars_for_sums: HashMap::new(),
999        };
1000
1001        assert_eq!(lta.get_ta(), &ta);
1002        assert_eq!(lta.get_max_number_of_thresholds_per_rule(), 6);
1003
1004        assert_eq!(lta.name(), "test_ta1");
1005        assert_eq!(lta.variables().count(), 3);
1006        assert!(lta.variables().all(|v| ta.variables().any(|tv| tv == v)));
1007
1008        assert_eq!(lta.parameters().count(), 3);
1009        assert!(lta.parameters().all(|p| ta.parameters().any(|tp| tp == p)));
1010
1011        assert_eq!(lta.locations().count(), 3);
1012        assert!(lta.locations().all(|l| ta.locations().any(|tl| tl == l)));
1013
1014        assert_eq!(lta.resilience_conditions().count(), 1);
1015        assert!(
1016            lta.resilience_conditions()
1017                .all(|rc| ta.resilience_conditions().any(|trc| trc == rc))
1018        );
1019
1020        assert!(lta.can_be_initial_location(&Location::new("loc1")));
1021        assert!(!lta.can_be_initial_location(&Location::new("loc2")));
1022
1023        assert!(lta.outgoing_rules(&Location::new("loc3")).next().is_none());
1024        assert_eq!(lta.outgoing_rules(&Location::new("loc2")).count(), 1);
1025        assert_eq!(lta.incoming_rules(&Location::new("loc2")).count(), 1);
1026        assert_eq!(lta.incoming_rules(&Location::new("loc1")).count(), 0);
1027
1028        let mut sorted_rules = lta.rules().cloned().collect::<Vec<_>>();
1029        sorted_rules.sort_by_key(|r| r.id());
1030
1031        assert_eq!(
1032            lta.get_thresholds(),
1033            HashSet::from([
1034                Threshold::new(Vec::<(Parameter, Fraction)>::new(), 1),
1035                Threshold::new(Vec::<(Parameter, Fraction)>::new(), 2),
1036                Threshold::new([(Parameter::new("n"), 1)], 0),
1037                Threshold::new([(Parameter::new("n"), 1)], 1),
1038            ])
1039        );
1040
1041        assert_eq!(
1042            sorted_rules,
1043            vec![
1044                LIARule {
1045                    id: 0,
1046                    source: Location::new("loc1"),
1047                    target: Location::new("loc2"),
1048                    guard: LIAVariableConstraint::True,
1049                    actions: vec![],
1050                },
1051                LIARule {
1052                    id: 1,
1053                    source: Location::new("loc2"),
1054                    target: Location::new("loc3"),
1055                    guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1056                        ThresholdConstraintOver::new(
1057                            WeightedSum::new([(Variable::new("var1"), 1,)]),
1058                            ThresholdConstraint::new(
1059                                ThresholdCompOp::Lt,
1060                                Vec::<(Parameter, Fraction)>::new(),
1061                                2
1062                            ),
1063                        )
1064                    )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1065                        ThresholdConstraintOver::new(
1066                            WeightedSum::new([(Variable::new("var1"), 1,)]),
1067                            ThresholdConstraint::new(
1068                                ThresholdCompOp::Geq,
1069                                Vec::<(Parameter, Fraction)>::new(),
1070                                1
1071                            ),
1072                        )
1073                    ))) & (LIAVariableConstraint::SingleVarConstraint(
1074                        SingleAtomConstraint(ThresholdConstraintOver::new(
1075                            Variable::new("var2"),
1076                            ThresholdConstraint::new(
1077                                ThresholdCompOp::Lt,
1078                                [(Parameter::new("n"), 1)],
1079                                1
1080                            ),
1081                        ))
1082                    ) & LIAVariableConstraint::SingleVarConstraint(
1083                        SingleAtomConstraint(ThresholdConstraintOver::new(
1084                            Variable::new("var2"),
1085                            ThresholdConstraint::new(
1086                                ThresholdCompOp::Geq,
1087                                [(Parameter::new("n"), 1)],
1088                                0
1089                            ),
1090                        ))
1091                    )) | (LIAVariableConstraint::ComparisonConstraint(
1092                        ComparisonConstraint(ThresholdConstraintOver::new(
1093                            WeightedSum::new([
1094                                (Variable::new("var1"), Fraction::from(1)),
1095                                (Variable::new("var2"), -Fraction::from(1))
1096                            ]),
1097                            ThresholdConstraint::new(
1098                                ThresholdCompOp::Lt,
1099                                Vec::<(Parameter, Fraction)>::new(),
1100                                2,
1101                            ),
1102                        ))
1103                    ) & LIAVariableConstraint::ComparisonConstraint(
1104                        ComparisonConstraint(ThresholdConstraintOver::new(
1105                            WeightedSum::new([
1106                                (Variable::new("var1"), Fraction::from(1)),
1107                                (Variable::new("var2"), -Fraction::from(1))
1108                            ]),
1109                            ThresholdConstraint::new(
1110                                ThresholdCompOp::Geq,
1111                                Vec::<(Parameter, Fraction)>::new(),
1112                                1,
1113                            ),
1114                        ))
1115                    )),
1116                    actions: vec![
1117                        Action::new(Variable::new("var3"), IntegerExpression::Const(0)).unwrap(),
1118                        Action::new(
1119                            Variable::new("var1"),
1120                            IntegerExpression::BinaryExpr(
1121                                Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1122                                IntegerOp::Add,
1123                                Box::new(IntegerExpression::Const(1)),
1124                            ),
1125                        )
1126                        .unwrap(),
1127                    ],
1128                }
1129            ]
1130        );
1131
1132        assert_eq!(lta.get_derived_rule(&abstract_rule), rule);
1133
1134        assert_eq!(lta.initial_location_constraints().count(), 1);
1135        assert!(
1136            lta.initial_location_constraints()
1137                .any(|ilc| ta.initial_location_constraints().any(|tilc| tilc == ilc))
1138        );
1139
1140        assert_eq!(lta.initial_variable_constraints().count(), 1);
1141        assert!(lta.initial_variable_constraints().any(|ivc| {
1142            ta.initial_variable_constraints()
1143                .map(|c| LIAVariableConstraint::try_from(c.remove_boolean_negations()).unwrap())
1144                .any(|tivc| tivc == *ivc)
1145        }));
1146    }
1147
1148    #[test]
1149    fn test_lia_get_single_var_threshold() {
1150        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1151            .with_parameters(vec![
1152                Parameter::new("n"),
1153                Parameter::new("t"),
1154                Parameter::new("f"),
1155            ])
1156            .unwrap()
1157            .with_variables(vec![
1158                Variable::new("var1"),
1159                Variable::new("var2"),
1160                Variable::new("var3"),
1161            ])
1162            .unwrap()
1163            .with_locations(vec![
1164                Location::new("loc1"),
1165                Location::new("loc2"),
1166                Location::new("loc3"),
1167            ])
1168            .unwrap()
1169            .initialize()
1170            .with_rules(vec![
1171                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1172                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1173                    .with_guard(
1174                        BooleanVarConstraint::ComparisonExpression(
1175                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1176                            ComparisonOp::Eq,
1177                            Box::new(IntegerExpression::Const(1)),
1178                        ) & BooleanVarConstraint::ComparisonExpression(
1179                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1180                            ComparisonOp::Eq,
1181                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1182                        ),
1183                    )
1184                    .with_action(Action::new_reset(Variable::new("var3")))
1185                    .with_action(
1186                        Action::new(
1187                            Variable::new("var1"),
1188                            IntegerExpression::Atom(Variable::new("var1"))
1189                                + IntegerExpression::Const(1),
1190                        )
1191                        .unwrap(),
1192                    )
1193                    .build(),
1194            ])
1195            .unwrap()
1196            .with_initial_location_constraint(
1197                LocationConstraint::ComparisonExpression(
1198                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1199                    ComparisonOp::Eq,
1200                    Box::new(
1201                        IntegerExpression::Param(Parameter::new("n"))
1202                            - IntegerExpression::Param(Parameter::new("f")),
1203                    ),
1204                ) | LocationConstraint::ComparisonExpression(
1205                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1206                    ComparisonOp::Eq,
1207                    Box::new(IntegerExpression::Const(0)),
1208                ),
1209            )
1210            .unwrap()
1211            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1212                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1213                ComparisonOp::Gt,
1214                Box::new(
1215                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1216                ),
1217            ))
1218            .unwrap()
1219            .build();
1220
1221        let lta = LIAThresholdAutomaton {
1222            ta,
1223            rules: HashMap::from([
1224                (
1225                    Location::new("loc1"),
1226                    vec![LIARule {
1227                        id: 0,
1228                        source: Location::new("loc1"),
1229                        target: Location::new("loc2"),
1230                        guard: LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1231                            ThresholdConstraintOver::new(
1232                                Variable::new("var1"),
1233                                ThresholdConstraint::new(
1234                                    ThresholdCompOp::Lt,
1235                                    Vec::<(Parameter, Fraction)>::new(),
1236                                    2,
1237                                ),
1238                            ),
1239                        )) & LIAVariableConstraint::SingleVarConstraint(
1240                            SingleAtomConstraint(ThresholdConstraintOver::new(
1241                                Variable::new("var1"),
1242                                ThresholdConstraint::new(
1243                                    ThresholdCompOp::Geq,
1244                                    Vec::<(Parameter, Fraction)>::new(),
1245                                    1,
1246                                ),
1247                            )),
1248                        ),
1249                        actions: vec![],
1250                    }],
1251                ),
1252                (
1253                    Location::new("loc2"),
1254                    vec![LIARule {
1255                        id: 1,
1256                        source: Location::new("loc2"),
1257                        target: Location::new("loc3"),
1258                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1259                            ThresholdConstraintOver::new(
1260                                Variable::new("var1"),
1261                                ThresholdConstraint::new(
1262                                    ThresholdCompOp::Lt,
1263                                    Vec::<(Parameter, Fraction)>::new(),
1264                                    2,
1265                                ),
1266                            ),
1267                        )) & LIAVariableConstraint::SingleVarConstraint(
1268                            SingleAtomConstraint(ThresholdConstraintOver::new(
1269                                Variable::new("var1"),
1270                                ThresholdConstraint::new(
1271                                    ThresholdCompOp::Geq,
1272                                    Vec::<(Parameter, Fraction)>::new(),
1273                                    1,
1274                                ),
1275                            )),
1276                        )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1277                            ThresholdConstraintOver::new(
1278                                WeightedSum::new(BTreeMap::from([
1279                                    (Variable::new("var2"), 1),
1280                                    (Variable::new("var1"), 1),
1281                                ])),
1282                                ThresholdConstraint::new(
1283                                    ThresholdCompOp::Lt,
1284                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1285                                    1,
1286                                ),
1287                            ),
1288                        )) & LIAVariableConstraint::SumVarConstraint(
1289                            SumAtomConstraint(ThresholdConstraintOver::new(
1290                                WeightedSum::new(BTreeMap::from([
1291                                    (Variable::new("var2"), 1),
1292                                    (Variable::new("var1"), 1),
1293                                ])),
1294                                ThresholdConstraint::new(
1295                                    ThresholdCompOp::Geq,
1296                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1297                                    0,
1298                                ),
1299                            )),
1300                        )) | (LIAVariableConstraint::ComparisonConstraint(
1301                            ComparisonConstraint(ThresholdConstraintOver::new(
1302                                WeightedSum::new(BTreeMap::from([
1303                                    (Variable::new("var3"), 1.into()),
1304                                    (Variable::new("var3"), -Fraction::from(1)),
1305                                ])),
1306                                ThresholdConstraint::new(
1307                                    ThresholdCompOp::Lt,
1308                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1309                                    1,
1310                                ),
1311                            )),
1312                        ) & LIAVariableConstraint::ComparisonConstraint(
1313                            ComparisonConstraint(ThresholdConstraintOver::new(
1314                                WeightedSum::new(BTreeMap::from([
1315                                    (Variable::new("var3"), 1.into()),
1316                                    (Variable::new("var3"), -Fraction::from(1)),
1317                                ])),
1318                                ThresholdConstraint::new(
1319                                    ThresholdCompOp::Geq,
1320                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1321                                    0,
1322                                ),
1323                            )),
1324                        )),
1325                        actions: vec![
1326                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1327                                .unwrap(),
1328                            Action::new(
1329                                Variable::new("var1"),
1330                                IntegerExpression::BinaryExpr(
1331                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1332                                    IntegerOp::Add,
1333                                    Box::new(IntegerExpression::Const(1)),
1334                                ),
1335                            )
1336                            .unwrap(),
1337                        ],
1338                    }],
1339                ),
1340            ]),
1341            init_variable_constr: vec![],
1342            additional_vars_for_sums: HashMap::new(),
1343        };
1344
1345        let expected = HashSet::from([
1346            SingleAtomConstraint(ThresholdConstraintOver::new(
1347                Variable::new("var1"),
1348                ThresholdConstraint::new(
1349                    ThresholdCompOp::Lt,
1350                    Vec::<(Parameter, Fraction)>::new(),
1351                    2,
1352                ),
1353            )),
1354            SingleAtomConstraint(ThresholdConstraintOver::new(
1355                Variable::new("var1"),
1356                ThresholdConstraint::new(
1357                    ThresholdCompOp::Geq,
1358                    Vec::<(Parameter, Fraction)>::new(),
1359                    1,
1360                ),
1361            )),
1362        ]);
1363        assert_eq!(lta.get_single_var_constraints_rules(), expected)
1364    }
1365
1366    #[test]
1367    fn test_lia_ta_has_comparison_guard() {
1368        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1369            .with_parameters(vec![
1370                Parameter::new("n"),
1371                Parameter::new("t"),
1372                Parameter::new("f"),
1373            ])
1374            .unwrap()
1375            .with_variables(vec![
1376                Variable::new("var1"),
1377                Variable::new("var2"),
1378                Variable::new("var3"),
1379            ])
1380            .unwrap()
1381            .with_locations(vec![
1382                Location::new("loc1"),
1383                Location::new("loc2"),
1384                Location::new("loc3"),
1385            ])
1386            .unwrap()
1387            .initialize()
1388            .with_rules(vec![
1389                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1390                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1391                    .with_guard(
1392                        BooleanVarConstraint::ComparisonExpression(
1393                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1394                            ComparisonOp::Eq,
1395                            Box::new(IntegerExpression::Const(1)),
1396                        ) & BooleanVarConstraint::ComparisonExpression(
1397                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1398                            ComparisonOp::Eq,
1399                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1400                        ),
1401                    )
1402                    .with_action(Action::new_reset(Variable::new("var3")))
1403                    .with_action(
1404                        Action::new(
1405                            Variable::new("var1"),
1406                            IntegerExpression::Atom(Variable::new("var1"))
1407                                + IntegerExpression::Const(1),
1408                        )
1409                        .unwrap(),
1410                    )
1411                    .build(),
1412            ])
1413            .unwrap()
1414            .with_initial_location_constraint(
1415                LocationConstraint::ComparisonExpression(
1416                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1417                    ComparisonOp::Eq,
1418                    Box::new(
1419                        IntegerExpression::Param(Parameter::new("n"))
1420                            - IntegerExpression::Param(Parameter::new("f")),
1421                    ),
1422                ) | LocationConstraint::ComparisonExpression(
1423                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1424                    ComparisonOp::Eq,
1425                    Box::new(IntegerExpression::Const(0)),
1426                ),
1427            )
1428            .unwrap()
1429            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1430                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1431                ComparisonOp::Gt,
1432                Box::new(
1433                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1434                ),
1435            ))
1436            .unwrap()
1437            .build();
1438
1439        let lta = LIAThresholdAutomaton {
1440            ta,
1441            rules: HashMap::from([
1442                (
1443                    Location::new("loc1"),
1444                    vec![LIARule {
1445                        id: 0,
1446                        source: Location::new("loc1"),
1447                        target: Location::new("loc2"),
1448                        guard: LIAVariableConstraint::True,
1449                        actions: vec![],
1450                    }],
1451                ),
1452                (
1453                    Location::new("loc2"),
1454                    vec![LIARule {
1455                        id: 1,
1456                        source: Location::new("loc2"),
1457                        target: Location::new("loc3"),
1458                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1459                            ThresholdConstraintOver::new(
1460                                Variable::new("var1"),
1461                                ThresholdConstraint::new(
1462                                    ThresholdCompOp::Lt,
1463                                    Vec::<(Parameter, Fraction)>::new(),
1464                                    2,
1465                                ),
1466                            ),
1467                        )) & LIAVariableConstraint::SingleVarConstraint(
1468                            SingleAtomConstraint(ThresholdConstraintOver::new(
1469                                Variable::new("var1"),
1470                                ThresholdConstraint::new(
1471                                    ThresholdCompOp::Geq,
1472                                    Vec::<(Parameter, Fraction)>::new(),
1473                                    1,
1474                                ),
1475                            )),
1476                        )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1477                            ThresholdConstraintOver::new(
1478                                WeightedSum::new(BTreeMap::from([
1479                                    (Variable::new("var2"), 1),
1480                                    (Variable::new("var1"), 1),
1481                                ])),
1482                                ThresholdConstraint::new(
1483                                    ThresholdCompOp::Lt,
1484                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1485                                    1,
1486                                ),
1487                            ),
1488                        )) & LIAVariableConstraint::SumVarConstraint(
1489                            SumAtomConstraint(ThresholdConstraintOver::new(
1490                                WeightedSum::new(BTreeMap::from([
1491                                    (Variable::new("var2"), 1),
1492                                    (Variable::new("var1"), 1),
1493                                ])),
1494                                ThresholdConstraint::new(
1495                                    ThresholdCompOp::Geq,
1496                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1497                                    0,
1498                                ),
1499                            )),
1500                        )) | (LIAVariableConstraint::ComparisonConstraint(
1501                            ComparisonConstraint(ThresholdConstraintOver::new(
1502                                WeightedSum::new(BTreeMap::from([
1503                                    (Variable::new("var3"), 1.into()),
1504                                    (Variable::new("var3"), -Fraction::from(1)),
1505                                ])),
1506                                ThresholdConstraint::new(
1507                                    ThresholdCompOp::Lt,
1508                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1509                                    1,
1510                                ),
1511                            )),
1512                        ) & LIAVariableConstraint::ComparisonConstraint(
1513                            ComparisonConstraint(ThresholdConstraintOver::new(
1514                                WeightedSum::new(BTreeMap::from([
1515                                    (Variable::new("var3"), 1.into()),
1516                                    (Variable::new("var3"), -Fraction::from(1)),
1517                                ])),
1518                                ThresholdConstraint::new(
1519                                    ThresholdCompOp::Geq,
1520                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1521                                    0,
1522                                ),
1523                            )),
1524                        )),
1525                        actions: vec![
1526                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1527                                .unwrap(),
1528                            Action::new(
1529                                Variable::new("var1"),
1530                                IntegerExpression::BinaryExpr(
1531                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1532                                    IntegerOp::Add,
1533                                    Box::new(IntegerExpression::Const(1)),
1534                                ),
1535                            )
1536                            .unwrap(),
1537                        ],
1538                    }],
1539                ),
1540            ]),
1541            init_variable_constr: vec![],
1542            additional_vars_for_sums: HashMap::new(),
1543        };
1544
1545        assert!(lta.has_comparison_guard());
1546        let expected = HashSet::from([
1547            ComparisonConstraint(ThresholdConstraintOver::new(
1548                WeightedSum::new(BTreeMap::from([
1549                    (Variable::new("var3"), 1.into()),
1550                    (Variable::new("var3"), -Fraction::from(1)),
1551                ])),
1552                ThresholdConstraint::new(
1553                    ThresholdCompOp::Lt,
1554                    BTreeMap::from([(Parameter::new("t"), 3)]),
1555                    1,
1556                ),
1557            )),
1558            ComparisonConstraint(ThresholdConstraintOver::new(
1559                WeightedSum::new(BTreeMap::from([
1560                    (Variable::new("var3"), 1.into()),
1561                    (Variable::new("var3"), -Fraction::from(1)),
1562                ])),
1563                ThresholdConstraint::new(
1564                    ThresholdCompOp::Geq,
1565                    BTreeMap::from([(Parameter::new("t"), 3)]),
1566                    0,
1567                ),
1568            )),
1569        ]);
1570        assert_eq!(lta.get_comparison_guards(), expected);
1571
1572        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1573            .with_parameters(vec![
1574                Parameter::new("n"),
1575                Parameter::new("t"),
1576                Parameter::new("f"),
1577            ])
1578            .unwrap()
1579            .with_variables(vec![
1580                Variable::new("var1"),
1581                Variable::new("var2"),
1582                Variable::new("var3"),
1583            ])
1584            .unwrap()
1585            .with_locations(vec![
1586                Location::new("loc1"),
1587                Location::new("loc2"),
1588                Location::new("loc3"),
1589            ])
1590            .unwrap()
1591            .initialize()
1592            .with_rules(vec![
1593                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1594                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1595                    .with_guard(
1596                        BooleanVarConstraint::ComparisonExpression(
1597                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1598                            ComparisonOp::Eq,
1599                            Box::new(IntegerExpression::Const(1)),
1600                        ) & BooleanVarConstraint::ComparisonExpression(
1601                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1602                            ComparisonOp::Eq,
1603                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1604                        ),
1605                    )
1606                    .with_action(Action::new_reset(Variable::new("var3")))
1607                    .with_action(
1608                        Action::new(
1609                            Variable::new("var1"),
1610                            IntegerExpression::Atom(Variable::new("var1"))
1611                                + IntegerExpression::Const(1),
1612                        )
1613                        .unwrap(),
1614                    )
1615                    .build(),
1616            ])
1617            .unwrap()
1618            .with_initial_location_constraint(
1619                LocationConstraint::ComparisonExpression(
1620                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1621                    ComparisonOp::Eq,
1622                    Box::new(
1623                        IntegerExpression::Param(Parameter::new("n"))
1624                            - IntegerExpression::Param(Parameter::new("f")),
1625                    ),
1626                ) | LocationConstraint::ComparisonExpression(
1627                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1628                    ComparisonOp::Eq,
1629                    Box::new(IntegerExpression::Const(0)),
1630                ),
1631            )
1632            .unwrap()
1633            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1634                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1635                ComparisonOp::Gt,
1636                Box::new(
1637                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1638                ),
1639            ))
1640            .unwrap()
1641            .build();
1642
1643        let lta = LIAThresholdAutomaton {
1644            ta,
1645            rules: HashMap::from([
1646                (
1647                    Location::new("loc1"),
1648                    vec![LIARule {
1649                        id: 0,
1650                        source: Location::new("loc1"),
1651                        target: Location::new("loc2"),
1652                        guard: LIAVariableConstraint::True,
1653                        actions: vec![],
1654                    }],
1655                ),
1656                (
1657                    Location::new("loc2"),
1658                    vec![LIARule {
1659                        id: 1,
1660                        source: Location::new("loc2"),
1661                        target: Location::new("loc3"),
1662                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1663                            ThresholdConstraintOver::new(
1664                                Variable::new("var1"),
1665                                ThresholdConstraint::new(
1666                                    ThresholdCompOp::Lt,
1667                                    Vec::<(Parameter, Fraction)>::new(),
1668                                    2,
1669                                ),
1670                            ),
1671                        )) & LIAVariableConstraint::SingleVarConstraint(
1672                            SingleAtomConstraint(ThresholdConstraintOver::new(
1673                                Variable::new("var1"),
1674                                ThresholdConstraint::new(
1675                                    ThresholdCompOp::Geq,
1676                                    Vec::<(Parameter, Fraction)>::new(),
1677                                    1,
1678                                ),
1679                            )),
1680                        )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1681                            ThresholdConstraintOver::new(
1682                                WeightedSum::new(BTreeMap::from([
1683                                    (Variable::new("var2"), 1),
1684                                    (Variable::new("var1"), 1),
1685                                ])),
1686                                ThresholdConstraint::new(
1687                                    ThresholdCompOp::Lt,
1688                                    [(Parameter::new("n"), 1)],
1689                                    1,
1690                                ),
1691                            ),
1692                        )) & LIAVariableConstraint::SumVarConstraint(
1693                            SumAtomConstraint(ThresholdConstraintOver::new(
1694                                WeightedSum::new(BTreeMap::from([
1695                                    (Variable::new("var2"), 1),
1696                                    (Variable::new("var1"), 1),
1697                                ])),
1698                                ThresholdConstraint::new(
1699                                    ThresholdCompOp::Geq,
1700                                    [(Parameter::new("n"), 1)],
1701                                    0,
1702                                ),
1703                            )),
1704                        )),
1705                        actions: vec![
1706                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1707                                .unwrap(),
1708                            Action::new(
1709                                Variable::new("var1"),
1710                                IntegerExpression::BinaryExpr(
1711                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1712                                    IntegerOp::Add,
1713                                    Box::new(IntegerExpression::Const(1)),
1714                                ),
1715                            )
1716                            .unwrap(),
1717                        ],
1718                    }],
1719                ),
1720            ]),
1721            init_variable_constr: vec![],
1722            additional_vars_for_sums: HashMap::new(),
1723        };
1724
1725        assert!(!lta.has_comparison_guard());
1726        assert!(lta.get_comparison_guards().is_empty());
1727    }
1728
1729    #[test]
1730    fn test_lia_has_multi_var() {
1731        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1732            .with_parameters(vec![
1733                Parameter::new("n"),
1734                Parameter::new("t"),
1735                Parameter::new("f"),
1736            ])
1737            .unwrap()
1738            .with_variables(vec![
1739                Variable::new("var1"),
1740                Variable::new("var2"),
1741                Variable::new("var3"),
1742            ])
1743            .unwrap()
1744            .with_locations(vec![
1745                Location::new("loc1"),
1746                Location::new("loc2"),
1747                Location::new("loc3"),
1748            ])
1749            .unwrap()
1750            .initialize()
1751            .with_rules(vec![
1752                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1753                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1754                    .with_guard(
1755                        BooleanVarConstraint::ComparisonExpression(
1756                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1757                            ComparisonOp::Eq,
1758                            Box::new(IntegerExpression::Const(1)),
1759                        ) & BooleanVarConstraint::ComparisonExpression(
1760                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1761                            ComparisonOp::Eq,
1762                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1763                        ),
1764                    )
1765                    .with_action(Action::new_reset(Variable::new("var3")))
1766                    .with_action(
1767                        Action::new(
1768                            Variable::new("var1"),
1769                            IntegerExpression::Atom(Variable::new("var1"))
1770                                + IntegerExpression::Const(1),
1771                        )
1772                        .unwrap(),
1773                    )
1774                    .build(),
1775            ])
1776            .unwrap()
1777            .with_initial_location_constraint(
1778                LocationConstraint::ComparisonExpression(
1779                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1780                    ComparisonOp::Eq,
1781                    Box::new(
1782                        IntegerExpression::Param(Parameter::new("n"))
1783                            - IntegerExpression::Param(Parameter::new("f")),
1784                    ),
1785                ) | LocationConstraint::ComparisonExpression(
1786                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1787                    ComparisonOp::Eq,
1788                    Box::new(IntegerExpression::Const(0)),
1789                ),
1790            )
1791            .unwrap()
1792            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1793                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1794                ComparisonOp::Gt,
1795                Box::new(
1796                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
1797                ),
1798            ))
1799            .unwrap()
1800            .build();
1801
1802        let lta = LIAThresholdAutomaton {
1803            ta,
1804            rules: HashMap::from([
1805                (
1806                    Location::new("loc1"),
1807                    vec![LIARule {
1808                        id: 0,
1809                        source: Location::new("loc1"),
1810                        target: Location::new("loc2"),
1811                        guard: LIAVariableConstraint::True,
1812                        actions: vec![],
1813                    }],
1814                ),
1815                (
1816                    Location::new("loc2"),
1817                    vec![LIARule {
1818                        id: 1,
1819                        source: Location::new("loc2"),
1820                        target: Location::new("loc3"),
1821                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
1822                            ThresholdConstraintOver::new(
1823                                Variable::new("var1"),
1824                                ThresholdConstraint::new(
1825                                    ThresholdCompOp::Lt,
1826                                    Vec::<(Parameter, Fraction)>::new(),
1827                                    2,
1828                                ),
1829                            ),
1830                        )) & LIAVariableConstraint::SingleVarConstraint(
1831                            SingleAtomConstraint(ThresholdConstraintOver::new(
1832                                Variable::new("var1"),
1833                                ThresholdConstraint::new(
1834                                    ThresholdCompOp::Geq,
1835                                    Vec::<(Parameter, Fraction)>::new(),
1836                                    1,
1837                                ),
1838                            )),
1839                        )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
1840                            ThresholdConstraintOver::new(
1841                                WeightedSum::new(BTreeMap::from([
1842                                    (Variable::new("var2"), 1),
1843                                    (Variable::new("var1"), 1),
1844                                ])),
1845                                ThresholdConstraint::new(
1846                                    ThresholdCompOp::Lt,
1847                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1848                                    1,
1849                                ),
1850                            ),
1851                        )) & LIAVariableConstraint::SumVarConstraint(
1852                            SumAtomConstraint(ThresholdConstraintOver::new(
1853                                WeightedSum::new(BTreeMap::from([
1854                                    (Variable::new("var2"), 1),
1855                                    (Variable::new("var1"), 1),
1856                                ])),
1857                                ThresholdConstraint::new(
1858                                    ThresholdCompOp::Geq,
1859                                    BTreeMap::from([(Parameter::new("n"), 1)]),
1860                                    0,
1861                                ),
1862                            )),
1863                        )) | (LIAVariableConstraint::ComparisonConstraint(
1864                            ComparisonConstraint(ThresholdConstraintOver::new(
1865                                WeightedSum::new(BTreeMap::from([
1866                                    (Variable::new("var3"), 1.into()),
1867                                    (Variable::new("var3"), -Fraction::from(1)),
1868                                ])),
1869                                ThresholdConstraint::new(
1870                                    ThresholdCompOp::Lt,
1871                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1872                                    1,
1873                                ),
1874                            )),
1875                        ) & LIAVariableConstraint::ComparisonConstraint(
1876                            ComparisonConstraint(ThresholdConstraintOver::new(
1877                                WeightedSum::new(BTreeMap::from([
1878                                    (Variable::new("var3"), 1.into()),
1879                                    (Variable::new("var3"), -Fraction::from(1)),
1880                                ])),
1881                                ThresholdConstraint::new(
1882                                    ThresholdCompOp::Geq,
1883                                    BTreeMap::from([(Parameter::new("t"), 3)]),
1884                                    0,
1885                                ),
1886                            )),
1887                        )),
1888                        actions: vec![
1889                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
1890                                .unwrap(),
1891                            Action::new(
1892                                Variable::new("var1"),
1893                                IntegerExpression::BinaryExpr(
1894                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1895                                    IntegerOp::Add,
1896                                    Box::new(IntegerExpression::Const(1)),
1897                                ),
1898                            )
1899                            .unwrap(),
1900                        ],
1901                    }],
1902                ),
1903            ]),
1904            init_variable_constr: vec![],
1905            additional_vars_for_sums: HashMap::new(),
1906        };
1907
1908        assert!(lta.has_sum_var_threshold_guard());
1909
1910        let expected = HashSet::from([
1911            SumAtomConstraint(ThresholdConstraintOver::new(
1912                WeightedSum::new(BTreeMap::from([
1913                    (Variable::new("var2"), 1),
1914                    (Variable::new("var1"), 1),
1915                ])),
1916                ThresholdConstraint::new(
1917                    ThresholdCompOp::Geq,
1918                    BTreeMap::from([(Parameter::new("n"), 1)]),
1919                    0,
1920                ),
1921            )),
1922            SumAtomConstraint(ThresholdConstraintOver::new(
1923                WeightedSum::new(BTreeMap::from([
1924                    (Variable::new("var2"), 1),
1925                    (Variable::new("var1"), 1),
1926                ])),
1927                ThresholdConstraint::new(
1928                    ThresholdCompOp::Lt,
1929                    BTreeMap::from([(Parameter::new("n"), 1)]),
1930                    1,
1931                ),
1932            )),
1933        ]);
1934        assert_eq!(lta.get_sum_var_constraints(), expected);
1935
1936        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
1937            .with_parameters(vec![
1938                Parameter::new("n"),
1939                Parameter::new("t"),
1940                Parameter::new("f"),
1941            ])
1942            .unwrap()
1943            .with_variables(vec![
1944                Variable::new("var1"),
1945                Variable::new("var2"),
1946                Variable::new("var3"),
1947            ])
1948            .unwrap()
1949            .with_locations(vec![
1950                Location::new("loc1"),
1951                Location::new("loc2"),
1952                Location::new("loc3"),
1953            ])
1954            .unwrap()
1955            .initialize()
1956            .with_rules(vec![
1957                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
1958                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
1959                    .with_guard(
1960                        BooleanVarConstraint::ComparisonExpression(
1961                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
1962                            ComparisonOp::Eq,
1963                            Box::new(IntegerExpression::Const(1)),
1964                        ) & BooleanVarConstraint::ComparisonExpression(
1965                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
1966                            ComparisonOp::Eq,
1967                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
1968                        ),
1969                    )
1970                    .with_action(Action::new_reset(Variable::new("var3")))
1971                    .with_action(
1972                        Action::new(
1973                            Variable::new("var1"),
1974                            IntegerExpression::Atom(Variable::new("var1"))
1975                                + IntegerExpression::Const(1),
1976                        )
1977                        .unwrap(),
1978                    )
1979                    .build(),
1980            ])
1981            .unwrap()
1982            .with_initial_location_constraint(
1983                LocationConstraint::ComparisonExpression(
1984                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
1985                    ComparisonOp::Eq,
1986                    Box::new(
1987                        IntegerExpression::Param(Parameter::new("n"))
1988                            - IntegerExpression::Param(Parameter::new("f")),
1989                    ),
1990                ) | LocationConstraint::ComparisonExpression(
1991                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
1992                    ComparisonOp::Eq,
1993                    Box::new(IntegerExpression::Const(0)),
1994                ),
1995            )
1996            .unwrap()
1997            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
1998                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1999                ComparisonOp::Gt,
2000                Box::new(
2001                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
2002                ),
2003            ))
2004            .unwrap()
2005            .build();
2006
2007        let lta = LIAThresholdAutomaton {
2008            ta,
2009            rules: HashMap::from([
2010                (
2011                    Location::new("loc1"),
2012                    vec![LIARule {
2013                        id: 0,
2014                        source: Location::new("loc1"),
2015                        target: Location::new("loc2"),
2016                        guard: LIAVariableConstraint::True,
2017                        actions: vec![],
2018                    }],
2019                ),
2020                (
2021                    Location::new("loc2"),
2022                    vec![LIARule {
2023                        id: 1,
2024                        source: Location::new("loc2"),
2025                        target: Location::new("loc3"),
2026                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2027                            ThresholdConstraintOver::new(
2028                                Variable::new("var1"),
2029                                ThresholdConstraint::new(
2030                                    ThresholdCompOp::Lt,
2031                                    Vec::<(Parameter, Fraction)>::new(),
2032                                    2,
2033                                ),
2034                            ),
2035                        )) & LIAVariableConstraint::SingleVarConstraint(
2036                            SingleAtomConstraint(ThresholdConstraintOver::new(
2037                                Variable::new("var1"),
2038                                ThresholdConstraint::new(
2039                                    ThresholdCompOp::Geq,
2040                                    Vec::<(Parameter, Fraction)>::new(),
2041                                    1,
2042                                ),
2043                            )),
2044                        )) | (LIAVariableConstraint::ComparisonConstraint(
2045                            ComparisonConstraint(ThresholdConstraintOver::new(
2046                                WeightedSum::new(BTreeMap::from([
2047                                    (Variable::new("var3"), 1.into()),
2048                                    (Variable::new("var3"), -Fraction::from(1)),
2049                                ])),
2050                                ThresholdConstraint::new(
2051                                    ThresholdCompOp::Lt,
2052                                    BTreeMap::from([(Parameter::new("t"), 3)]),
2053                                    1,
2054                                ),
2055                            )),
2056                        ) & LIAVariableConstraint::ComparisonConstraint(
2057                            ComparisonConstraint(ThresholdConstraintOver::new(
2058                                WeightedSum::new(BTreeMap::from([
2059                                    (Variable::new("var3"), 1.into()),
2060                                    (Variable::new("var3"), -Fraction::from(1)),
2061                                ])),
2062                                ThresholdConstraint::new(
2063                                    ThresholdCompOp::Geq,
2064                                    BTreeMap::from([(Parameter::new("t"), 3)]),
2065                                    0,
2066                                ),
2067                            )),
2068                        )),
2069                        actions: vec![
2070                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
2071                                .unwrap(),
2072                            Action::new(
2073                                Variable::new("var1"),
2074                                IntegerExpression::BinaryExpr(
2075                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2076                                    IntegerOp::Add,
2077                                    Box::new(IntegerExpression::Const(1)),
2078                                ),
2079                            )
2080                            .unwrap(),
2081                        ],
2082                    }],
2083                ),
2084            ]),
2085            init_variable_constr: vec![],
2086            additional_vars_for_sums: HashMap::new(),
2087        };
2088
2089        assert!(!lta.has_sum_var_threshold_guard());
2090        assert!(lta.get_sum_var_constraints().is_empty());
2091    }
2092
2093    #[test]
2094    fn test_lia_display() {
2095        let ta = GeneralThresholdAutomatonBuilder::new("test_ta1")
2096            .with_parameters(vec![
2097                Parameter::new("n"),
2098                Parameter::new("t"),
2099                Parameter::new("f"),
2100            ])
2101            .unwrap()
2102            .with_variables(vec![
2103                Variable::new("var1"),
2104                Variable::new("var2"),
2105                Variable::new("var3"),
2106            ])
2107            .unwrap()
2108            .with_locations(vec![
2109                Location::new("loc1"),
2110                Location::new("loc2"),
2111                Location::new("loc3"),
2112            ])
2113            .unwrap()
2114            .initialize()
2115            .with_rules(vec![
2116                RuleBuilder::new(0, Location::new("loc1"), Location::new("loc2")).build(),
2117                RuleBuilder::new(1, Location::new("loc2"), Location::new("loc3"))
2118                    .with_guard(
2119                        BooleanVarConstraint::ComparisonExpression(
2120                            Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2121                            ComparisonOp::Eq,
2122                            Box::new(IntegerExpression::Const(1)),
2123                        ) & BooleanVarConstraint::ComparisonExpression(
2124                            Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2125                            ComparisonOp::Eq,
2126                            Box::new(IntegerExpression::Param(Parameter::new("n"))),
2127                        ),
2128                    )
2129                    .with_action(Action::new_reset(Variable::new("var3")))
2130                    .with_action(
2131                        Action::new(
2132                            Variable::new("var1"),
2133                            IntegerExpression::Atom(Variable::new("var1"))
2134                                + IntegerExpression::Const(1),
2135                        )
2136                        .unwrap(),
2137                    )
2138                    .build(),
2139            ])
2140            .unwrap()
2141            .with_initial_location_constraint(
2142                LocationConstraint::ComparisonExpression(
2143                    Box::new(IntegerExpression::Atom(Location::new("loc1"))),
2144                    ComparisonOp::Eq,
2145                    Box::new(
2146                        IntegerExpression::Param(Parameter::new("n"))
2147                            - IntegerExpression::Param(Parameter::new("f")),
2148                    ),
2149                ) | LocationConstraint::ComparisonExpression(
2150                    Box::new(IntegerExpression::Atom(Location::new("loc2"))),
2151                    ComparisonOp::Eq,
2152                    Box::new(IntegerExpression::Const(0)),
2153                ),
2154            )
2155            .unwrap()
2156            .with_initial_variable_constraint(BooleanVarConstraint::ComparisonExpression(
2157                Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2158                ComparisonOp::Eq,
2159                Box::new(IntegerExpression::Const(1)),
2160            ))
2161            .unwrap()
2162            .with_resilience_condition(ParameterConstraint::ComparisonExpression(
2163                Box::new(IntegerExpression::Atom(Parameter::new("n"))),
2164                ComparisonOp::Gt,
2165                Box::new(
2166                    IntegerExpression::Const(3) * IntegerExpression::Atom(Parameter::new("f")),
2167                ),
2168            ))
2169            .unwrap()
2170            .build();
2171
2172        let lta = LIAThresholdAutomaton {
2173            ta,
2174            rules: HashMap::from([
2175                (
2176                    Location::new("loc1"),
2177                    vec![LIARule {
2178                        id: 0,
2179                        source: Location::new("loc1"),
2180                        target: Location::new("loc2"),
2181                        guard: LIAVariableConstraint::True,
2182                        actions: vec![],
2183                    }],
2184                ),
2185                (
2186                    Location::new("loc2"),
2187                    vec![LIARule {
2188                        id: 1,
2189                        source: Location::new("loc2"),
2190                        target: Location::new("loc3"),
2191                        guard: (LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2192                            ThresholdConstraintOver::new(
2193                                Variable::new("var1"),
2194                                ThresholdConstraint::new(
2195                                    ThresholdCompOp::Lt,
2196                                    Vec::<(Parameter, Fraction)>::new(),
2197                                    2,
2198                                ),
2199                            ),
2200                        )) & LIAVariableConstraint::SingleVarConstraint(
2201                            SingleAtomConstraint(ThresholdConstraintOver::new(
2202                                Variable::new("var1"),
2203                                ThresholdConstraint::new(
2204                                    ThresholdCompOp::Geq,
2205                                    Vec::<(Parameter, Fraction)>::new(),
2206                                    1,
2207                                ),
2208                            )),
2209                        )) & (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2210                            ThresholdConstraintOver::new(
2211                                WeightedSum::new(BTreeMap::from([
2212                                    (Variable::new("var2"), 1),
2213                                    (Variable::new("var1"), 1),
2214                                ])),
2215                                ThresholdConstraint::new(
2216                                    ThresholdCompOp::Lt,
2217                                    BTreeMap::from([(Parameter::new("n"), 1)]),
2218                                    1,
2219                                ),
2220                            ),
2221                        )) & LIAVariableConstraint::SumVarConstraint(
2222                            SumAtomConstraint(ThresholdConstraintOver::new(
2223                                WeightedSum::new(BTreeMap::from([
2224                                    (Variable::new("var2"), 1),
2225                                    (Variable::new("var1"), 1),
2226                                ])),
2227                                ThresholdConstraint::new(
2228                                    ThresholdCompOp::Geq,
2229                                    BTreeMap::from([(Parameter::new("n"), 1)]),
2230                                    0,
2231                                ),
2232                            )),
2233                        )) | (LIAVariableConstraint::ComparisonConstraint(
2234                            ComparisonConstraint(ThresholdConstraintOver::new(
2235                                WeightedSum::new(BTreeMap::from([
2236                                    (Variable::new("var3"), 1.into()),
2237                                    (Variable::new("var3"), -Fraction::from(1)),
2238                                ])),
2239                                ThresholdConstraint::new(
2240                                    ThresholdCompOp::Lt,
2241                                    BTreeMap::from([(Parameter::new("t"), 3)]),
2242                                    1,
2243                                ),
2244                            )),
2245                        ) & LIAVariableConstraint::ComparisonConstraint(
2246                            ComparisonConstraint(ThresholdConstraintOver::new(
2247                                WeightedSum::new(BTreeMap::from([
2248                                    (Variable::new("var3"), 1.into()),
2249                                    (Variable::new("var3"), -Fraction::from(1)),
2250                                ])),
2251                                ThresholdConstraint::new(
2252                                    ThresholdCompOp::Geq,
2253                                    BTreeMap::from([(Parameter::new("t"), 3)]),
2254                                    0,
2255                                ),
2256                            )),
2257                        )),
2258
2259                        actions: vec![
2260                            Action::new(Variable::new("var3"), IntegerExpression::Const(0))
2261                                .unwrap(),
2262                            Action::new(
2263                                Variable::new("var1"),
2264                                IntegerExpression::BinaryExpr(
2265                                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2266                                    IntegerOp::Add,
2267                                    Box::new(IntegerExpression::Const(1)),
2268                                ),
2269                            )
2270                            .unwrap(),
2271                        ],
2272                    }],
2273                ),
2274            ]),
2275            init_variable_constr: vec![
2276                LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2277                    ThresholdConstraintOver::new(
2278                        Variable::new("var1"),
2279                        ThresholdConstraint::new(
2280                            ThresholdCompOp::Lt,
2281                            Vec::<(Parameter, Fraction)>::new(),
2282                            Fraction::from(2),
2283                        ),
2284                    ),
2285                )),
2286                LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2287                    ThresholdConstraintOver::new(
2288                        Variable::new("var1"),
2289                        ThresholdConstraint::new(
2290                            ThresholdCompOp::Geq,
2291                            Vec::<(Parameter, Fraction)>::new(),
2292                            Fraction::from(1),
2293                        ),
2294                    ),
2295                )),
2296            ],
2297            additional_vars_for_sums: HashMap::new(),
2298        };
2299
2300        let expected = "thresholdAutomaton test_ta1 {\n    shared var1, var2, var3;\n\n    parameters f, n, t;\n\n    assumptions (1) {\n        n > (3 * f);\n    }\n\n    locations (3) {\n        loc1:[0];\n        loc2:[1];\n        loc3:[2];\n    }\n\n    inits (3) {\n        (loc1 == (n - f) || loc2 == 0);\n        var1 < 2;\n        var1 >= 1;\n    }\n\n    rules (2) {\n        0: loc1 -> loc2\n            when ( true )\n            do {  };\n        1: loc2 -> loc3\n            when ( (((var1 < 2) && (var1 >= 1)) && ((var1 + var2 < n + 1) && (var1 + var2 >= n))) || ((-1 * var3 < 3 * t + 1) && (-1 * var3 >= 3 * t)) )\n            do { var3' == 0; var1' == var1 + 1 };\n    }\n}\n";
2301
2302        assert_eq!(lta.to_string(), expected)
2303    }
2304
2305    #[test]
2306    fn test_lia_rule_getters() {
2307        let rule = LIARule {
2308            id: 0,
2309            source: Location::new("loc1"),
2310            target: Location::new("loc2"),
2311            guard: LIAVariableConstraint::True,
2312            actions: vec![],
2313        };
2314
2315        assert_eq!(rule.id(), 0);
2316        assert_eq!(rule.source(), &Location::new("loc1"));
2317        assert_eq!(rule.target(), &Location::new("loc2"));
2318        assert_eq!(rule.guard(), &LIAVariableConstraint::True);
2319        assert_eq!(rule.actions().count(), 0);
2320        assert_eq!(rule.number_of_updates(), 0);
2321        assert!(!rule.has_resets());
2322        assert!(!rule.has_decrements());
2323
2324        let rule = LIARule {
2325            id: 0,
2326            source: Location::new("loc1"),
2327            target: Location::new("loc2"),
2328            guard: LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2329                ThresholdConstraintOver::new(
2330                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2331                    ThresholdConstraint::new(
2332                        ThresholdCompOp::Lt,
2333                        Vec::<(Parameter, Fraction)>::new(),
2334                        2,
2335                    ),
2336                ),
2337            )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2338                ThresholdConstraintOver::new(
2339                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2340                    ThresholdConstraint::new(
2341                        ThresholdCompOp::Geq,
2342                        Vec::<(Parameter, Fraction)>::new(),
2343                        1,
2344                    ),
2345                ),
2346            )),
2347            actions: vec![
2348                Action::new(
2349                    Variable::new("var1"),
2350                    IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1),
2351                )
2352                .unwrap(),
2353            ],
2354        };
2355
2356        assert_eq!(rule.id(), 0);
2357        assert_eq!(rule.source(), &Location::new("loc1"));
2358        assert_eq!(rule.target(), &Location::new("loc2"));
2359        assert_eq!(
2360            rule.guard(),
2361            &(LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2362                ThresholdConstraintOver::new(
2363                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2364                    ThresholdConstraint::new(
2365                        ThresholdCompOp::Lt,
2366                        Vec::<(Parameter, Fraction)>::new(),
2367                        2,
2368                    ),
2369                ),
2370            )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2371                ThresholdConstraintOver::new(
2372                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2373                    ThresholdConstraint::new(
2374                        ThresholdCompOp::Geq,
2375                        Vec::<(Parameter, Fraction)>::new(),
2376                        1,
2377                    ),
2378                ),
2379            ))),
2380        );
2381        assert_eq!(
2382            rule.actions().cloned().collect::<Vec<_>>(),
2383            vec![
2384                Action::new(
2385                    Variable::new("var1"),
2386                    IntegerExpression::Atom(Variable::new("var1")) - IntegerExpression::Const(1)
2387                )
2388                .unwrap()
2389            ]
2390        );
2391        assert_eq!(rule.number_of_updates(), 1);
2392        assert!(!rule.has_resets());
2393        assert!(rule.has_decrements());
2394
2395        let rule = LIARule {
2396            id: 2,
2397            source: Location::new("loc1"),
2398            target: Location::new("loc2"),
2399            guard: (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2400                ThresholdConstraintOver::new(
2401                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2402                    ThresholdConstraint::new(
2403                        ThresholdCompOp::Lt,
2404                        Vec::<(Parameter, Fraction)>::new(),
2405                        2,
2406                    ),
2407                ),
2408            )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2409                ThresholdConstraintOver::new(
2410                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2411                    ThresholdConstraint::new(
2412                        ThresholdCompOp::Geq,
2413                        Vec::<(Parameter, Fraction)>::new(),
2414                        1,
2415                    ),
2416                ),
2417            ))),
2418            actions: vec![Action::new(Variable::new("var1"), IntegerExpression::Const(0)).unwrap()],
2419        };
2420
2421        assert_eq!(rule.id(), 2);
2422        assert_eq!(rule.source(), &Location::new("loc1"));
2423        assert_eq!(rule.target(), &Location::new("loc2"));
2424        assert_eq!(
2425            rule.guard(),
2426            &(LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2427                ThresholdConstraintOver::new(
2428                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2429                    ThresholdConstraint::new(
2430                        ThresholdCompOp::Lt,
2431                        Vec::<(Parameter, Fraction)>::new(),
2432                        2,
2433                    ),
2434                ),
2435            )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2436                ThresholdConstraintOver::new(
2437                    WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2438                    ThresholdConstraint::new(
2439                        ThresholdCompOp::Geq,
2440                        Vec::<(Parameter, Fraction)>::new(),
2441                        1,
2442                    ),
2443                ),
2444            )))
2445        );
2446        assert_eq!(
2447            rule.actions().cloned().collect::<Vec<_>>(),
2448            vec![Action::new(Variable::new("var1"), IntegerExpression::Const(0)).unwrap()]
2449        );
2450        assert_eq!(rule.number_of_updates(), 1);
2451        assert!(rule.has_resets());
2452        assert!(!rule.has_decrements());
2453    }
2454
2455    #[test]
2456    fn test_count_number_of_thresholds() {
2457        let thr = LIAVariableConstraint::False;
2458        assert_eq!(thr.count_number_of_thresholds(), 0);
2459
2460        let thr = LIAVariableConstraint::True;
2461        assert_eq!(thr.count_number_of_thresholds(), 0);
2462
2463        let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2464            ThresholdConstraintOver::new(
2465                WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2466                ThresholdConstraint::new(
2467                    ThresholdCompOp::Lt,
2468                    Vec::<(Parameter, Fraction)>::new(),
2469                    2,
2470                ),
2471            ),
2472        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2473            ThresholdConstraintOver::new(
2474                WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2475                ThresholdConstraint::new(
2476                    ThresholdCompOp::Geq,
2477                    Vec::<(Parameter, Fraction)>::new(),
2478                    1,
2479                ),
2480            ),
2481        ));
2482        assert_eq!(thr.count_number_of_thresholds(), 2);
2483
2484        let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2485            ThresholdConstraintOver::new(
2486                WeightedSum::new(BTreeMap::from([
2487                    (Variable::new("var1"), 1),
2488                    (Variable::new("var2"), 2),
2489                ])),
2490                ThresholdConstraint::new(
2491                    ThresholdCompOp::Lt,
2492                    BTreeMap::from([(Parameter::new("n"), 3)]),
2493                    6,
2494                ),
2495            ),
2496        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2497            ThresholdConstraintOver::new(
2498                WeightedSum::new(BTreeMap::from([
2499                    (Variable::new("var1"), 1),
2500                    (Variable::new("var2"), 2),
2501                ])),
2502                ThresholdConstraint::new(
2503                    ThresholdCompOp::Geq,
2504                    BTreeMap::from([(Parameter::new("n"), 3)]),
2505                    5,
2506                ),
2507            ),
2508        ));
2509        assert_eq!(thr.count_number_of_thresholds(), 2);
2510
2511        let thr = (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2512            ThresholdConstraintOver::new(
2513                WeightedSum::new(BTreeMap::from([
2514                    (Variable::new("var1"), 1),
2515                    (Variable::new("var2"), 2),
2516                ])),
2517                ThresholdConstraint::new(
2518                    ThresholdCompOp::Lt,
2519                    BTreeMap::from([(Parameter::new("n"), 3)]),
2520                    1,
2521                ),
2522            ),
2523        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2524            ThresholdConstraintOver::new(
2525                WeightedSum::new(BTreeMap::from([
2526                    (Variable::new("var1"), 1),
2527                    (Variable::new("var2"), 2),
2528                ])),
2529                ThresholdConstraint::new(
2530                    ThresholdCompOp::Geq,
2531                    BTreeMap::from([(Parameter::new("n"), 3)]),
2532                    0,
2533                ),
2534            ),
2535        ))) | (LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2536            ThresholdConstraintOver::new(
2537                WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2538                ThresholdConstraint::new(
2539                    ThresholdCompOp::Lt,
2540                    BTreeMap::from([(Parameter::new("m"), 3)]),
2541                    1,
2542                ),
2543            ),
2544        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2545            ThresholdConstraintOver::new(
2546                WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2547                ThresholdConstraint::new(
2548                    ThresholdCompOp::Geq,
2549                    BTreeMap::from([(Parameter::new("m"), 3)]),
2550                    0,
2551                ),
2552            ),
2553        )));
2554        assert_eq!(thr.count_number_of_thresholds(), 4);
2555    }
2556
2557    #[test]
2558    fn test_bin_op_threshold_guard() {
2559        let thr = LIAVariableConstraint::False & LIAVariableConstraint::True;
2560        assert_eq!(
2561            thr,
2562            LIAVariableConstraint::BinaryGuard(
2563                Box::new(LIAVariableConstraint::False),
2564                BooleanConnective::And,
2565                Box::new(LIAVariableConstraint::True)
2566            )
2567        );
2568
2569        let thr = LIAVariableConstraint::False | LIAVariableConstraint::True;
2570        assert_eq!(
2571            thr,
2572            LIAVariableConstraint::BinaryGuard(
2573                Box::new(LIAVariableConstraint::False),
2574                BooleanConnective::Or,
2575                Box::new(LIAVariableConstraint::True)
2576            )
2577        );
2578    }
2579
2580    #[test]
2581    fn test_guards_to_boolean() {
2582        let guard = LIAVariableConstraint::True;
2583        assert_eq!(guard.as_boolean_expr(), BooleanExpression::True);
2584
2585        let guard = LIAVariableConstraint::False;
2586        assert_eq!(guard.as_boolean_expr(), BooleanExpression::False);
2587
2588        let guard = LIAVariableConstraint::SingleVarConstraint(SingleAtomConstraint(
2589            ThresholdConstraintOver::new(
2590                Variable::new("var1"),
2591                ThresholdConstraint::new(
2592                    ThresholdCompOp::Lt,
2593                    Vec::<(Parameter, Fraction)>::new(),
2594                    1,
2595                ),
2596            ),
2597        ));
2598        assert_eq!(
2599            guard.as_boolean_expr(),
2600            BooleanExpression::ComparisonExpression(
2601                Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2602                ComparisonOp::Lt,
2603                Box::new(IntegerExpression::Const(1))
2604            )
2605        );
2606
2607        let guard = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2608            ThresholdConstraintOver::new(
2609                WeightedSum::new([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
2610                ThresholdConstraint::new(
2611                    ThresholdCompOp::Lt,
2612                    BTreeMap::from([(Parameter::new("n"), 3)]),
2613                    5,
2614                ),
2615            ),
2616        ));
2617        assert_eq!(
2618            guard.as_boolean_expr(),
2619            BooleanExpression::ComparisonExpression(
2620                Box::new(IntegerExpression::BinaryExpr(
2621                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2622                    IntegerOp::Add,
2623                    Box::new(IntegerExpression::BinaryExpr(
2624                        Box::new(IntegerExpression::Const(2)),
2625                        IntegerOp::Mul,
2626                        Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2627                    ))
2628                )),
2629                ComparisonOp::Lt,
2630                Box::new(
2631                    IntegerExpression::BinaryExpr(
2632                        Box::new(IntegerExpression::Const(3)),
2633                        IntegerOp::Mul,
2634                        Box::new(IntegerExpression::Param(Parameter::new("n")))
2635                    ) + IntegerExpression::Const(5)
2636                )
2637            )
2638        );
2639
2640        let guard = LIAVariableConstraint::ComparisonConstraint(ComparisonConstraint(
2641            ThresholdConstraintOver::new(
2642                WeightedSum::new([
2643                    (Variable::new("var1"), 1.into()),
2644                    (Variable::new("var2"), Fraction::from(2)),
2645                ]),
2646                ThresholdConstraint::new(
2647                    ThresholdCompOp::Lt,
2648                    BTreeMap::from([(Parameter::new("n"), 3)]),
2649                    1,
2650                ),
2651            ),
2652        ));
2653        assert_eq!(
2654            guard.as_boolean_expr(),
2655            BooleanExpression::ComparisonExpression(
2656                Box::new(IntegerExpression::BinaryExpr(
2657                    Box::new(IntegerExpression::Atom(Variable::new("var1"))),
2658                    IntegerOp::Add,
2659                    Box::new(IntegerExpression::BinaryExpr(
2660                        Box::new(IntegerExpression::Const(2)),
2661                        IntegerOp::Mul,
2662                        Box::new(IntegerExpression::Atom(Variable::new("var2"))),
2663                    ))
2664                )),
2665                ComparisonOp::Lt,
2666                Box::new(
2667                    IntegerExpression::BinaryExpr(
2668                        Box::new(IntegerExpression::Const(3)),
2669                        IntegerOp::Mul,
2670                        Box::new(IntegerExpression::Param(Parameter::new("n")))
2671                    ) + IntegerExpression::Const(1)
2672                )
2673            )
2674        );
2675
2676        let guard = LIAVariableConstraint::BinaryGuard(
2677            Box::new(LIAVariableConstraint::True),
2678            BooleanConnective::And,
2679            Box::new(LIAVariableConstraint::False),
2680        );
2681        assert_eq!(
2682            guard.as_boolean_expr(),
2683            BooleanExpression::BinaryExpression(
2684                Box::new(BooleanExpression::True),
2685                BooleanConnective::And,
2686                Box::new(BooleanExpression::False)
2687            )
2688        );
2689
2690        let guard = LIAVariableConstraint::BinaryGuard(
2691            Box::new(LIAVariableConstraint::True),
2692            BooleanConnective::Or,
2693            Box::new(LIAVariableConstraint::False),
2694        );
2695        assert_eq!(
2696            guard.as_boolean_expr(),
2697            BooleanExpression::BinaryExpression(
2698                Box::new(BooleanExpression::True),
2699                BooleanConnective::Or,
2700                Box::new(BooleanExpression::False)
2701            )
2702        );
2703    }
2704
2705    #[test]
2706    fn test_threshold_guard_display() {
2707        let thr = LIAVariableConstraint::False;
2708        let expected = "false";
2709        assert_eq!(thr.to_string(), expected);
2710
2711        let thr = LIAVariableConstraint::True;
2712        let expected = "true";
2713        assert_eq!(thr.to_string(), expected);
2714
2715        let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2716            ThresholdConstraintOver::new(
2717                WeightedSum::new(BTreeMap::from([(Variable::new("var1"), 1)])),
2718                ThresholdConstraint::new(
2719                    ThresholdCompOp::Lt,
2720                    Vec::<(Parameter, Fraction)>::new(),
2721                    1,
2722                ),
2723            ),
2724        ));
2725        let expected = "var1 < 1";
2726        assert_eq!(thr.to_string(), expected);
2727
2728        let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2729            ThresholdConstraintOver::new(
2730                WeightedSum::new(BTreeMap::from([
2731                    (Variable::new("var1"), 1),
2732                    (Variable::new("var2"), 2),
2733                ])),
2734                ThresholdConstraint::new(
2735                    ThresholdCompOp::Lt,
2736                    BTreeMap::from([(Parameter::new("n"), 3)]),
2737                    5,
2738                ),
2739            ),
2740        ));
2741        let expected = "var1 + 2 * var2 < 3 * n + 5";
2742        assert_eq!(thr.to_string(), expected);
2743
2744        let thr = LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2745            ThresholdConstraintOver::new(
2746                WeightedSum::new(BTreeMap::from([
2747                    (Variable::new("var1"), 1),
2748                    (Variable::new("var2"), 2),
2749                ])),
2750                ThresholdConstraint::new(
2751                    ThresholdCompOp::Lt,
2752                    BTreeMap::from([(Parameter::new("n"), 3)]),
2753                    0,
2754                ),
2755            ),
2756        )) & LIAVariableConstraint::SumVarConstraint(SumAtomConstraint(
2757            ThresholdConstraintOver::new(
2758                WeightedSum::new(BTreeMap::from([(Variable::new("var3"), 1)])),
2759                ThresholdConstraint::new(
2760                    ThresholdCompOp::Lt,
2761                    BTreeMap::from([(Parameter::new("m"), 3), (Parameter::new("o"), 4)]),
2762                    0,
2763                ),
2764            ),
2765        ));
2766        let expected = "(var1 + 2 * var2 < 3 * n) && (var3 < 3 * m + 4 * o)";
2767        assert_eq!(thr.to_string(), expected);
2768    }
2769
2770    #[test]
2771    fn test_display_single_var_threshold_guard() {
2772        let guard = SingleAtomConstraint::new(
2773            Variable::new("var1"),
2774            ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 5),
2775        );
2776        let expected = "var1 >= 5";
2777        assert_eq!(guard.to_string(), expected);
2778    }
2779
2780    #[test]
2781    fn test_new_multi_var_threshold() {
2782        let guard = SumAtomConstraint::try_new(
2783            [(Variable::new("var1"), 1), (Variable::new("var2"), 2)],
2784            ThresholdConstraint::new(
2785                ThresholdCompOp::Geq,
2786                BTreeMap::from([(Parameter::new("n"), 3)]),
2787                5,
2788            ),
2789        )
2790        .unwrap();
2791
2792        let expected = SumAtomConstraint(ThresholdConstraintOver::new(
2793            WeightedSum::new(BTreeMap::from([
2794                (Variable::new("var1"), 1),
2795                (Variable::new("var2"), 2),
2796            ])),
2797            ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5),
2798        ));
2799
2800        assert_eq!(guard, expected);
2801
2802        let expected_thr = Threshold::new([(Parameter::new("n"), 3)], 5);
2803
2804        assert_eq!(guard.get_threshold(), &expected_thr);
2805        assert_eq!(
2806            guard.get_atoms(),
2807            &WeightedSum::new(BTreeMap::from([
2808                (Variable::new("var1"), 1),
2809                (Variable::new("var2"), 2),
2810            ]))
2811        );
2812    }
2813
2814    #[test]
2815    fn test_new_multi_var_threshold_all_vars_neg() {
2816        let guard = SumAtomConstraint::try_new(
2817            BTreeMap::from([
2818                (Variable::new("var1"), -Fraction::from(1)),
2819                (Variable::new("var2"), -Fraction::from(2)),
2820            ]),
2821            ThresholdConstraint::new(
2822                ThresholdCompOp::Geq,
2823                BTreeMap::from([(Parameter::new("n"), 3)]),
2824                5,
2825            ),
2826        )
2827        .unwrap();
2828
2829        let expected = SumAtomConstraint(ThresholdConstraintOver::new(
2830            WeightedSum::new(BTreeMap::from([
2831                (Variable::new("var1"), 1),
2832                (Variable::new("var2"), 2),
2833            ])),
2834            ThresholdConstraint::new(
2835                ThresholdCompOp::Lt,
2836                BTreeMap::from([(Parameter::new("n"), -Fraction::from(3))]),
2837                -Fraction::from(6),
2838            ),
2839        ));
2840
2841        assert_eq!(guard, expected);
2842    }
2843
2844    #[test]
2845    fn test_new_multi_var_threshold_err_single() {
2846        let guard = SumAtomConstraint::try_new(
2847            BTreeMap::from([(Variable::new("var1"), 1)]),
2848            ThresholdConstraint::new(
2849                ThresholdCompOp::Geq,
2850                BTreeMap::from([(Parameter::new("n"), 3)]),
2851                5,
2852            ),
2853        );
2854
2855        assert!(matches!(
2856            guard.clone(),
2857            Err(SumVarConstraintCreationError::IsSingleAtomConstraint)
2858        ));
2859        assert!(
2860            guard
2861                .unwrap_err()
2862                .to_string()
2863                .contains("SingleVarThresholdGuard")
2864        );
2865    }
2866
2867    #[test]
2868    fn test_new_multi_var_threshold_err_mixed() {
2869        let guard = SumAtomConstraint::try_new(
2870            BTreeMap::from([
2871                (Variable::new("var1"), 1.into()),
2872                (Variable::new("var2"), -Fraction::from(2)),
2873            ]),
2874            ThresholdConstraint::new(
2875                ThresholdCompOp::Geq,
2876                BTreeMap::from([(Parameter::new("n"), 3)]),
2877                5,
2878            ),
2879        );
2880
2881        assert!(matches!(
2882            guard.clone(),
2883            Err(SumVarConstraintCreationError::IsComparisonConstraint)
2884        ));
2885        assert!(guard.unwrap_err().to_string().contains("ComparisonGuard"));
2886    }
2887
2888    #[test]
2889    fn test_display_multi_var_threshold_guard() {
2890        let guard = SumAtomConstraint::try_new(
2891            BTreeMap::from([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
2892            ThresholdConstraint::new(
2893                ThresholdCompOp::Geq,
2894                BTreeMap::from([(Parameter::new("n"), 3)]),
2895                5,
2896            ),
2897        )
2898        .unwrap();
2899        let expected = "var1 + 2 * var2 >= 3 * n + 5";
2900        assert_eq!(guard.to_string(), expected);
2901    }
2902
2903    #[test]
2904    fn test_new_comparison_guard() {
2905        let guard = ComparisonConstraint::try_new(
2906            BTreeMap::from([
2907                (Variable::new("var1"), 1.into()),
2908                (Variable::new("var2"), -Fraction::from(2)),
2909            ]),
2910            ThresholdConstraint::new(
2911                ThresholdCompOp::Geq,
2912                BTreeMap::from([(Parameter::new("n"), 3)]),
2913                5,
2914            ),
2915        )
2916        .unwrap();
2917
2918        let expected = ComparisonConstraint(ThresholdConstraintOver::new(
2919            WeightedSum::new(BTreeMap::from([
2920                (Variable::new("var1"), 1.into()),
2921                (Variable::new("var2"), -Fraction::from(2)),
2922            ])),
2923            ThresholdConstraint::new(
2924                ThresholdCompOp::Geq,
2925                BTreeMap::from([(Parameter::new("n"), 3)]),
2926                5,
2927            ),
2928        ));
2929
2930        assert_eq!(guard, expected);
2931    }
2932
2933    #[test]
2934    fn test_comparison_guard_get_threshold_constr() {
2935        let guard = ComparisonConstraint(ThresholdConstraintOver::new(
2936            WeightedSum::from([
2937                (Variable::new("var1"), 1.into()),
2938                (Variable::new("var2"), -Fraction::from(2)),
2939            ]),
2940            ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5),
2941        ));
2942
2943        assert_eq!(
2944            guard.get_threshold_constraint(),
2945            &ThresholdConstraint::new(ThresholdCompOp::Geq, [(Parameter::new("n"), 3)], 5)
2946        )
2947    }
2948
2949    #[test]
2950    fn test_new_comparison_guard_err_multi() {
2951        let guard = ComparisonConstraint::try_new(
2952            BTreeMap::from([
2953                (Variable::new("var1"), 1.into()),
2954                (Variable::new("var2"), 2.into()),
2955            ]),
2956            ThresholdConstraint::new(
2957                ThresholdCompOp::Geq,
2958                BTreeMap::from([(Parameter::new("n"), 3)]),
2959                5,
2960            ),
2961        );
2962
2963        assert!(matches!(
2964            guard.clone(),
2965            Err(ComparisonConstraintCreationError::IsSumVarConstraint)
2966        ));
2967        assert!(
2968            guard
2969                .unwrap_err()
2970                .to_string()
2971                .contains("MultiVarThresholdGuard")
2972        );
2973    }
2974
2975    #[test]
2976    fn test_display_comparison_guard() {
2977        let guard = ComparisonConstraint::try_new(
2978            BTreeMap::from([
2979                (Variable::new("var1"), 1.into()),
2980                (Variable::new("var2"), -Fraction::from(2)),
2981            ]),
2982            ThresholdConstraint::new(
2983                ThresholdCompOp::Geq,
2984                BTreeMap::from([(Parameter::new("n"), 3)]),
2985                5,
2986            ),
2987        )
2988        .unwrap();
2989        let expected = "var1 + -2 * var2 >= 3 * n + 5";
2990        assert_eq!(guard.to_string(), expected);
2991    }
2992
2993    #[test]
2994    fn test_lower_and_upper_guard() {
2995        let lower_thr = ThresholdConstraint::new(
2996            ThresholdCompOp::Geq,
2997            BTreeMap::from([(Parameter::new("n"), 3)]),
2998            5,
2999        );
3000
3001        let upper_thr = ThresholdConstraint::new(
3002            ThresholdCompOp::Lt,
3003            BTreeMap::from([(Parameter::new("n"), 3)]),
3004            4,
3005        );
3006
3007        let guard = LIAVariableConstraint::ComparisonConstraint(
3008            ComparisonConstraint::try_new(
3009                BTreeMap::from([
3010                    (Variable::new("var1"), 1.into()),
3011                    (Variable::new("var2"), -Fraction::from(2)),
3012                ]),
3013                lower_thr.clone(),
3014            )
3015            .unwrap(),
3016        );
3017        assert!(guard.is_lower_guard());
3018        assert!(!guard.is_upper_guard());
3019
3020        let guard = LIAVariableConstraint::ComparisonConstraint(
3021            ComparisonConstraint::try_new(
3022                BTreeMap::from([
3023                    (Variable::new("var1"), 1.into()),
3024                    (Variable::new("var2"), -Fraction::from(2)),
3025                ]),
3026                upper_thr.clone(),
3027            )
3028            .unwrap(),
3029        );
3030        assert!(!guard.is_lower_guard());
3031        assert!(guard.is_upper_guard());
3032
3033        let guard = SingleAtomConstraint::new(Variable::new("var1"), lower_thr.clone());
3034        assert!(guard.is_lower_guard());
3035        assert!(!guard.is_upper_guard());
3036
3037        let guard = SingleAtomConstraint::new(Variable::new("var1"), upper_thr.clone());
3038        assert!(!guard.is_lower_guard());
3039        assert!(guard.is_upper_guard());
3040
3041        let guard = LIAVariableConstraint::SumVarConstraint(
3042            SumAtomConstraint::try_new(
3043                BTreeMap::from([
3044                    (Variable::new("var1"), 1.into()),
3045                    (Variable::new("var2"), Fraction::from(2)),
3046                ]),
3047                lower_thr.clone(),
3048            )
3049            .unwrap(),
3050        );
3051        assert!(guard.is_lower_guard());
3052        assert!(!guard.is_upper_guard());
3053
3054        let guard = LIAVariableConstraint::SumVarConstraint(
3055            SumAtomConstraint::try_new(
3056                BTreeMap::from([
3057                    (Variable::new("var1"), 1.into()),
3058                    (Variable::new("var2"), Fraction::from(2)),
3059                ]),
3060                upper_thr.clone(),
3061            )
3062            .unwrap(),
3063        );
3064        assert!(!guard.is_lower_guard());
3065        assert!(guard.is_upper_guard());
3066
3067        let guard = LIAVariableConstraint::True;
3068        assert!(!guard.is_lower_guard());
3069        assert!(!guard.is_upper_guard());
3070
3071        let guard = LIAVariableConstraint::False;
3072        assert!(!guard.is_lower_guard());
3073        assert!(!guard.is_upper_guard());
3074
3075        let guard = LIAVariableConstraint::BinaryGuard(
3076            Box::new(LIAVariableConstraint::True),
3077            BooleanConnective::And,
3078            Box::new(LIAVariableConstraint::False),
3079        );
3080        assert!(!guard.is_lower_guard());
3081        assert!(!guard.is_upper_guard());
3082
3083        let guard = LIAVariableConstraint::BinaryGuard(
3084            Box::new(LIAVariableConstraint::SingleVarConstraint(
3085                SingleAtomConstraint::new(Variable::new("var1"), lower_thr.clone()),
3086            )),
3087            BooleanConnective::Or,
3088            Box::new(LIAVariableConstraint::SingleVarConstraint(
3089                SingleAtomConstraint::new(Variable::new("var1"), upper_thr.clone()),
3090            )),
3091        );
3092        assert!(guard.is_lower_guard());
3093        assert!(guard.is_upper_guard());
3094    }
3095
3096    #[test]
3097    fn test_getters_single_var_thr() {
3098        let guard = SingleAtomConstraint::new(
3099            Variable::new("var1"),
3100            ThresholdConstraint::new(ThresholdCompOp::Geq, Vec::<(Parameter, Fraction)>::new(), 5),
3101        );
3102
3103        assert_eq!(guard.get_atom(), &Variable::new("var1"));
3104        assert_eq!(
3105            guard.get_threshold(),
3106            &Threshold::new(Vec::<(Parameter, Fraction)>::new(), 5)
3107        );
3108    }
3109
3110    #[test]
3111    fn test_getters_multi_var_thr() {
3112        let guard = SumAtomConstraint::try_new(
3113            BTreeMap::from([(Variable::new("var1"), 1), (Variable::new("var2"), 2)]),
3114            ThresholdConstraint::new(
3115                ThresholdCompOp::Geq,
3116                BTreeMap::from([(Parameter::new("n"), 3)]),
3117                5,
3118            ),
3119        )
3120        .unwrap();
3121
3122        assert_eq!(
3123            guard.get_atoms(),
3124            &WeightedSum::new(BTreeMap::from([
3125                (Variable::new("var1"), 1),
3126                (Variable::new("var2"), 2),
3127            ]))
3128        );
3129        assert_eq!(
3130            guard.get_threshold(),
3131            &Threshold::new(BTreeMap::from([(Parameter::new("n"), 3)]), 5,)
3132        );
3133    }
3134
3135    #[test]
3136    fn test_display_lia_transformation_error() {
3137        let error = LIATransformationError::GuardError(
3138            Box::new(RuleBuilder::new(0, Location::new("l1"), Location::new("l2")).build()),
3139            Box::new(BooleanExpression::False),
3140            Box::new(ConstraintRewriteError::NotLinearArithmetic),
3141        );
3142        let expected = "Error while transforming guard false of rule 0: l1 -> l2\n    when ( true )\n    do {  }: Non linear integer arithmetic expression detected. Threshold automata only allow for linear arithmetic formula in their guards.";
3143
3144        assert_eq!(error.to_string(), expected);
3145
3146        let error = LIATransformationError::InitialConstraintError(
3147            Box::new(BooleanExpression::False),
3148            Box::new(ConstraintRewriteError::NotLinearArithmetic),
3149        );
3150        let expected = "Error while transforming initial constraint false into linear arithmetic: Non linear integer arithmetic expression detected. Threshold automata only allow for linear arithmetic formula in their guards.";
3151        assert_eq!(error.to_string(), expected);
3152    }
3153}