taco_acs_model_checker/acs_threshold_automaton/
configuration.rs

1//! Configurations of a [`ACSThresholdAutomaton`]
2//!
3//! This module contains the type definitions for configurations of a
4//! [ `ACSThresholdAutomaton`]. These types are designed to have a small memory
5//! footprint and should be easily computable.
6//! This also means that when working with configurations, there is no extensive
7//! validation that is performed. In particular, one must ensure that
8//! configurations, locations, and intervals etc. from different threshold
9//! automata are not mixed. Otherwise panics can arise, and the results will not
10//! have any meaning.
11
12use std::{
13    collections::HashSet,
14    ops::{Index, IndexMut},
15    vec,
16};
17
18use taco_display_utils::display_iterator_stable_order;
19use taco_interval_ta::{IntervalActionEffect, IntervalConstraint};
20use taco_model_checker::reachability_specification::{DisjunctionTargetConfig, TargetConfig};
21use taco_smt_encoder::{
22    SMTExpr, SMTSolver,
23    expression_encoding::{EncodeToSMT, SMTVariableContext},
24};
25use taco_threshold_automaton::expressions::{Location, Parameter, Variable};
26
27use crate::{
28    acs_threshold_automaton::{
29        ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalAction, CSRule, CSVariable,
30    },
31    partial_ord::{PartialOrdCompResult, PartialOrder},
32};
33
34pub(crate) mod partially_ordered_cfg_map;
35
36/// Configuration of a [`ACSThresholdAutomaton`]
37///
38/// This struct represents a configuration, i.e. an assignment of intervals for
39/// all shared variables and a number of processes in each location.
40///
41/// Use these configurations with care. Configurations of different automata
42/// should never be indexed by locations, variables or intervals of other
43/// automata.
44#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
45pub struct ACSTAConfig {
46    /// Location state specifying how many processes are in each location
47    loc_state: ACSLocState,
48    /// Interval state specifying the current interval for each variable
49    interval_state: ACSIntervalState,
50}
51
52impl ACSTAConfig {
53    /// Get the interval state of the configuration
54    pub fn interval_state(&self) -> &ACSIntervalState {
55        &self.interval_state
56    }
57
58    /// Get the location state of the configuration
59    pub fn location_state(&self) -> &ACSLocState {
60        &self.loc_state
61    }
62
63    /// Check whether the current configuration is an initial configuration of
64    /// the threshold automaton
65    pub fn is_initial(&self, ta: &ACSThresholdAutomaton) -> bool {
66        let locs_all_init = self
67            .loc_state
68            .into_iterator_loc_n_procs()
69            .filter(|(_, n_procs)| *n_procs > 0)
70            .all(|(loc, _)| ta.is_initial_loc(&loc));
71
72        let intervals_all_init = self
73            .interval_state
74            .into_iterator_variable_interval()
75            .all(|(var, interval)| ta.is_initial_interval(&var, &interval));
76
77        locs_all_init && intervals_all_init
78    }
79
80    /// Compute all predecessors of the configuration that could stem from
81    /// rule `r` being applied
82    ///
83    /// Note that the iterator returned here might contain comparable elements.
84    /// Computing the *minimal* basis must be handled when collecting this
85    /// iterator
86    /// This function returns `None` if the rule contains a reset, and the
87    /// variable the reset has been applied to is not in its 0 interval.
88    pub fn compute_potential_predecessors(
89        &self,
90        rule: &CSRule,
91        ta: &ACSThresholdAutomaton,
92    ) -> Option<impl Iterator<Item = Self>> {
93        let res_acts = rule
94            .actions()
95            .filter(|a| a.effect() == &IntervalActionEffect::Reset)
96            .collect::<Vec<_>>();
97        if !res_acts.is_empty() {
98            for res_act in res_acts.into_iter() {
99                let reset_var = res_act.variable();
100                if self.interval_state[reset_var] != ta.get_zero_interval(reset_var) {
101                    return None;
102                }
103            }
104        }
105
106        let location_state = self.loc_state.compute_predecessors_min_basis(rule);
107
108        Some(
109            self.interval_state
110                .compute_all_predecessor_configs(rule, ta)
111                .into_iter()
112                .filter(|is| rule.guard().is_satisfied(is))
113                .map(move |interval_cfg| Self {
114                    loc_state: location_state.clone(),
115                    interval_state: interval_cfg,
116                }),
117        )
118    }
119
120    /// Compute all configurations that correspond to an error state in the
121    /// given disjunction over target configurations
122    pub fn from_disjunct_target(
123        spec: &DisjunctionTargetConfig,
124        ta: &ACSThresholdAutomaton,
125    ) -> impl Iterator<Item = ACSTAConfig> {
126        spec.get_target_configs()
127            .flat_map(|target| Self::from_target_config(target, ta))
128    }
129
130    /// Compute a goal configuration from a single [`TargetConfig`]
131    pub fn from_target_config(spec: &TargetConfig, ta: &ACSThresholdAutomaton) -> HashSet<Self> {
132        let interval_constr = ta
133            .interval_ta
134            .get_interval_constraint(spec.get_variable_constraint())
135            .expect("Failed to derive interval constraint for target");
136
137        let interval_cfgs = ACSIntervalState::get_target_interval_configs(interval_constr, ta);
138
139        let location_cfg = ACSLocState::compute_target_cfg(spec, ta);
140
141        interval_cfgs
142            .into_iter()
143            .map(|interval_cfg| Self {
144                loc_state: location_cfg.clone(),
145                interval_state: interval_cfg,
146            })
147            .collect()
148    }
149
150    /// Get a string representation of the configuration
151    pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
152        format!(
153            "locations: ({}), variables: ({})",
154            self.loc_state.display(ta),
155            self.interval_state.display(ta)
156        )
157    }
158
159    /// Get a compact string representation of the configuration without
160    /// locations that are not covered by processes or variables that are zero
161    pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
162        format!(
163            "locations: ({}), variables: ({})",
164            self.loc_state.display_compact(ta),
165            self.interval_state.display_compact(ta)
166        )
167    }
168
169    /// Encode the conditions this abstract configuration places on the
170    /// corresponding concrete configurations into an SMT expression
171    pub fn encode_config_constraints_to_smt<
172        C: SMTVariableContext<Parameter> + SMTVariableContext<Variable> + SMTVariableContext<Location>,
173    >(
174        &self,
175        ta: &ACSThresholdAutomaton,
176        solver: &SMTSolver,
177        ctx: &C,
178    ) -> SMTExpr {
179        // encode at least required number of procs
180        let loc_constr = self
181            .location_state()
182            .into_iterator_loc_n_procs()
183            .filter(|(_, n)| *n > 0)
184            .map(|(loc, n)| {
185                let loc = ctx
186                    .get_expr_for(ta.idx_ctx.get_loc(&loc))
187                    .expect("Expected location SMT variable to be declared");
188
189                solver.gte(loc, solver.numeral(n))
190            });
191
192        let interval_constr = self
193            .interval_state()
194            .into_iterator_variable_interval()
195            .filter(|(v, _)| {
196                // We do not encode intervals of the replacement variables as
197                // these variables do not appear in the original automaton but
198                // we use the original automaton in the encoding.
199                // Correctness of the interval abstraction is guaranteed because
200                // the single variable behaves the same as the sum of two
201                // variables and the correctness of steps is ensured by encoding
202                // the rule guards
203                // TODO: remove once ordering support is ready
204                !ta.interval_ta
205                    .get_helper_vars_for_sumvars()
206                    .contains_key(ta.idx_ctx.get_var(v))
207            })
208            .map(|(var, i)| {
209                let i = ta.idx_ctx.get_interval(&var, &i);
210                let var = ta.idx_ctx.get_var(&var);
211
212                let interval_expr = i.encode_into_boolean_expr(var);
213
214                interval_expr
215                    .encode_to_smt_with_ctx(solver, ctx)
216                    .expect("Failed to encode interval constraint")
217            });
218
219        solver.and_many(loc_constr.chain(interval_constr))
220    }
221}
222
223impl PartialOrder for ACSTAConfig {
224    #[inline(always)]
225    fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
226        let interval_comp = self.interval_state.part_cmp(&other.interval_state);
227        if interval_comp == PartialOrdCompResult::Incomparable {
228            return PartialOrdCompResult::Incomparable;
229        }
230
231        let loc_comp = self.loc_state.part_cmp(&other.loc_state);
232        interval_comp.combine(loc_comp)
233    }
234}
235
236/// Assignment of variables to their current value
237///
238/// An interval state represents a valuation of the shared variables of a
239/// threshold automaton with interval abstraction.
240///
241/// Use this type with care. `IntervalState`s of different automata
242/// should never be indexed by variables or intervals of other automata.
243#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
244pub struct ACSIntervalState {
245    /// Intervals of the configuration
246    /// Index of the entry represents the variable
247    v_to_interval: Vec<ACSInterval>,
248}
249
250impl ACSIntervalState {
251    pub fn into_iterator_variable_interval(
252        &self,
253    ) -> impl Iterator<Item = (CSVariable, ACSInterval)> {
254        self.v_to_interval
255            .iter()
256            .enumerate()
257            .map(|(var, interval)| (CSVariable(var), *interval))
258    }
259
260    /// Create a new interval configuration where all variables are in the
261    /// interval mapped to zero
262    ///
263    /// The resulting configuration of this function should not be used directly
264    /// as it does not necessarily correspond to a valid configuration. It
265    /// should only be used for initialization purposes.
266    fn new_cfg_all_zero_interval(ta: &ACSThresholdAutomaton) -> Self {
267        Self {
268            v_to_interval: vec![ACSInterval(0); ta.variables().count()],
269        }
270    }
271
272    /// Compute all interval states that are possible in the threshold automaton
273    pub fn all_possible_interval_configs(ta: &ACSThresholdAutomaton) -> Vec<ACSIntervalState> {
274        let zero_interval = Vec::from([Self::new_cfg_all_zero_interval(ta)]);
275
276        ta.variables().fold(zero_interval, |cfgs, var| {
277            cfgs.iter()
278                .flat_map(|cfg| {
279                    ta.get_all_intervals(var).map(|interval| {
280                        let mut new_cfg = cfg.clone();
281                        new_cfg[var] = *interval;
282                        new_cfg
283                    })
284                })
285                .collect()
286        })
287    }
288
289    /// Compute all interval states that are allowed by the given interval
290    /// constraint in the threshold automaton
291    pub fn get_target_interval_configs(
292        constr: IntervalConstraint,
293        ta: &ACSThresholdAutomaton,
294    ) -> Vec<ACSIntervalState> {
295        if constr == IntervalConstraint::True {
296            return Self::all_possible_interval_configs(ta);
297        }
298
299        let zero_interval = Vec::from([Self::new_cfg_all_zero_interval(ta)]);
300
301        ta.variables().fold(zero_interval, |cfgs, var| {
302            cfgs.iter()
303                .flat_map(|cfg| {
304                    let org_var = ta.idx_ctx.get_var(var);
305
306                    ta.get_interval_ta()
307                        .get_enabled_intervals(&constr, org_var)
308                        .map(|interval| {
309                            let interval = ta.idx_ctx.to_cs_interval(var, interval);
310                            let mut new_cfg = cfg.clone();
311                            new_cfg[var] = interval;
312                            new_cfg
313                        })
314                })
315                .collect()
316        })
317    }
318
319    /// Compute all potential predecessor interval configurations
320    pub fn compute_all_predecessor_configs(
321        &self,
322        rule: &CSRule,
323        ta: &ACSThresholdAutomaton,
324    ) -> Vec<Self> {
325        // for each action add the predecessor where the action had an affect on
326        // the interval, but also the interval state, where the interval was unaffected
327        rule.actions()
328            .fold(Vec::from([self.clone()]), |predecessors, act| {
329                // for each computed predecessor compute intervals before action
330                predecessors
331                    .into_iter()
332                    .flat_map(|interval_cfg| {
333                        Self::compute_possible_interval_state_before_application_of_act(
334                            &interval_cfg,
335                            act,
336                            ta,
337                        )
338                    })
339                    .collect()
340            })
341    }
342
343    /// Compute all possible interval configurations before the application of
344    /// the action
345    ///
346    /// Concretely, for
347    /// - Increment actions: The increment could have lead to jumping to the
348    ///   next interval, or could have stayed in the current interval.
349    /// - Decrement actions: The decrement could have lead to a jump to a
350    ///   smaller interval or could have stayed in the current interval.
351    /// - Reset actions: A reset will always go to the 0 interval.
352    fn compute_possible_interval_state_before_application_of_act(
353        &self,
354        act: &CSIntervalAction,
355        ta: &ACSThresholdAutomaton,
356    ) -> HashSet<Self> {
357        let mut possible_pred_intervals = HashSet::new();
358
359        match act.effect() {
360            IntervalActionEffect::Inc(i) => {
361                let prev_interval = ta.get_prev_interval(act.variable(), &self[act.variable()]);
362
363                // check whether we could have stayed in the interval
364                // if yes, add back the current interval state
365                if !ta
366                    .idx_ctx
367                    .get_interval(act.variable(), &self[act.variable()])
368                    .check_sub_always_out_of_interval(*i)
369                {
370                    possible_pred_intervals.insert(self.clone());
371                }
372
373                // check if a smaller interval exists, if yes insert the interval
374                // state with `act.variable()` is in that interval
375                if let Some(prev_interval) = prev_interval {
376                    let mut prev_interval_cfg = self.clone();
377                    prev_interval_cfg[act.variable()] = prev_interval;
378
379                    possible_pred_intervals.insert(prev_interval_cfg);
380                }
381            }
382            IntervalActionEffect::Dec(d) => {
383                let next_interval = ta.get_next_interval(act.variable(), &self[act.variable()]);
384
385                // check whether we could have stayed in the interval
386                // if yes, add back the current interval state
387                if !ta
388                    .idx_ctx
389                    .get_interval(act.variable(), &self[act.variable()])
390                    .check_add_always_out_of_interval(*d)
391                {
392                    possible_pred_intervals.insert(self.clone());
393                }
394
395                // check if a bigger interval exists, if yes insert the interval
396                // state with `act.variable()` is in that interval
397                if let Some(nxt_interval) = next_interval {
398                    let mut nxt_interval_cfg = self.clone();
399                    nxt_interval_cfg[act.variable()] = nxt_interval;
400
401                    possible_pred_intervals.insert(nxt_interval_cfg);
402                }
403            }
404            IntervalActionEffect::Reset => {
405                // the previous
406                let potential_intervals = ta.get_all_intervals(act.variable()).map(|interval| {
407                    let mut new_cfg = self.clone();
408                    new_cfg[act.variable()] = *interval;
409
410                    new_cfg
411                });
412
413                possible_pred_intervals.extend(potential_intervals);
414            }
415        }
416
417        possible_pred_intervals
418    }
419
420    /// Get a string representation of the interval state
421    pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
422        display_iterator_stable_order(
423            self.into_iterator_variable_interval()
424                .map(|(var, interval)| {
425                    format!("{} : {}", var.display(ta), interval.display(&var, ta))
426                }),
427        )
428    }
429
430    /// Get a string representation of the interval state, leaving out variables
431    /// in their zero interval
432    pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
433        display_iterator_stable_order(
434            self.into_iterator_variable_interval()
435                .filter(|(var, interval)| ta.get_zero_interval(var) != *interval)
436                .map(|(var, interval)| {
437                    format!("{} : {}", var.display(ta), interval.display(&var, ta))
438                }),
439        )
440    }
441}
442
443impl Index<&CSVariable> for ACSIntervalState {
444    type Output = ACSInterval;
445
446    fn index(&self, var: &CSVariable) -> &Self::Output {
447        &self.v_to_interval[var.0]
448    }
449}
450
451impl IndexMut<&CSVariable> for ACSIntervalState {
452    fn index_mut(&mut self, var: &CSVariable) -> &mut Self::Output {
453        &mut self.v_to_interval[var.0]
454    }
455}
456
457impl Index<CSVariable> for ACSIntervalState {
458    type Output = ACSInterval;
459
460    fn index(&self, var: CSVariable) -> &Self::Output {
461        &self.v_to_interval[var.0]
462    }
463}
464
465impl IndexMut<CSVariable> for ACSIntervalState {
466    fn index_mut(&mut self, var: CSVariable) -> &mut Self::Output {
467        &mut self.v_to_interval[var.0]
468    }
469}
470
471impl PartialOrder for ACSInterval {
472    #[inline(always)]
473    fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
474        if self == other {
475            return PartialOrdCompResult::Equal;
476        }
477        PartialOrdCompResult::Incomparable
478    }
479}
480
481impl PartialOrder for ACSIntervalState {
482    fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
483        self.v_to_interval.part_cmp(&other.v_to_interval)
484    }
485}
486
487/// Assignment of process counts to a location
488///
489/// The LocationState tracks how many processes are in each location of the
490/// threshold automaton.
491///
492/// Use this type with care. `LocationState`s of different automata
493/// should never be indexed by locations of other automata.
494#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
495pub struct ACSLocState {
496    /// Vector counting how many processes are in each location
497    loc_state: Vec<u32>,
498}
499
500impl ACSLocState {
501    /// Iterate over the configurations and the number of processes in these locations
502    pub fn into_iterator_loc_n_procs(&self) -> impl Iterator<Item = (ACSLocation, u32)> {
503        self.loc_state
504            .iter()
505            .enumerate()
506            .map(|(i, n)| (ACSLocation(i), *n))
507    }
508
509    /// Compute the minimal basis of the predecessor
510    ///
511    /// This function assumes that `self` is the minimal basis of an upwards
512    /// closed set of locations and computes the minimal basis of a state from
513    /// which an element in `self` can be reached using `CSRule`.
514    ///
515    /// In particular this means that the predecessor generated here, can have
516    /// more processes than `self`.
517    pub fn compute_predecessors_min_basis(&self, rule: &CSRule) -> Self {
518        let mut new_loc_state = self.clone();
519
520        if self[rule.target()] > 0 {
521            new_loc_state[rule.target()] -= 1;
522        }
523
524        new_loc_state[rule.source()] += 1;
525
526        new_loc_state
527    }
528
529    fn new_all_zero(ta: &ACSThresholdAutomaton) -> Self {
530        Self {
531            loc_state: vec![0; ta.locations().count()],
532        }
533    }
534
535    /// Get the [`ACSLocState`] that corresponds to the target configuration
536    pub fn compute_target_cfg(spec: &TargetConfig, ta: &ACSThresholdAutomaton) -> Self {
537        debug_assert!(
538            spec.get_locations_to_uncover().count() == 0,
539            "This model checker currently does not support reachability constraints, this should have been caught"
540        );
541
542        let mut target = Self::new_all_zero(ta);
543
544        for (loc, n) in spec.get_locations_to_cover() {
545            target[ta.idx_ctx.to_cs_loc(loc)] = *n;
546        }
547
548        target
549    }
550
551    /// Get a string representation of the location state
552    pub fn display(&self, ta: &ACSThresholdAutomaton) -> String {
553        display_iterator_stable_order(
554            self.into_iterator_loc_n_procs()
555                .map(|(l, n)| format!("{} : {}", l.display(ta), n)),
556        )
557    }
558
559    /// Get a string representation of the location state, leaving out locations
560    /// with zero processes in them
561    pub fn display_compact(&self, ta: &ACSThresholdAutomaton) -> String {
562        display_iterator_stable_order(
563            self.into_iterator_loc_n_procs()
564                .filter(|(_, n)| *n > 0)
565                .map(|(l, n)| format!("{} : {}", l.display(ta), n)),
566        )
567    }
568}
569
570impl Index<&ACSLocation> for ACSLocState {
571    type Output = u32;
572
573    fn index(&self, loc: &ACSLocation) -> &Self::Output {
574        &self.loc_state[loc.0]
575    }
576}
577
578impl IndexMut<&ACSLocation> for ACSLocState {
579    fn index_mut(&mut self, loc: &ACSLocation) -> &mut Self::Output {
580        &mut self.loc_state[loc.0]
581    }
582}
583
584impl Index<ACSLocation> for ACSLocState {
585    type Output = u32;
586
587    fn index(&self, loc: ACSLocation) -> &Self::Output {
588        &self.loc_state[loc.0]
589    }
590}
591
592impl IndexMut<ACSLocation> for ACSLocState {
593    fn index_mut(&mut self, loc: ACSLocation) -> &mut Self::Output {
594        &mut self.loc_state[loc.0]
595    }
596}
597
598impl PartialOrder for ACSLocState {
599    fn part_cmp(&self, other: &Self) -> PartialOrdCompResult {
600        self.loc_state.part_cmp(&other.loc_state)
601    }
602}
603
604#[cfg(test)]
605mod mock_objects {
606    use crate::acs_threshold_automaton::{
607        ACSInterval, ACSThresholdAutomaton,
608        configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
609    };
610
611    impl ACSTAConfig {
612        pub fn new_mock_from_vecs(loc: Vec<u32>, intervals: Vec<ACSInterval>) -> Self {
613            ACSTAConfig {
614                loc_state: super::ACSLocState { loc_state: loc },
615                interval_state: super::ACSIntervalState {
616                    v_to_interval: intervals,
617                },
618            }
619        }
620
621        pub fn new_mock(loc: ACSLocState, int: ACSIntervalState) -> Self {
622            Self {
623                loc_state: loc,
624                interval_state: int,
625            }
626        }
627    }
628
629    impl ACSLocState {
630        pub fn new_empty(ta: &ACSThresholdAutomaton) -> Self {
631            Self::new_all_zero(ta)
632        }
633    }
634
635    impl ACSIntervalState {
636        pub fn new_mock_from_vec(intervals: Vec<ACSInterval>) -> Self {
637            ACSIntervalState {
638                v_to_interval: intervals,
639            }
640        }
641
642        pub fn new_mock_zero(ta: &ACSThresholdAutomaton) -> Self {
643            Self::new_cfg_all_zero_interval(ta)
644        }
645
646        pub fn get_all_possible_intervals(ta: &ACSThresholdAutomaton) -> Vec<ACSIntervalState> {
647            Self::all_possible_interval_configs(ta)
648        }
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use std::collections::{HashMap, HashSet};
655
656    use taco_interval_ta::{
657        IntervalActionEffect, IntervalConstraint, builder::IntervalTABuilder, interval::Interval,
658    };
659    use taco_model_checker::reachability_specification::{DisjunctionTargetConfig, TargetConfig};
660    use taco_smt_encoder::SMTSolverBuilder;
661    use taco_threshold_automaton::{
662        expressions::{
663            BooleanExpression, ComparisonOp, IntegerExpression, Location, Parameter, Variable,
664        },
665        general_threshold_automaton::{
666            Action,
667            builder::{GeneralThresholdAutomatonBuilder, RuleBuilder},
668        },
669        lia_threshold_automaton::LIAThresholdAutomaton,
670    };
671
672    use crate::{
673        acs_threshold_automaton::{
674            ACSInterval, ACSLocation, ACSThresholdAutomaton, CSIntervalAction,
675            CSIntervalConstraint, CSRule, CSVariable,
676            configuration::{ACSIntervalState, ACSLocState, ACSTAConfig},
677        },
678        partial_ord::{PartialOrdCompResult, PartialOrder},
679    };
680
681    #[test]
682    fn test_cstaconfig_getters() {
683        let config = ACSTAConfig {
684            loc_state: ACSLocState {
685                loc_state: vec![1, 2, 3],
686            },
687            interval_state: ACSIntervalState {
688                v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
689            },
690        };
691        assert_eq!(
692            config.location_state(),
693            &ACSLocState {
694                loc_state: vec![1, 2, 3],
695            }
696        );
697        assert_eq!(
698            config.interval_state(),
699            &ACSIntervalState {
700                v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
701            }
702        );
703    }
704
705    #[test]
706    fn test_cstaconfig_is_initial() {
707        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
708            .with_variables([Variable::new("x"), Variable::new("y")])
709            .unwrap()
710            .with_locations([
711                Location::new("l1"),
712                Location::new("l2"),
713                Location::new("l3"),
714            ])
715            .unwrap()
716            .with_parameter(Parameter::new("n"))
717            .unwrap()
718            .initialize()
719            .with_resilience_condition(BooleanExpression::ComparisonExpression(
720                Box::new(IntegerExpression::Param(Parameter::new("n"))),
721                ComparisonOp::Gt,
722                Box::new(IntegerExpression::Const(3)),
723            ))
724            .unwrap()
725            .with_initial_location_constraints([
726                BooleanExpression::ComparisonExpression(
727                    Box::new(IntegerExpression::Atom(Location::new("l2"))),
728                    ComparisonOp::Eq,
729                    Box::new(IntegerExpression::Const(0)),
730                ),
731                BooleanExpression::ComparisonExpression(
732                    Box::new(IntegerExpression::Atom(Location::new("l3"))),
733                    ComparisonOp::Eq,
734                    Box::new(IntegerExpression::Const(0)),
735                ),
736            ])
737            .unwrap()
738            .with_initial_variable_constraints([
739                BooleanExpression::ComparisonExpression(
740                    Box::new(IntegerExpression::Atom(Variable::new("x"))),
741                    ComparisonOp::Eq,
742                    Box::new(IntegerExpression::Const(0)),
743                ),
744                BooleanExpression::ComparisonExpression(
745                    Box::new(IntegerExpression::Atom(Variable::new("y"))),
746                    ComparisonOp::Eq,
747                    Box::new(IntegerExpression::Const(0)),
748                ),
749            ])
750            .unwrap()
751            .with_rule(
752                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
753                    .with_guard(BooleanExpression::ComparisonExpression(
754                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
755                        ComparisonOp::Gt,
756                        Box::new(IntegerExpression::Const(2)),
757                    ))
758                    .with_action(
759                        Action::new(
760                            Variable::new("x"),
761                            IntegerExpression::Const(1)
762                                + IntegerExpression::Atom(Variable::new("x")),
763                        )
764                        .unwrap(),
765                    )
766                    .build(),
767            )
768            .unwrap()
769            .build();
770        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
771        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
772            .build()
773            .unwrap();
774        let interval_ta = interval_tas.next().unwrap();
775        assert!(interval_tas.next().is_none());
776        // l2 == 0, l3 == 0, x == 0, y == 0
777        let ta = ACSThresholdAutomaton::new(interval_ta);
778        let l1 = ta.idx_ctx.to_cs_loc(&Location::new("l1"));
779        let l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
780
781        let mut loc_state = ACSLocState::new_all_zero(&ta);
782        loc_state[l1] = 1;
783
784        let config1 = ACSTAConfig {
785            loc_state,
786            interval_state: ACSIntervalState {
787                v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
788            },
789        };
790
791        assert!(config1.is_initial(&ta));
792
793        let mut loc_state = ACSLocState::new_all_zero(&ta);
794        loc_state[l1] = 420;
795
796        let config2 = ACSTAConfig {
797            loc_state,
798            interval_state: ACSIntervalState {
799                v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
800            },
801        };
802
803        assert!(config2.is_initial(&ta));
804
805        let mut loc_state = ACSLocState::new_all_zero(&ta);
806        loc_state[l1] = 1;
807        loc_state[l2] = 2;
808
809        let config2 = ACSTAConfig {
810            loc_state,
811            interval_state: ACSIntervalState {
812                v_to_interval: vec![ACSInterval(0), ACSInterval(0)],
813            },
814        };
815
816        assert!(!config2.is_initial(&ta));
817
818        let mut loc_state = ACSLocState::new_all_zero(&ta);
819        loc_state[l1] = 1;
820
821        let config2 = ACSTAConfig {
822            loc_state,
823            interval_state: ACSIntervalState {
824                v_to_interval: vec![ACSInterval(0), ACSInterval(1)],
825            },
826        };
827
828        assert!(!config2.is_initial(&ta));
829
830        let mut loc_state = ACSLocState::new_all_zero(&ta);
831        loc_state[l1] = 1;
832        loc_state[l2] = 2;
833
834        let config2 = ACSTAConfig {
835            loc_state,
836            interval_state: ACSIntervalState {
837                v_to_interval: vec![ACSInterval(1), ACSInterval(1)],
838            },
839        };
840
841        assert!(!config2.is_initial(&ta));
842    }
843
844    #[test]
845    fn test_cstaconfig_compute_min_predecessor() {
846        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
847            .with_variables([Variable::new("x"), Variable::new("y")])
848            .unwrap()
849            .with_locations([
850                Location::new("l1"),
851                Location::new("l2"),
852                Location::new("l3"),
853            ])
854            .unwrap()
855            .with_parameter(Parameter::new("n"))
856            .unwrap()
857            .initialize()
858            .with_resilience_condition(BooleanExpression::ComparisonExpression(
859                Box::new(IntegerExpression::Param(Parameter::new("n"))),
860                ComparisonOp::Gt,
861                Box::new(IntegerExpression::Const(3)),
862            ))
863            .unwrap()
864            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
865                Box::new(IntegerExpression::Atom(Location::new("l1"))),
866                ComparisonOp::Eq,
867                Box::new(IntegerExpression::Const(0)),
868            ))
869            .unwrap()
870            .with_rule(
871                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
872                    .with_guard(BooleanExpression::ComparisonExpression(
873                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
874                        ComparisonOp::Gt,
875                        Box::new(IntegerExpression::Const(2)),
876                    ))
877                    .with_action(
878                        Action::new(
879                            Variable::new("x"),
880                            IntegerExpression::Const(1)
881                                + IntegerExpression::Atom(Variable::new("x")),
882                        )
883                        .unwrap(),
884                    )
885                    .build(),
886            )
887            .unwrap()
888            .build();
889        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
890        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
891            .build()
892            .unwrap();
893        let interval_ta = interval_tas.next().unwrap();
894        assert!(interval_tas.next().is_none());
895        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
896        let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
897        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
898
899        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
900        interval_state[var_x] = ACSInterval(1);
901        interval_state[var_y] = ACSInterval(0);
902
903        let rule = CSRule {
904            id: 0,
905            source: ACSLocation(0),
906            target: ACSLocation(1),
907            guard: CSIntervalConstraint::True,
908            actions: vec![CSIntervalAction {
909                var: var_x,
910                effect: IntervalActionEffect::Inc(1),
911            }],
912        };
913
914        let config = ACSTAConfig {
915            loc_state: ACSLocState {
916                loc_state: vec![0, 1, 0],
917            },
918            interval_state: interval_state.clone(),
919        };
920
921        let mut pred_1 = config.clone();
922        pred_1.loc_state.loc_state = vec![1, 0, 0];
923
924        let mut pred_2 = config.clone();
925        pred_2.loc_state.loc_state = vec![1, 0, 0];
926        pred_2.interval_state[var_x] = ACSInterval(0);
927
928        let got_min_basis = config
929            .compute_potential_predecessors(&rule, &cs_ta)
930            .unwrap()
931            .collect::<HashSet<_>>();
932        let expected_min_basis = HashSet::from([pred_1, pred_2]);
933
934        assert_eq!(got_min_basis, expected_min_basis);
935    }
936
937    #[test]
938    fn test_compute_pred_none_if_var_of_reset_not_in_zero() {
939        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
940            .with_variables([Variable::new("x"), Variable::new("y")])
941            .unwrap()
942            .with_locations([
943                Location::new("l1"),
944                Location::new("l2"),
945                Location::new("l3"),
946            ])
947            .unwrap()
948            .with_parameter(Parameter::new("n"))
949            .unwrap()
950            .initialize()
951            .with_resilience_condition(BooleanExpression::ComparisonExpression(
952                Box::new(IntegerExpression::Param(Parameter::new("n"))),
953                ComparisonOp::Gt,
954                Box::new(IntegerExpression::Const(3)),
955            ))
956            .unwrap()
957            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
958                Box::new(IntegerExpression::Atom(Location::new("l1"))),
959                ComparisonOp::Eq,
960                Box::new(IntegerExpression::Const(0)),
961            ))
962            .unwrap()
963            .with_rule(
964                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
965                    .with_guard(BooleanExpression::ComparisonExpression(
966                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
967                        ComparisonOp::Gt,
968                        Box::new(IntegerExpression::Const(2)),
969                    ))
970                    .with_action(
971                        Action::new(Variable::new("x"), IntegerExpression::Const(0)).unwrap(),
972                    )
973                    .build(),
974            )
975            .unwrap()
976            .build();
977        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
978        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
979            .build()
980            .unwrap();
981        let interval_ta = interval_tas.next().unwrap();
982        assert!(interval_tas.next().is_none());
983        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
984        let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
985        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
986
987        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
988        interval_state[var_x] = ACSInterval(1);
989        interval_state[var_y] = ACSInterval(0);
990
991        let rule = CSRule {
992            id: 0,
993            source: ACSLocation(0),
994            target: ACSLocation(1),
995            guard: CSIntervalConstraint::True,
996            actions: vec![CSIntervalAction {
997                var: var_x,
998                effect: IntervalActionEffect::Reset,
999            }],
1000        };
1001
1002        let config = ACSTAConfig {
1003            loc_state: ACSLocState {
1004                loc_state: vec![0, 1, 0],
1005            },
1006            interval_state: interval_state.clone(),
1007        };
1008
1009        let got_pred = config.compute_potential_predecessors(&rule, &cs_ta);
1010        assert!(got_pred.is_none())
1011    }
1012
1013    #[test]
1014    fn test_castaconfig_from_disjunct_target() {
1015        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1016            .with_variables([Variable::new("x"), Variable::new("y")])
1017            .unwrap()
1018            .with_locations([
1019                Location::new("l1"),
1020                Location::new("l2"),
1021                Location::new("l3"),
1022            ])
1023            .unwrap()
1024            .with_parameter(Parameter::new("n"))
1025            .unwrap()
1026            .initialize()
1027            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1028                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1029                ComparisonOp::Gt,
1030                Box::new(IntegerExpression::Const(3)),
1031            ))
1032            .unwrap()
1033            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1034                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1035                ComparisonOp::Eq,
1036                Box::new(IntegerExpression::Const(0)),
1037            ))
1038            .unwrap()
1039            .with_rule(
1040                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1041                    .with_guard(BooleanExpression::ComparisonExpression(
1042                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1043                        ComparisonOp::Gt,
1044                        Box::new(IntegerExpression::Const(2)),
1045                    ))
1046                    .with_action(
1047                        Action::new(
1048                            Variable::new("x"),
1049                            IntegerExpression::Const(1)
1050                                + IntegerExpression::Atom(Variable::new("x")),
1051                        )
1052                        .unwrap(),
1053                    )
1054                    .build(),
1055            )
1056            .unwrap()
1057            .build();
1058        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1059        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1060            .build()
1061            .unwrap();
1062        let interval_ta = interval_tas.next().unwrap();
1063        assert!(interval_tas.next().is_none());
1064        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1065        let loc_l2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1066        let loc_l3 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1067
1068        let target_1 = TargetConfig::new_cover([Location::new("l3")]).unwrap();
1069        let target_2 = TargetConfig::new_cover([Location::new("l2")]).unwrap();
1070        let disj = DisjunctionTargetConfig::new_from_targets("test".into(), [target_1, target_2]);
1071
1072        let got_configs = ACSTAConfig::from_disjunct_target(&disj, &cs_ta).collect::<HashSet<_>>();
1073
1074        let interval_states = ACSIntervalState::all_possible_interval_configs(&cs_ta);
1075        let configs_l2 = interval_states.iter().map(|is| {
1076            let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1077            loc_state[loc_l2] = 1;
1078
1079            ACSTAConfig {
1080                loc_state,
1081                interval_state: is.clone(),
1082            }
1083        });
1084        let configs_l3 = interval_states.iter().map(|is| {
1085            let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1086            loc_state[loc_l3] = 1;
1087
1088            ACSTAConfig {
1089                loc_state,
1090                interval_state: is.clone(),
1091            }
1092        });
1093        let expected_configs = configs_l2.chain(configs_l3).collect::<HashSet<_>>();
1094
1095        assert_eq!(got_configs, expected_configs)
1096    }
1097
1098    #[test]
1099    fn test_castaconfig_from_disjunct_target_with_var_constr() {
1100        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1101            .with_variables([Variable::new("x"), Variable::new("y")])
1102            .unwrap()
1103            .with_locations([
1104                Location::new("l1"),
1105                Location::new("l2"),
1106                Location::new("l3"),
1107            ])
1108            .unwrap()
1109            .with_parameter(Parameter::new("n"))
1110            .unwrap()
1111            .initialize()
1112            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1113                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1114                ComparisonOp::Gt,
1115                Box::new(IntegerExpression::Const(3)),
1116            ))
1117            .unwrap()
1118            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1119                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1120                ComparisonOp::Eq,
1121                Box::new(IntegerExpression::Const(0)),
1122            ))
1123            .unwrap()
1124            .with_rule(
1125                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1126                    .with_guard(BooleanExpression::ComparisonExpression(
1127                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1128                        ComparisonOp::Gt,
1129                        Box::new(IntegerExpression::Const(2)),
1130                    ))
1131                    .with_action(
1132                        Action::new(
1133                            Variable::new("x"),
1134                            IntegerExpression::Const(1)
1135                                + IntegerExpression::Atom(Variable::new("x")),
1136                        )
1137                        .unwrap(),
1138                    )
1139                    .build(),
1140            )
1141            .unwrap()
1142            .build();
1143        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1144        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1145            .build()
1146            .unwrap();
1147        let interval_ta = interval_tas.next().unwrap();
1148        assert!(interval_tas.next().is_none());
1149        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1150        let loc_l2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1151        let loc_l3 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1152
1153        let cstr = BooleanExpression::ComparisonExpression(
1154            Box::new(IntegerExpression::Atom(Variable::new("x"))),
1155            ComparisonOp::Eq,
1156            Box::new(IntegerExpression::Const(0)),
1157        );
1158
1159        let target_1 =
1160            TargetConfig::new_reach_with_var_constr([(Location::new("l3"), 1)], [], cstr.clone())
1161                .unwrap();
1162        let target_2 =
1163            TargetConfig::new_reach_with_var_constr([(Location::new("l2"), 1)], [], cstr.clone())
1164                .unwrap();
1165        let disj = DisjunctionTargetConfig::new_from_targets("test".into(), [target_1, target_2]);
1166
1167        let got_configs = ACSTAConfig::from_disjunct_target(&disj, &cs_ta).collect::<HashSet<_>>();
1168
1169        let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1170        let mut state2 = interval_state.clone();
1171        state2[cs_ta.to_cs_var(&Variable::new("y"))] = ACSInterval(1);
1172
1173        let interval_states = [interval_state, state2];
1174        let configs_l2 = interval_states.iter().map(|is| {
1175            let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1176            loc_state[loc_l2] = 1;
1177
1178            ACSTAConfig {
1179                loc_state,
1180                interval_state: is.clone(),
1181            }
1182        });
1183        let configs_l3 = interval_states.iter().map(|is| {
1184            let mut loc_state = ACSLocState::new_all_zero(&cs_ta);
1185            loc_state[loc_l3] = 1;
1186
1187            ACSTAConfig {
1188                loc_state,
1189                interval_state: is.clone(),
1190            }
1191        });
1192        let expected_configs = configs_l2.chain(configs_l3).collect::<HashSet<_>>();
1193
1194        assert_eq!(got_configs, expected_configs)
1195    }
1196
1197    #[test]
1198    fn test_cstaconfig_partial_order() {
1199        let config1 = ACSTAConfig {
1200            loc_state: ACSLocState {
1201                loc_state: vec![1, 2, 3],
1202            },
1203            interval_state: ACSIntervalState {
1204                v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1205            },
1206        };
1207        let config2 = ACSTAConfig {
1208            loc_state: ACSLocState {
1209                loc_state: vec![2, 2, 4],
1210            },
1211            interval_state: ACSIntervalState {
1212                v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1213            },
1214        };
1215
1216        assert_eq!(config1.part_cmp(&config1), PartialOrdCompResult::Equal);
1217        assert_eq!(config1.part_cmp(&config2), PartialOrdCompResult::Smaller);
1218        assert_eq!(config2.part_cmp(&config1), PartialOrdCompResult::Greater);
1219
1220        let config3 = ACSTAConfig {
1221            loc_state: ACSLocState {
1222                loc_state: vec![0, 2, 6],
1223            },
1224            interval_state: ACSIntervalState {
1225                v_to_interval: vec![ACSInterval(3), ACSInterval(2), ACSInterval(1)],
1226            },
1227        };
1228        assert_eq!(
1229            config1.part_cmp(&config3),
1230            PartialOrdCompResult::Incomparable
1231        );
1232        assert_eq!(
1233            config3.part_cmp(&config1),
1234            PartialOrdCompResult::Incomparable
1235        );
1236
1237        let config4 = ACSTAConfig {
1238            loc_state: ACSLocState {
1239                loc_state: vec![1, 2, 3],
1240            },
1241            interval_state: ACSIntervalState {
1242                v_to_interval: vec![ACSInterval(0), ACSInterval(2), ACSInterval(1)],
1243            },
1244        };
1245        assert_eq!(
1246            config1.part_cmp(&config4),
1247            PartialOrdCompResult::Incomparable
1248        );
1249        assert_eq!(
1250            config4.part_cmp(&config1),
1251            PartialOrdCompResult::Incomparable
1252        );
1253    }
1254
1255    #[test]
1256    fn test_cstaconfig_display() {
1257        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1258            .with_variables([Variable::new("x"), Variable::new("y")])
1259            .unwrap()
1260            .with_locations([
1261                Location::new("l1"),
1262                Location::new("l2"),
1263                Location::new("l3"),
1264            ])
1265            .unwrap()
1266            .with_parameter(Parameter::new("n"))
1267            .unwrap()
1268            .initialize()
1269            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1270                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1271                ComparisonOp::Gt,
1272                Box::new(IntegerExpression::Const(3)),
1273            ))
1274            .unwrap()
1275            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1276                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1277                ComparisonOp::Eq,
1278                Box::new(IntegerExpression::Const(0)),
1279            ))
1280            .unwrap()
1281            .with_rule(
1282                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1283                    .with_guard(BooleanExpression::ComparisonExpression(
1284                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1285                        ComparisonOp::Gt,
1286                        Box::new(IntegerExpression::Const(2)),
1287                    ))
1288                    .with_action(
1289                        Action::new(
1290                            Variable::new("x"),
1291                            IntegerExpression::Const(1)
1292                                + IntegerExpression::Atom(Variable::new("x")),
1293                        )
1294                        .unwrap(),
1295                    )
1296                    .build(),
1297            )
1298            .unwrap()
1299            .build();
1300        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1301        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1302            .build()
1303            .unwrap();
1304        let interval_ta = interval_tas.next().unwrap();
1305        assert!(interval_tas.next().is_none());
1306        let ta = ACSThresholdAutomaton::new(interval_ta);
1307
1308        let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1309
1310        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1311        interval_state[var_x] = ACSInterval(2);
1312
1313        let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1314        let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1315
1316        let mut loc_state = ACSLocState::new_all_zero(&ta);
1317        loc_state[loc_l2] = 1;
1318        loc_state[loc_l3] = 42;
1319
1320        let config = ACSTAConfig {
1321            loc_state,
1322            interval_state,
1323        };
1324
1325        let got_string = config.display(&ta);
1326        let expected_string =
1327            "locations: (l1 : 0, l2 : 1, l3 : 42), variables: (x : [3, ∞[, y : [0, 1[)";
1328
1329        assert_eq!(&got_string, expected_string);
1330    }
1331
1332    #[test]
1333    fn test_cstaconfig_display_compact() {
1334        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1335            .with_variables([Variable::new("x"), Variable::new("y")])
1336            .unwrap()
1337            .with_locations([
1338                Location::new("l1"),
1339                Location::new("l2"),
1340                Location::new("l3"),
1341            ])
1342            .unwrap()
1343            .with_parameter(Parameter::new("n"))
1344            .unwrap()
1345            .initialize()
1346            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1347                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1348                ComparisonOp::Gt,
1349                Box::new(IntegerExpression::Const(3)),
1350            ))
1351            .unwrap()
1352            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1353                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1354                ComparisonOp::Eq,
1355                Box::new(IntegerExpression::Const(0)),
1356            ))
1357            .unwrap()
1358            .with_rule(
1359                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1360                    .with_guard(BooleanExpression::ComparisonExpression(
1361                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1362                        ComparisonOp::Gt,
1363                        Box::new(IntegerExpression::Const(2)),
1364                    ))
1365                    .with_action(
1366                        Action::new(
1367                            Variable::new("x"),
1368                            IntegerExpression::Const(1)
1369                                + IntegerExpression::Atom(Variable::new("x")),
1370                        )
1371                        .unwrap(),
1372                    )
1373                    .build(),
1374            )
1375            .unwrap()
1376            .build();
1377        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1378        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1379            .build()
1380            .unwrap();
1381        let interval_ta = interval_tas.next().unwrap();
1382        assert!(interval_tas.next().is_none());
1383        let ta = ACSThresholdAutomaton::new(interval_ta);
1384
1385        let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1386
1387        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1388        interval_state[var_x] = ACSInterval(2);
1389
1390        let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
1391        let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
1392
1393        let mut loc_state = ACSLocState::new_all_zero(&ta);
1394        loc_state[loc_l2] = 1;
1395        loc_state[loc_l3] = 42;
1396
1397        let config = ACSTAConfig {
1398            loc_state,
1399            interval_state,
1400        };
1401
1402        let got_string = config.display_compact(&ta);
1403        let expected_string = "locations: (l2 : 1, l3 : 42), variables: (x : [3, ∞[)";
1404
1405        assert_eq!(&got_string, expected_string);
1406    }
1407
1408    #[test]
1409    fn test_interval_state_getters() {
1410        let mut interval_state = ACSIntervalState {
1411            v_to_interval: vec![ACSInterval(0), ACSInterval(1), ACSInterval(2)],
1412        };
1413
1414        let var_to_interval = interval_state
1415            .into_iterator_variable_interval()
1416            .collect::<HashMap<_, _>>();
1417
1418        assert_eq!(
1419            var_to_interval,
1420            HashMap::from([
1421                (CSVariable(0), ACSInterval(0)),
1422                (CSVariable(1), ACSInterval(1)),
1423                (CSVariable(2), ACSInterval(2))
1424            ])
1425        );
1426
1427        assert_eq!(interval_state[CSVariable(1)], ACSInterval(1));
1428        assert_eq!(interval_state[&CSVariable(1)], ACSInterval(1));
1429
1430        interval_state[CSVariable(1)] = ACSInterval(0);
1431        assert_eq!(interval_state[CSVariable(1)], ACSInterval(0));
1432        assert_eq!(interval_state[&CSVariable(1)], ACSInterval(0));
1433    }
1434
1435    #[test]
1436    fn test_interval_state_partial_order() {
1437        let interval_state1 = ACSIntervalState {
1438            v_to_interval: vec![ACSInterval(0), ACSInterval(1), ACSInterval(2)],
1439        };
1440        let interval_state2 = ACSIntervalState {
1441            v_to_interval: vec![ACSInterval(0), ACSInterval(2), ACSInterval(2)],
1442        };
1443
1444        assert_eq!(
1445            interval_state1.part_cmp(&interval_state1),
1446            PartialOrdCompResult::Equal
1447        );
1448
1449        assert_eq!(
1450            interval_state1.part_cmp(&interval_state2),
1451            PartialOrdCompResult::Incomparable
1452        );
1453        assert_eq!(
1454            interval_state2.part_cmp(&interval_state1),
1455            PartialOrdCompResult::Incomparable
1456        );
1457    }
1458
1459    #[test]
1460    fn test_interval_state_all_possible_interval_configs() {
1461        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1462            .with_variables([Variable::new("x"), Variable::new("y")])
1463            .unwrap()
1464            .with_locations([
1465                Location::new("l1"),
1466                Location::new("l2"),
1467                Location::new("l3"),
1468            ])
1469            .unwrap()
1470            .with_parameter(Parameter::new("n"))
1471            .unwrap()
1472            .initialize()
1473            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1474                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1475                ComparisonOp::Gt,
1476                Box::new(IntegerExpression::Const(3)),
1477            ))
1478            .unwrap()
1479            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1480                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1481                ComparisonOp::Eq,
1482                Box::new(IntegerExpression::Const(0)),
1483            ))
1484            .unwrap()
1485            .with_rule(
1486                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1487                    .with_guard(BooleanExpression::ComparisonExpression(
1488                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1489                        ComparisonOp::Gt,
1490                        Box::new(IntegerExpression::Const(2)),
1491                    ))
1492                    .with_action(
1493                        Action::new(
1494                            Variable::new("x"),
1495                            IntegerExpression::Const(1)
1496                                + IntegerExpression::Atom(Variable::new("x")),
1497                        )
1498                        .unwrap(),
1499                    )
1500                    .build(),
1501            )
1502            .unwrap()
1503            .build();
1504        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1505        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1506            .build()
1507            .unwrap();
1508        let interval_ta = interval_tas.next().unwrap();
1509        assert!(interval_tas.next().is_none());
1510        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1511        let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1512        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1513
1514        let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1515
1516        let got_states = ACSIntervalState::all_possible_interval_configs(&cs_ta);
1517
1518        let mut state1 = interval_state.clone();
1519        state1[var_x] = ACSInterval(1);
1520
1521        let mut state2 = interval_state.clone();
1522        state2[var_x] = ACSInterval(2);
1523
1524        let mut state3 = interval_state.clone();
1525        state3[var_y] = ACSInterval(1);
1526
1527        let mut state4 = interval_state.clone();
1528        state4[var_x] = ACSInterval(1);
1529        state4[var_y] = ACSInterval(1);
1530
1531        let mut state5 = interval_state.clone();
1532        state5[var_x] = ACSInterval(2);
1533        state5[var_y] = ACSInterval(1);
1534
1535        let expected_states = HashSet::from([
1536            interval_state.clone(),
1537            state1,
1538            state2,
1539            state3,
1540            state4,
1541            state5,
1542        ]);
1543        assert_eq!(
1544            got_states.into_iter().collect::<HashSet<_>>(),
1545            expected_states
1546        );
1547    }
1548
1549    #[test]
1550    fn test_interval_state_all_possible_interval_configs_true() {
1551        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1552            .with_variables([Variable::new("x"), Variable::new("y")])
1553            .unwrap()
1554            .with_locations([
1555                Location::new("l1"),
1556                Location::new("l2"),
1557                Location::new("l3"),
1558            ])
1559            .unwrap()
1560            .with_parameter(Parameter::new("n"))
1561            .unwrap()
1562            .initialize()
1563            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1564                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1565                ComparisonOp::Gt,
1566                Box::new(IntegerExpression::Const(3)),
1567            ))
1568            .unwrap()
1569            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1570                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1571                ComparisonOp::Eq,
1572                Box::new(IntegerExpression::Const(0)),
1573            ))
1574            .unwrap()
1575            .with_rule(
1576                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1577                    .with_guard(BooleanExpression::ComparisonExpression(
1578                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1579                        ComparisonOp::Gt,
1580                        Box::new(IntegerExpression::Const(2)),
1581                    ))
1582                    .with_action(
1583                        Action::new(
1584                            Variable::new("x"),
1585                            IntegerExpression::Const(1)
1586                                + IntegerExpression::Atom(Variable::new("x")),
1587                        )
1588                        .unwrap(),
1589                    )
1590                    .build(),
1591            )
1592            .unwrap()
1593            .build();
1594        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1595        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1596            .build()
1597            .unwrap();
1598        let interval_ta = interval_tas.next().unwrap();
1599        assert!(interval_tas.next().is_none());
1600        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1601        let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1602        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1603
1604        let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1605
1606        let cstr = IntervalConstraint::True;
1607
1608        let got_states = ACSIntervalState::get_target_interval_configs(cstr, &cs_ta);
1609
1610        let mut state1 = interval_state.clone();
1611        state1[var_x] = ACSInterval(1);
1612
1613        let mut state2 = interval_state.clone();
1614        state2[var_x] = ACSInterval(2);
1615
1616        let mut state3 = interval_state.clone();
1617        state3[var_y] = ACSInterval(1);
1618
1619        let mut state4 = interval_state.clone();
1620        state4[var_x] = ACSInterval(1);
1621        state4[var_y] = ACSInterval(1);
1622
1623        let mut state5 = interval_state.clone();
1624        state5[var_x] = ACSInterval(2);
1625        state5[var_y] = ACSInterval(1);
1626
1627        let expected_states = HashSet::from([
1628            interval_state.clone(),
1629            state1,
1630            state2,
1631            state3,
1632            state4,
1633            state5,
1634        ]);
1635        assert_eq!(
1636            got_states.into_iter().collect::<HashSet<_>>(),
1637            expected_states
1638        );
1639    }
1640
1641    #[test]
1642    fn test_target_interval_configs() {
1643        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1644            .with_variables([Variable::new("x"), Variable::new("y")])
1645            .unwrap()
1646            .with_locations([
1647                Location::new("l1"),
1648                Location::new("l2"),
1649                Location::new("l3"),
1650            ])
1651            .unwrap()
1652            .with_parameter(Parameter::new("n"))
1653            .unwrap()
1654            .initialize()
1655            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1656                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1657                ComparisonOp::Gt,
1658                Box::new(IntegerExpression::Const(3)),
1659            ))
1660            .unwrap()
1661            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1662                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1663                ComparisonOp::Eq,
1664                Box::new(IntegerExpression::Const(0)),
1665            ))
1666            .unwrap()
1667            .with_rule(
1668                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1669                    .with_guard(BooleanExpression::ComparisonExpression(
1670                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1671                        ComparisonOp::Gt,
1672                        Box::new(IntegerExpression::Const(2)),
1673                    ))
1674                    .with_action(
1675                        Action::new(
1676                            Variable::new("x"),
1677                            IntegerExpression::Const(1)
1678                                + IntegerExpression::Atom(Variable::new("x")),
1679                        )
1680                        .unwrap(),
1681                    )
1682                    .build(),
1683            )
1684            .unwrap()
1685            .build();
1686        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1687        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1688            .build()
1689            .unwrap();
1690        let interval_ta = interval_tas.next().unwrap();
1691        assert!(interval_tas.next().is_none());
1692        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1693        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1694
1695        let cstr = IntervalConstraint::SingleVarIntervalConstr(
1696            Variable::new("x"),
1697            vec![Interval::new_constant(0, 1)],
1698        );
1699        let got_states = ACSIntervalState::get_target_interval_configs(cstr, &cs_ta);
1700
1701        let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1702        let mut state2 = interval_state.clone();
1703        state2[var_y] = ACSInterval(1);
1704
1705        let expected_states = HashSet::from([interval_state.clone(), state2]);
1706        assert_eq!(
1707            got_states.into_iter().collect::<HashSet<_>>(),
1708            expected_states
1709        );
1710    }
1711
1712    #[test]
1713    fn test_interval_state_compute_all_predecessor_configs() {
1714        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1715            .with_variables([Variable::new("x"), Variable::new("y")])
1716            .unwrap()
1717            .with_locations([
1718                Location::new("l1"),
1719                Location::new("l2"),
1720                Location::new("l3"),
1721            ])
1722            .unwrap()
1723            .with_parameter(Parameter::new("n"))
1724            .unwrap()
1725            .initialize()
1726            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1727                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1728                ComparisonOp::Gt,
1729                Box::new(IntegerExpression::Const(3)),
1730            ))
1731            .unwrap()
1732            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1733                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1734                ComparisonOp::Eq,
1735                Box::new(IntegerExpression::Const(0)),
1736            ))
1737            .unwrap()
1738            .with_rule(
1739                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1740                    .with_guard(BooleanExpression::ComparisonExpression(
1741                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1742                        ComparisonOp::Gt,
1743                        Box::new(IntegerExpression::Const(2)),
1744                    ))
1745                    .with_action(
1746                        Action::new(
1747                            Variable::new("x"),
1748                            IntegerExpression::Const(1)
1749                                + IntegerExpression::Atom(Variable::new("x")),
1750                        )
1751                        .unwrap(),
1752                    )
1753                    .build(),
1754            )
1755            .unwrap()
1756            .build();
1757        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1758        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1759            .build()
1760            .unwrap();
1761        let interval_ta = interval_tas.next().unwrap();
1762        assert!(interval_tas.next().is_none());
1763        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
1764
1765        let var_x = cs_ta.idx_ctx.to_cs_var(&Variable::new("x"));
1766        let var_y = cs_ta.idx_ctx.to_cs_var(&Variable::new("y"));
1767
1768        // all zero -> no predecessor because no inc possible
1769        let interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1770        let rule = CSRule {
1771            id: 0,
1772            source: ACSLocation(0),
1773            target: ACSLocation(1),
1774            guard: CSIntervalConstraint::True,
1775            actions: vec![CSIntervalAction {
1776                var: var_x,
1777                effect: IntervalActionEffect::Inc(1),
1778            }],
1779        };
1780        let got_preds = interval_state.compute_all_predecessor_configs(&rule, &cs_ta);
1781        let expected_preds = HashSet::from([]);
1782        assert_eq!(
1783            got_preds.into_iter().collect::<HashSet<_>>(),
1784            expected_preds
1785        );
1786
1787        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&cs_ta);
1788        interval_state[var_x] = ACSInterval(1);
1789        interval_state[var_y] = ACSInterval(0);
1790
1791        let rule = CSRule {
1792            id: 0,
1793            source: ACSLocation(0),
1794            target: ACSLocation(1),
1795            guard: CSIntervalConstraint::True,
1796            actions: vec![CSIntervalAction {
1797                var: var_x,
1798                effect: IntervalActionEffect::Inc(1),
1799            }],
1800        };
1801
1802        let got_preds = interval_state.compute_all_predecessor_configs(&rule, &cs_ta);
1803
1804        let mut pred_1 = interval_state.clone();
1805        pred_1[var_x] = ACSInterval(0);
1806
1807        let expected_preds = HashSet::from([
1808            // unchanged
1809            interval_state.clone(),
1810            // previous interval
1811            pred_1,
1812        ]);
1813        assert_eq!(
1814            got_preds.into_iter().collect::<HashSet<_>>(),
1815            expected_preds
1816        );
1817
1818        let rule = CSRule {
1819            id: 0,
1820            source: ACSLocation(0),
1821            target: ACSLocation(1),
1822            guard: CSIntervalConstraint::True,
1823            actions: vec![CSIntervalAction {
1824                var: var_x,
1825                effect: IntervalActionEffect::Dec(1),
1826            }],
1827        };
1828
1829        let got_preds = interval_state.compute_all_predecessor_configs(&rule, &cs_ta);
1830
1831        let mut pred_1 = interval_state.clone();
1832        pred_1[var_x] = ACSInterval(2);
1833        let expected_preds = HashSet::from([interval_state.clone(), pred_1.clone()]);
1834        assert_eq!(
1835            got_preds.into_iter().collect::<HashSet<_>>(),
1836            expected_preds
1837        );
1838
1839        let rule = CSRule {
1840            id: 0,
1841            source: ACSLocation(0),
1842            target: ACSLocation(1),
1843            guard: CSIntervalConstraint::True,
1844            actions: vec![CSIntervalAction {
1845                var: var_y,
1846                effect: IntervalActionEffect::Reset,
1847            }],
1848        };
1849
1850        let got_preds = interval_state.compute_all_predecessor_configs(&rule, &cs_ta);
1851        let mut pred_1 = interval_state.clone();
1852        pred_1[var_y] = ACSInterval(1);
1853        let expected_preds = HashSet::from([interval_state.clone(), pred_1]);
1854        assert_eq!(
1855            got_preds.into_iter().collect::<HashSet<_>>(),
1856            expected_preds
1857        );
1858        assert_eq!(cs_ta.get_all_intervals(&var_x).count(), 3);
1859    }
1860
1861    #[test]
1862    fn test_interval_state_display() {
1863        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1864            .with_variables([Variable::new("x"), Variable::new("y")])
1865            .unwrap()
1866            .with_locations([
1867                Location::new("l1"),
1868                Location::new("l2"),
1869                Location::new("l3"),
1870            ])
1871            .unwrap()
1872            .with_parameter(Parameter::new("n"))
1873            .unwrap()
1874            .initialize()
1875            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1876                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1877                ComparisonOp::Gt,
1878                Box::new(IntegerExpression::Const(3)),
1879            ))
1880            .unwrap()
1881            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1882                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1883                ComparisonOp::Eq,
1884                Box::new(IntegerExpression::Const(0)),
1885            ))
1886            .unwrap()
1887            .with_rule(
1888                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1889                    .with_guard(BooleanExpression::ComparisonExpression(
1890                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1891                        ComparisonOp::Gt,
1892                        Box::new(IntegerExpression::Const(2)),
1893                    ))
1894                    .with_action(
1895                        Action::new(
1896                            Variable::new("x"),
1897                            IntegerExpression::Const(1)
1898                                + IntegerExpression::Atom(Variable::new("x")),
1899                        )
1900                        .unwrap(),
1901                    )
1902                    .build(),
1903            )
1904            .unwrap()
1905            .build();
1906        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1907        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1908            .build()
1909            .unwrap();
1910        let interval_ta = interval_tas.next().unwrap();
1911        assert!(interval_tas.next().is_none());
1912        let ta = ACSThresholdAutomaton::new(interval_ta);
1913
1914        let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1915
1916        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1917        interval_state[var_x] = ACSInterval(2);
1918
1919        let got_string = interval_state.display(&ta);
1920        let expected_string = "x : [3, ∞[, y : [0, 1[";
1921
1922        assert_eq!(&got_string, expected_string);
1923    }
1924
1925    #[test]
1926    fn test_interval_state_display_compact() {
1927        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
1928            .with_variables([Variable::new("x"), Variable::new("y")])
1929            .unwrap()
1930            .with_locations([
1931                Location::new("l1"),
1932                Location::new("l2"),
1933                Location::new("l3"),
1934            ])
1935            .unwrap()
1936            .with_parameter(Parameter::new("n"))
1937            .unwrap()
1938            .initialize()
1939            .with_resilience_condition(BooleanExpression::ComparisonExpression(
1940                Box::new(IntegerExpression::Param(Parameter::new("n"))),
1941                ComparisonOp::Gt,
1942                Box::new(IntegerExpression::Const(3)),
1943            ))
1944            .unwrap()
1945            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
1946                Box::new(IntegerExpression::Atom(Location::new("l1"))),
1947                ComparisonOp::Eq,
1948                Box::new(IntegerExpression::Const(0)),
1949            ))
1950            .unwrap()
1951            .with_rule(
1952                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
1953                    .with_guard(BooleanExpression::ComparisonExpression(
1954                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
1955                        ComparisonOp::Gt,
1956                        Box::new(IntegerExpression::Const(2)),
1957                    ))
1958                    .with_action(
1959                        Action::new(
1960                            Variable::new("x"),
1961                            IntegerExpression::Const(1)
1962                                + IntegerExpression::Atom(Variable::new("x")),
1963                        )
1964                        .unwrap(),
1965                    )
1966                    .build(),
1967            )
1968            .unwrap()
1969            .build();
1970        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
1971        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
1972            .build()
1973            .unwrap();
1974        let interval_ta = interval_tas.next().unwrap();
1975        assert!(interval_tas.next().is_none());
1976        let ta = ACSThresholdAutomaton::new(interval_ta);
1977
1978        let var_x = ta.idx_ctx.to_cs_var(&Variable::new("x"));
1979
1980        let mut interval_state = ACSIntervalState::new_cfg_all_zero_interval(&ta);
1981        interval_state[var_x] = ACSInterval(2);
1982
1983        let got_string = interval_state.display_compact(&ta);
1984        let expected_string = "x : [3, ∞[";
1985
1986        assert_eq!(&got_string, expected_string);
1987    }
1988
1989    #[test]
1990    fn test_loc_state_getters() {
1991        let mut loc_state = ACSLocState {
1992            loc_state: vec![1, 2, 3, 4, 5],
1993        };
1994
1995        let state_n = loc_state
1996            .into_iterator_loc_n_procs()
1997            .collect::<HashMap<_, _>>();
1998
1999        assert_eq!(
2000            state_n,
2001            HashMap::from([
2002                (ACSLocation(0), 1),
2003                (ACSLocation(1), 2),
2004                (ACSLocation(2), 3),
2005                (ACSLocation(3), 4),
2006                (ACSLocation(4), 5)
2007            ])
2008        );
2009
2010        assert_eq!(loc_state[ACSLocation(1)], 2);
2011        assert_eq!(loc_state[&ACSLocation(1)], 2);
2012
2013        loc_state[ACSLocation(0)] += 1;
2014        assert_eq!(loc_state[ACSLocation(0)], 2);
2015    }
2016
2017    #[test]
2018    fn test_loc_state_part_ord() {
2019        let loc_state1 = ACSLocState {
2020            loc_state: vec![1, 2, 3, 4, 5],
2021        };
2022
2023        let loc_state2 = ACSLocState {
2024            loc_state: vec![1, 2, 3, 5, 5],
2025        };
2026
2027        assert_eq!(
2028            loc_state1.part_cmp(&loc_state2),
2029            PartialOrdCompResult::Smaller
2030        );
2031        assert_eq!(
2032            loc_state2.part_cmp(&loc_state1),
2033            PartialOrdCompResult::Greater
2034        );
2035        assert_eq!(
2036            loc_state1.part_cmp(&loc_state1),
2037            PartialOrdCompResult::Equal
2038        );
2039
2040        let loc_state3 = ACSLocState {
2041            loc_state: vec![1, 2, 3, 0, 6],
2042        };
2043
2044        assert_eq!(
2045            loc_state1.part_cmp(&loc_state3),
2046            PartialOrdCompResult::Incomparable
2047        );
2048
2049        let loc_state4 = ACSLocState {
2050            loc_state: vec![1, 2, 3, 6, 0],
2051        };
2052
2053        assert_eq!(
2054            loc_state1.part_cmp(&loc_state4),
2055            PartialOrdCompResult::Incomparable
2056        );
2057    }
2058
2059    #[test]
2060    fn test_loc_state_compute_predecessor_min_basis() {
2061        let loc_state = ACSLocState {
2062            loc_state: vec![0, 1, 2],
2063        };
2064
2065        let rule = CSRule {
2066            id: 0,
2067            source: ACSLocation(0),
2068            target: ACSLocation(1),
2069            guard: CSIntervalConstraint::True,
2070            actions: vec![],
2071        };
2072
2073        let pred = loc_state.compute_predecessors_min_basis(&rule);
2074
2075        let expected_pred = ACSLocState {
2076            loc_state: vec![1, 0, 2],
2077        };
2078
2079        assert_eq!(pred, expected_pred);
2080
2081        let loc_state = ACSLocState {
2082            loc_state: vec![0, 0, 2],
2083        };
2084
2085        let rule = CSRule {
2086            id: 0,
2087            source: ACSLocation(0),
2088            target: ACSLocation(1),
2089            guard: CSIntervalConstraint::True,
2090            actions: vec![],
2091        };
2092
2093        let pred = loc_state.compute_predecessors_min_basis(&rule);
2094
2095        let expected_pred = ACSLocState {
2096            loc_state: vec![1, 0, 2],
2097        };
2098
2099        assert_eq!(pred, expected_pred);
2100    }
2101
2102    #[test]
2103    fn test_loc_state_compute_target_cfg() {
2104        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2105            .with_variables([Variable::new("x"), Variable::new("y")])
2106            .unwrap()
2107            .with_locations([
2108                Location::new("l1"),
2109                Location::new("l2"),
2110                Location::new("l3"),
2111            ])
2112            .unwrap()
2113            .with_parameter(Parameter::new("n"))
2114            .unwrap()
2115            .initialize()
2116            .with_resilience_condition(BooleanExpression::ComparisonExpression(
2117                Box::new(IntegerExpression::Param(Parameter::new("n"))),
2118                ComparisonOp::Gt,
2119                Box::new(IntegerExpression::Const(3)),
2120            ))
2121            .unwrap()
2122            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2123                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2124                ComparisonOp::Eq,
2125                Box::new(IntegerExpression::Const(0)),
2126            ))
2127            .unwrap()
2128            .with_rule(
2129                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2130                    .with_guard(BooleanExpression::ComparisonExpression(
2131                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
2132                        ComparisonOp::Gt,
2133                        Box::new(IntegerExpression::Const(2)),
2134                    ))
2135                    .with_action(
2136                        Action::new(
2137                            Variable::new("x"),
2138                            IntegerExpression::Const(1)
2139                                + IntegerExpression::Atom(Variable::new("x")),
2140                        )
2141                        .unwrap(),
2142                    )
2143                    .build(),
2144            )
2145            .unwrap()
2146            .build();
2147        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2148        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2149            .build()
2150            .unwrap();
2151        let interval_ta = interval_tas.next().unwrap();
2152        assert!(interval_tas.next().is_none());
2153        let cs_ta = ACSThresholdAutomaton::new(interval_ta);
2154
2155        // Cover l3
2156        let spec = TargetConfig::new_cover([Location::new("l3")]).unwrap();
2157        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2158
2159        let cs_loc = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2160        let mut expected_loc_state = ACSLocState {
2161            loc_state: vec![0, 0, 0],
2162        };
2163        expected_loc_state[cs_loc] = 1;
2164
2165        assert_eq!(got_loc_state, expected_loc_state);
2166
2167        // Cover l1
2168        let spec = TargetConfig::new_cover([Location::new("l1")]).unwrap();
2169        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2170
2171        let loc_l1 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l1"));
2172        let mut expected_loc_state = ACSLocState {
2173            loc_state: vec![0, 0, 0],
2174        };
2175        expected_loc_state[loc_l1] = 1;
2176        assert_eq!(got_loc_state, expected_loc_state);
2177
2178        // Cover l1 + l2
2179        let spec = TargetConfig::new_cover([Location::new("l1"), Location::new("l2")]).unwrap();
2180        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2181
2182        let mut expected_loc_state = ACSLocState {
2183            loc_state: vec![0, 0, 0],
2184        };
2185        let cs_loc1 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l1"));
2186        expected_loc_state[cs_loc1] = 1;
2187        let cs_loc2 = cs_ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2188        expected_loc_state[cs_loc2] = 1;
2189
2190        assert_eq!(got_loc_state, expected_loc_state);
2191
2192        // Cover l1 + l2 + l3
2193        let spec = TargetConfig::new_cover([
2194            Location::new("l1"),
2195            Location::new("l2"),
2196            Location::new("l3"),
2197        ])
2198        .unwrap();
2199        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2200
2201        let expected_loc_state = ACSLocState {
2202            loc_state: vec![1, 1, 1],
2203        };
2204
2205        assert_eq!(got_loc_state, expected_loc_state);
2206
2207        // GeneralCover l3
2208        let spec = TargetConfig::new_general_cover([(Location::new("l3"), 42)]).unwrap();
2209        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2210
2211        let cs_loc = cs_ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2212        let mut expected_loc_state = ACSLocState {
2213            loc_state: vec![0, 0, 0],
2214        };
2215        expected_loc_state[cs_loc] = 42;
2216
2217        assert_eq!(got_loc_state, expected_loc_state);
2218
2219        // GeneralCover l1 + l2 + l3
2220        let spec = TargetConfig::new_general_cover([
2221            (Location::new("l1"), 42),
2222            (Location::new("l2"), 42),
2223            (Location::new("l3"), 42),
2224        ])
2225        .unwrap();
2226        let got_loc_state = ACSLocState::compute_target_cfg(&spec, &cs_ta);
2227
2228        let expected_loc_state = ACSLocState {
2229            loc_state: vec![42, 42, 42],
2230        };
2231
2232        assert_eq!(got_loc_state, expected_loc_state);
2233    }
2234
2235    #[test]
2236    fn test_loc_state_display() {
2237        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2238            .with_variables([Variable::new("x"), Variable::new("y")])
2239            .unwrap()
2240            .with_locations([
2241                Location::new("l1"),
2242                Location::new("l2"),
2243                Location::new("l3"),
2244            ])
2245            .unwrap()
2246            .with_parameter(Parameter::new("n"))
2247            .unwrap()
2248            .initialize()
2249            .with_resilience_condition(BooleanExpression::ComparisonExpression(
2250                Box::new(IntegerExpression::Param(Parameter::new("n"))),
2251                ComparisonOp::Gt,
2252                Box::new(IntegerExpression::Const(3)),
2253            ))
2254            .unwrap()
2255            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2256                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2257                ComparisonOp::Eq,
2258                Box::new(IntegerExpression::Const(0)),
2259            ))
2260            .unwrap()
2261            .with_rule(
2262                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2263                    .with_guard(BooleanExpression::ComparisonExpression(
2264                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
2265                        ComparisonOp::Gt,
2266                        Box::new(IntegerExpression::Const(2)),
2267                    ))
2268                    .with_action(
2269                        Action::new(
2270                            Variable::new("x"),
2271                            IntegerExpression::Const(1)
2272                                + IntegerExpression::Atom(Variable::new("x")),
2273                        )
2274                        .unwrap(),
2275                    )
2276                    .build(),
2277            )
2278            .unwrap()
2279            .build();
2280        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2281        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2282            .build()
2283            .unwrap();
2284        let interval_ta = interval_tas.next().unwrap();
2285        assert!(interval_tas.next().is_none());
2286        let ta = ACSThresholdAutomaton::new(interval_ta);
2287
2288        let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2289        let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2290
2291        let mut loc_state = ACSLocState::new_all_zero(&ta);
2292        loc_state[loc_l2] = 1;
2293        loc_state[loc_l3] = 42;
2294
2295        let got_string = loc_state.display(&ta);
2296        let expected_string = "l1 : 0, l2 : 1, l3 : 42";
2297
2298        assert_eq!(&got_string, expected_string);
2299    }
2300
2301    #[test]
2302    fn test_loc_state_display_compact() {
2303        let ta = GeneralThresholdAutomatonBuilder::new("test_ta")
2304            .with_variables([Variable::new("x"), Variable::new("y")])
2305            .unwrap()
2306            .with_locations([
2307                Location::new("l1"),
2308                Location::new("l2"),
2309                Location::new("l3"),
2310            ])
2311            .unwrap()
2312            .with_parameter(Parameter::new("n"))
2313            .unwrap()
2314            .initialize()
2315            .with_resilience_condition(BooleanExpression::ComparisonExpression(
2316                Box::new(IntegerExpression::Param(Parameter::new("n"))),
2317                ComparisonOp::Gt,
2318                Box::new(IntegerExpression::Const(3)),
2319            ))
2320            .unwrap()
2321            .with_initial_location_constraint(BooleanExpression::ComparisonExpression(
2322                Box::new(IntegerExpression::Atom(Location::new("l1"))),
2323                ComparisonOp::Eq,
2324                Box::new(IntegerExpression::Const(0)),
2325            ))
2326            .unwrap()
2327            .with_rule(
2328                RuleBuilder::new(0, Location::new("l1"), Location::new("l2"))
2329                    .with_guard(BooleanExpression::ComparisonExpression(
2330                        Box::new(IntegerExpression::Atom(Variable::new("x"))),
2331                        ComparisonOp::Gt,
2332                        Box::new(IntegerExpression::Const(2)),
2333                    ))
2334                    .with_action(
2335                        Action::new(
2336                            Variable::new("x"),
2337                            IntegerExpression::Const(1)
2338                                + IntegerExpression::Atom(Variable::new("x")),
2339                        )
2340                        .unwrap(),
2341                    )
2342                    .build(),
2343            )
2344            .unwrap()
2345            .build();
2346        let lia_ta = LIAThresholdAutomaton::try_from(ta).unwrap();
2347        let mut interval_tas = IntervalTABuilder::new(lia_ta, SMTSolverBuilder::default(), vec![])
2348            .build()
2349            .unwrap();
2350        let interval_ta = interval_tas.next().unwrap();
2351        assert!(interval_tas.next().is_none());
2352        let ta = ACSThresholdAutomaton::new(interval_ta);
2353
2354        let loc_l3 = ta.idx_ctx.to_cs_loc(&Location::new("l3"));
2355        let loc_l2 = ta.idx_ctx.to_cs_loc(&Location::new("l2"));
2356
2357        let mut loc_state = ACSLocState::new_all_zero(&ta);
2358        loc_state[loc_l2] = 1;
2359        loc_state[loc_l3] = 42;
2360
2361        let got_string = loc_state.display_compact(&ta);
2362        let expected_string = "l2 : 1, l3 : 42";
2363
2364        assert_eq!(&got_string, expected_string);
2365    }
2366}