1use core::fmt;
10use core::ops::{Index, IndexMut};
11
12use log::{debug, warn};
13use taco_display_utils::join_iterator;
14use taco_threshold_automaton::ModifiableThresholdAutomaton;
15use taco_threshold_automaton::lia_threshold_automaton::LIAVariableConstraint;
16use taco_threshold_automaton::{
17 expressions::{
18 And, Atomic, BooleanConnective, BooleanExpression, Location, Or, Parameter, Variable,
19 },
20 impl_bitand,
21};
22
23use crate::internal_spec::extraction::ExtractionError;
24use crate::{ModelCheckerContext, SpecificationTrait, TASpecification};
25use crate::{
26 eltl::{ELTLExpression, remove_negations::NonNegatedELTLExpression},
27 internal_spec::upwards_closed_set::UpwardsClosedSet,
28};
29
30mod extraction;
31pub mod upwards_closed_set;
32
33#[derive(Debug, Clone, PartialEq)]
39pub struct ErrorSpec {
40 name: String,
42 source: Box<ELTLExpression>,
44 ef: ErrorFormula,
46}
47
48impl ErrorSpec {
49 pub fn name(&self) -> &str {
51 &self.name
52 }
53
54 pub fn source(&self) -> &ELTLExpression {
56 &self.source
57 }
58
59 pub fn error_formula(&self) -> &ErrorFormula {
61 &self.ef
62 }
63
64 pub fn create_tas_to_check<T: ModifiableThresholdAutomaton>(
66 &self,
67 ta: &T,
68 ) -> impl Iterator<Item = (ErrorTarget, T)> {
69 self.ef.iter().map(move |at| {
70 let mut ta = ta.clone();
71
72 ta.add_initial_location_constraints(
73 at.init_restriction().location_constraints().iter().cloned(),
74 );
75 ta.add_initial_variable_constraints(
76 at.init_restriction().variable_constraints().iter().cloned(),
77 );
78 ta.add_resilience_conditions(
79 at.init_restriction()
80 .parameter_constraints()
81 .iter()
82 .cloned(),
83 );
84
85 (at.target.clone(), ta)
86 })
87 }
88}
89
90impl fmt::Display for ErrorSpec {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 write!(f, "{}: {}", self.name, self.ef)
93 }
94}
95
96impl<C: ModelCheckerContext> SpecificationTrait<C> for ErrorSpec {
97 type TransformationError = ExtractionError;
98 type InternalSpecType = ErrorTarget;
99
100 fn try_from_eltl(
101 spec: impl Iterator<Item = (String, ELTLExpression)>,
102 _ctx: &C,
103 ) -> Result<(Vec<Self>, Vec<String>), Self::TransformationError> {
104 let mut res_properties = Vec::new();
105 let mut untranslatable = Vec::new();
106
107 for (name, expr) in spec {
108 let res = ErrorSpec::from_named_eltl(name.clone(), expr.clone());
109
110 if matches!(&res, &Err(ExtractionError::NestedTemporalOperator))
111 || matches!(&res, &Err(ExtractionError::UnsupportedConjunction(_, _)))
112 {
113 warn!(
114 "Specification '{name}' contains an operator nesting not yet supported by TACO. It will be reported as unknown."
115 );
116 debug!(
117 "Classified property '{name}' as requiring operator nesting. Expression: {expr}"
118 );
119 untranslatable.push(name);
120 continue;
121 }
122
123 if let Err(err) = &res
126 && contains_globally(&(!expr.clone()).into())
127 {
128 warn!(
129 "Liveness specification '{name}' is not supported by TACO ({err}). It will be reported as unknown."
130 );
131 debug!("Failed to translate liveness property '{name}'. Expression: {expr}");
132 untranslatable.push(name);
133 continue;
134 }
135
136 let res = res?;
137 res_properties.push(res);
138 }
139
140 Ok((res_properties, untranslatable))
141 }
142
143 fn transform_threshold_automaton<
144 TA: taco_threshold_automaton::ThresholdAutomaton
145 + taco_threshold_automaton::ModifiableThresholdAutomaton,
146 >(
147 ta: TA,
148 specs: Vec<Self>,
149 _ctx: &C,
150 ) -> Vec<(String, Self::InternalSpecType, TA)> {
151 specs
152 .into_iter()
153 .flat_map(|spec| {
154 spec.create_tas_to_check(&ta)
155 .map(|(target, ta)| {
156 let target = match target {
159 ErrorTarget::Top => ErrorTarget::Reach(UpwardsClosedSet::new_top()),
160 t => t,
161 };
162 (spec.name.clone(), target, ta)
163 })
164 .collect::<Vec<_>>()
165 })
166 .collect::<Vec<_>>()
167 }
168}
169
170fn contains_globally(expr: &NonNegatedELTLExpression) -> bool {
172 match expr {
173 NonNegatedELTLExpression::Globally(_) => true,
174 NonNegatedELTLExpression::Eventually(e) => contains_globally(e),
175 NonNegatedELTLExpression::And(l, r) | NonNegatedELTLExpression::Or(l, r) => {
176 contains_globally(l) || contains_globally(r)
177 }
178 _ => false,
179 }
180}
181
182pub type ErrorFormula = Disjunction<ErrorAtom>;
192
193#[derive(Debug, Clone, PartialEq)]
199pub struct ErrorAtom {
200 init_restriction: InitRestriction,
202 target: ErrorTarget,
204}
205
206impl ErrorAtom {
207 pub fn new(init_restriction: InitRestriction, target: ErrorTarget) -> Self {
209 Self {
210 init_restriction,
211 target,
212 }
213 }
214
215 pub fn init_restriction(&self) -> &InitRestriction {
217 &self.init_restriction
218 }
219
220 pub fn target(&self) -> &ErrorTarget {
222 &self.target
223 }
224
225 pub fn is_top(&self) -> bool {
227 self.init_restriction.is_top() && matches!(self.target, ErrorTarget::Top)
228 }
229
230 pub fn is_bot(&self) -> bool {
232 self.target.is_bot()
233 }
234
235 fn targets(&self) -> impl Iterator<Item = &UpwardsClosedSet> {
237 self.target.sets()
238 }
239}
240
241impl TASpecification for ErrorAtom {
242 fn locs_appearing(&self) -> impl IntoIterator<Item = &Location> {
243 self.targets().flat_map(|set| set.locs_appearing())
244 }
245
246 fn var_constraint(&self) -> impl IntoIterator<Item = &LIAVariableConstraint> {
247 self.targets().flat_map(|set| set.var_constraint())
248 }
249}
250
251impl TASpecification for ErrorFormula {
252 fn locs_appearing(&self) -> impl IntoIterator<Item = &Location> {
258 self.iter().flat_map(|atom| atom.locs_appearing())
259 }
260
261 fn var_constraint(&self) -> impl IntoIterator<Item = &LIAVariableConstraint> {
266 self.iter().flat_map(|atom| atom.var_constraint())
267 }
268}
269
270impl TASpecification for ErrorTarget {
271 fn locs_appearing(&self) -> impl IntoIterator<Item = &Location> {
273 self.sets().flat_map(|set| set.locs_appearing())
274 }
275
276 fn var_constraint(&self) -> impl IntoIterator<Item = &LIAVariableConstraint> {
278 self.sets().flat_map(|set| set.var_constraint())
279 }
280}
281
282impl fmt::Display for ErrorAtom {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 if !self.init_restriction.is_top() {
285 write!(f, "({}) && ", self.init_restriction)?;
286 }
287
288 write!(f, "({})", self.target)
289 }
290}
291
292#[derive(Debug, Clone, PartialEq)]
294pub enum ErrorTarget {
295 Reach(UpwardsClosedSet),
297 Invariant(UpwardsClosedSet),
299 Ensure {
302 pre: UpwardsClosedSet,
304 inv: UpwardsClosedSet,
306 },
307 Repeat {
310 pre: UpwardsClosedSet,
312 inv: UpwardsClosedSet,
314 },
315 Top,
318}
319
320impl ErrorTarget {
321 pub fn is_bot(&self) -> bool {
323 match self {
324 ErrorTarget::Reach(set) | ErrorTarget::Invariant(set) => set.is_bot(),
325 ErrorTarget::Ensure { pre, inv } | ErrorTarget::Repeat { pre, inv } => {
326 pre.is_bot() || inv.is_bot()
327 }
328 ErrorTarget::Top => false,
329 }
330 }
331
332 pub fn is_top(&self) -> bool {
334 match self {
335 ErrorTarget::Reach(s) | ErrorTarget::Invariant(s) => s.is_top(),
336 ErrorTarget::Ensure { pre, inv } | ErrorTarget::Repeat { pre, inv } => {
337 pre.is_top() && inv.is_top()
338 }
339 ErrorTarget::Top => true,
340 }
341 }
342
343 pub fn new_reach(tgt: UpwardsClosedSet) -> Self {
345 Self::Reach(tgt)
346 }
347
348 pub fn new_invariant(tgt: UpwardsClosedSet) -> Self {
351 Self::Invariant(tgt)
352 }
353
354 pub fn new_ensure(pre: UpwardsClosedSet, inv: UpwardsClosedSet) -> Self {
357 Self::Ensure { pre, inv }
358 }
359
360 pub fn new_repeat(pre: UpwardsClosedSet, inv: UpwardsClosedSet) -> Self {
363 Self::Repeat { pre, inv }
364 }
365
366 pub fn contains_reachability_constraint(&self) -> bool {
372 match self {
373 ErrorTarget::Reach(s) => s.contains_reachability_constraint(),
374 _ => false,
375 }
376 }
377
378 fn sets(&self) -> impl Iterator<Item = &UpwardsClosedSet> {
380 let (pre, inv) = match self {
381 ErrorTarget::Reach(set) | ErrorTarget::Invariant(set) => (Some(set), None),
382 ErrorTarget::Ensure { pre, inv } | ErrorTarget::Repeat { pre, inv } => {
383 (Some(pre), Some(inv))
384 }
385 ErrorTarget::Top => (None, None),
386 };
387
388 [pre, inv].into_iter().flatten()
389 }
390}
391
392impl fmt::Display for ErrorTarget {
393 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
394 match self {
395 ErrorTarget::Reach(set) => write!(f, "<>({set})"),
396 ErrorTarget::Invariant(set) => write!(f, "[]({set})"),
397 ErrorTarget::Ensure { pre, inv } => write!(f, "<>(({pre}) && []({inv}))"),
398 ErrorTarget::Repeat { pre, inv } => write!(f, "[](({pre}) && <>({inv}))"),
399 ErrorTarget::Top => write!(f, "true"),
400 }
401 }
402}
403
404#[derive(Debug, Clone, PartialEq, Default)]
410pub struct InitRestriction {
411 precondition_par: Conjunction<BooleanExpression<Parameter>>,
413 precondition_loc: Conjunction<BooleanExpression<Location>>,
416 precondition_var: Conjunction<BooleanExpression<Variable>>,
419}
420
421impl InitRestriction {
422 pub fn new_top() -> Self {
424 Self::default()
425 }
426
427 pub fn new_parameter_constraint(constr: BooleanExpression<Parameter>) -> Self {
429 Self {
430 precondition_par: [constr].into(),
431 ..Default::default()
432 }
433 }
434
435 pub fn new_location_constraint(constr: BooleanExpression<Location>) -> Self {
438 Self {
439 precondition_loc: [constr].into(),
440 ..Default::default()
441 }
442 }
443
444 pub fn new_variable_constraint(constr: BooleanExpression<Variable>) -> Self {
447 Self {
448 precondition_var: [constr].into(),
449 ..Default::default()
450 }
451 }
452
453 pub fn is_top(&self) -> bool {
455 self.precondition_par.is_empty()
456 && self.precondition_loc.is_empty()
457 && self.precondition_var.is_empty()
458 }
459
460 pub fn try_or(&self, other: &Self) -> Option<Self> {
467 let differing = [
468 self.precondition_par != other.precondition_par,
469 self.precondition_loc != other.precondition_loc,
470 self.precondition_var != other.precondition_var,
471 ]
472 .into_iter()
473 .filter(|differs| *differs)
474 .count();
475
476 if differing > 1 {
477 return None;
478 }
479
480 Some(Self {
482 precondition_par: or_conjunctions(
483 self.precondition_par.clone(),
484 other.precondition_par.clone(),
485 ),
486 precondition_loc: or_conjunctions(
487 self.precondition_loc.clone(),
488 other.precondition_loc.clone(),
489 ),
490 precondition_var: or_conjunctions(
491 self.precondition_var.clone(),
492 other.precondition_var.clone(),
493 ),
494 })
495 }
496
497 pub fn parameter_constraints(&self) -> &Conjunction<BooleanExpression<Parameter>> {
499 &self.precondition_par
500 }
501
502 pub fn location_constraints(&self) -> &Conjunction<BooleanExpression<Location>> {
504 &self.precondition_loc
505 }
506
507 pub fn variable_constraints(&self) -> &Conjunction<BooleanExpression<Variable>> {
509 &self.precondition_var
510 }
511}
512
513fn or_conjunctions<T: Atomic>(
516 lhs: Conjunction<BooleanExpression<T>>,
517 rhs: Conjunction<BooleanExpression<T>>,
518) -> Conjunction<BooleanExpression<T>> {
519 if lhs == rhs {
521 return lhs;
522 }
523
524 if lhs.is_empty() || rhs.is_empty() {
526 return Conjunction::default();
527 }
528
529 let fold = |conj: Conjunction<BooleanExpression<T>>| {
530 conj.into_iter()
531 .reduce(|acc, c| {
532 BooleanExpression::BinaryExpression(
533 Box::new(acc),
534 BooleanConnective::And,
535 Box::new(c),
536 )
537 })
538 .expect("conjunction is not empty")
539 };
540
541 [BooleanExpression::BinaryExpression(
542 Box::new(fold(lhs)),
543 BooleanConnective::Or,
544 Box::new(fold(rhs)),
545 )]
546 .into()
547}
548
549impl And for InitRestriction {
550 fn and(self, other: Self) -> Self {
551 Self {
552 precondition_par: self.precondition_par.and(other.precondition_par),
553 precondition_loc: self.precondition_loc.and(other.precondition_loc),
554 precondition_var: self.precondition_var.and(other.precondition_var),
555 }
556 }
557}
558impl_bitand!(InitRestriction);
559
560impl fmt::Display for InitRestriction {
561 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
562 if self.is_top() {
563 return write!(f, "true");
564 }
565
566 let parts = [
567 self.precondition_par.to_string(),
568 self.precondition_loc.to_string(),
569 self.precondition_var.to_string(),
570 ];
571
572 write!(
573 f,
574 "{}",
575 join_iterator(parts.iter().filter(|p| !p.is_empty()), " && ")
576 )
577 }
578}
579
580#[derive(Debug, Clone)]
582pub struct Disjunction<T: PartialEq>(Vec<T>);
583
584#[derive(Debug, Clone)]
586pub struct Conjunction<T: PartialEq>(Vec<T>);
587
588fn dedup<T: PartialEq>(vec: &mut Vec<T>) {
590 let mut write = 0;
591 for read in 0..vec.len() {
592 if !vec[..write].contains(&vec[read]) {
594 vec.swap(write, read);
595 write += 1;
596 }
597 }
598 vec.truncate(write);
599}
600
601impl<T: PartialEq> Disjunction<T> {
602 pub fn is_empty(&self) -> bool {
604 self.0.is_empty()
605 }
606
607 pub fn len(&self) -> usize {
609 self.0.len()
610 }
611
612 pub fn new_bot() -> Self {
614 Self(Vec::new())
615 }
616
617 pub fn is_bot(&self) -> bool {
619 self.0.is_empty()
620 }
621
622 pub fn get(&self, index: usize) -> Option<&T> {
626 self.0.get(index)
627 }
628
629 pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
633 self.0.get_mut(index)
634 }
635}
636
637impl<T: PartialEq> Index<usize> for Disjunction<T> {
638 type Output = T;
639
640 fn index(&self, index: usize) -> &Self::Output {
646 &self.0[index]
647 }
648}
649
650impl<T: PartialEq> IndexMut<usize> for Disjunction<T> {
651 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
657 &mut self.0[index]
658 }
659}
660
661impl<T: PartialEq> AsRef<[T]> for Disjunction<T> {
662 fn as_ref(&self) -> &[T] {
663 &self.0
664 }
665}
666
667impl<T: PartialEq> AsMut<[T]> for Disjunction<T> {
668 fn as_mut(&mut self) -> &mut [T] {
669 &mut self.0
670 }
671}
672
673impl<T: PartialEq> Default for Disjunction<T> {
674 fn default() -> Self {
676 Self(Vec::new())
677 }
678}
679
680impl<T: PartialEq> PartialEq for Disjunction<T> {
681 fn eq(&self, other: &Self) -> bool {
684 self.0.iter().all(|d| other.0.contains(d)) && other.0.iter().all(|d| self.0.contains(d))
685 }
686}
687
688impl<T: PartialEq> Or for Disjunction<T> {
689 fn or(mut self, other: Self) -> Self {
690 self.0.extend(other.0);
691 dedup(&mut self.0);
692 self
693 }
694}
695
696impl<T: fmt::Display + PartialEq> fmt::Display for Disjunction<T> {
697 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
698 write!(f, "{}", join_iterator(self.0.iter(), " || "))
699 }
700}
701
702impl<T: PartialEq, S: Into<Vec<T>>> From<S> for Disjunction<T> {
703 fn from(value: S) -> Self {
704 Self(value.into())
705 }
706}
707
708impl<T: PartialEq> Disjunction<T> {
709 pub fn iter(&self) -> std::slice::Iter<'_, T> {
711 self.0.iter()
712 }
713
714 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
716 self.0.iter_mut()
717 }
718}
719
720impl<T: PartialEq> IntoIterator for Disjunction<T> {
721 type Item = T;
722
723 type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
724
725 fn into_iter(self) -> Self::IntoIter {
726 self.0.into_iter()
727 }
728}
729
730impl<'a, T: PartialEq> IntoIterator for &'a Disjunction<T> {
731 type Item = &'a T;
732
733 type IntoIter = std::slice::Iter<'a, T>;
734
735 fn into_iter(self) -> Self::IntoIter {
736 self.0.iter()
737 }
738}
739
740impl<'a, T: PartialEq> IntoIterator for &'a mut Disjunction<T> {
741 type Item = &'a mut T;
742
743 type IntoIter = std::slice::IterMut<'a, T>;
744
745 fn into_iter(self) -> Self::IntoIter {
746 self.0.iter_mut()
747 }
748}
749
750impl<T: PartialEq> FromIterator<T> for Disjunction<T> {
751 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
752 Self(iter.into_iter().collect())
753 }
754}
755
756impl<T: PartialEq> Conjunction<T> {
757 pub fn is_empty(&self) -> bool {
759 self.0.is_empty()
760 }
761
762 pub fn len(&self) -> usize {
764 self.0.len()
765 }
766
767 pub fn get(&self, index: usize) -> Option<&T> {
771 self.0.get(index)
772 }
773
774 pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
778 self.0.get_mut(index)
779 }
780}
781
782impl<T: PartialEq> Index<usize> for Conjunction<T> {
783 type Output = T;
784
785 fn index(&self, index: usize) -> &Self::Output {
791 &self.0[index]
792 }
793}
794
795impl<T: PartialEq> IndexMut<usize> for Conjunction<T> {
796 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
802 &mut self.0[index]
803 }
804}
805
806impl<T: PartialEq> AsRef<[T]> for Conjunction<T> {
807 fn as_ref(&self) -> &[T] {
808 &self.0
809 }
810}
811
812impl<T: PartialEq> AsMut<[T]> for Conjunction<T> {
813 fn as_mut(&mut self) -> &mut [T] {
814 &mut self.0
815 }
816}
817
818impl<T: PartialEq> Default for Conjunction<T> {
819 fn default() -> Self {
821 Self(Vec::new())
822 }
823}
824
825impl<T: PartialEq> PartialEq for Conjunction<T> {
826 fn eq(&self, other: &Self) -> bool {
829 self.0.iter().all(|d| other.0.contains(d)) && other.0.iter().all(|d| self.0.contains(d))
830 }
831}
832
833impl<T: PartialEq> And for Conjunction<T> {
834 fn and(mut self, other: Self) -> Self {
835 self.0.extend(other.0);
836 dedup(&mut self.0);
837 self
838 }
839}
840impl<T: fmt::Display + PartialEq> fmt::Display for Conjunction<T> {
841 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842 write!(f, "{}", join_iterator(self.0.iter(), " && "))
843 }
844}
845
846impl<T: PartialEq, S: Into<Vec<T>>> From<S> for Conjunction<T> {
847 fn from(value: S) -> Self {
848 Self(value.into())
849 }
850}
851
852impl<T: PartialEq> Conjunction<T> {
853 pub fn iter(&self) -> std::slice::Iter<'_, T> {
855 self.0.iter()
856 }
857
858 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, T> {
860 self.0.iter_mut()
861 }
862}
863
864impl<T: PartialEq> IntoIterator for Conjunction<T> {
865 type Item = T;
866
867 type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
868
869 fn into_iter(self) -> Self::IntoIter {
870 self.0.into_iter()
871 }
872}
873
874impl<'a, T: PartialEq> IntoIterator for &'a Conjunction<T> {
875 type Item = &'a T;
876
877 type IntoIter = std::slice::Iter<'a, T>;
878
879 fn into_iter(self) -> Self::IntoIter {
880 self.0.iter()
881 }
882}
883
884impl<'a, T: PartialEq> IntoIterator for &'a mut Conjunction<T> {
885 type Item = &'a mut T;
886
887 type IntoIter = std::slice::IterMut<'a, T>;
888
889 fn into_iter(self) -> Self::IntoIter {
890 self.0.iter_mut()
891 }
892}
893
894impl<T: PartialEq> FromIterator<T> for Conjunction<T> {
895 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
896 Self(iter.into_iter().collect())
897 }
898}
899
900#[cfg(test)]
901mod tests {
902 use taco_smt_encoder::SMTSolverBuilder;
903 use taco_threshold_automaton::{
904 ThresholdAutomaton,
905 expressions::{
906 And, BooleanConnective, BooleanExpression, ComparisonOp, IntegerExpression, Location,
907 Or, Parameter, Variable,
908 },
909 general_threshold_automaton::builder::GeneralThresholdAutomatonBuilder,
910 lia_threshold_automaton::{
911 LIAVariableConstraint, integer_thresholds::DeriveFromIntegerComp,
912 },
913 };
914
915 use crate::{
916 SpecificationTrait, TASpecification,
917 eltl::ELTLExpression,
918 internal_spec::{
919 Conjunction, Disjunction, ErrorAtom, ErrorFormula, ErrorSpec, ErrorTarget,
920 InitRestriction, upwards_closed_set::UpwardsClosedSet,
921 },
922 };
923
924 #[test]
925 fn test_try_from_eltl_untranslatable_atom() {
926 let non_linear = |op| {
928 Box::new(ELTLExpression::LocationExpr(
929 Box::new(
930 IntegerExpression::Atom(Location::new("l1"))
931 * IntegerExpression::Atom(Location::new("l2")),
932 ),
933 op,
934 Box::new(IntegerExpression::Const(1)),
935 ))
936 };
937 let safety = ELTLExpression::Globally(non_linear(ComparisonOp::Lt));
938 let liveness = ELTLExpression::Eventually(non_linear(ComparisonOp::Gt));
939
940 let (specs, unknown) = <ErrorSpec as SpecificationTrait<SMTSolverBuilder>>::try_from_eltl(
942 [("live".to_string(), liveness)].into_iter(),
943 &SMTSolverBuilder::default(),
944 )
945 .unwrap();
946 assert!(specs.is_empty());
947 assert_eq!(unknown, vec!["live".to_string()]);
948
949 let res = <ErrorSpec as SpecificationTrait<SMTSolverBuilder>>::try_from_eltl(
951 [("safe".to_string(), safety)].into_iter(),
952 &SMTSolverBuilder::default(),
953 );
954 assert!(res.is_err());
955 }
956
957 #[test]
958 fn test_error_spec_accessors_and_display() {
959 let eltl = ELTLExpression::Globally(Box::new(ELTLExpression::LocationExpr(
961 Box::new(IntegerExpression::Atom(Location::new("l1"))),
962 ComparisonOp::Eq,
963 Box::new(IntegerExpression::Const(0)),
964 )));
965
966 let ef = ErrorFormula::new(
968 InitRestriction::new_top(),
969 ErrorTarget::new_reach(UpwardsClosedSet::new_cover([Location::new("l1")])),
970 );
971
972 let spec = ErrorSpec {
973 name: "safety".to_string(),
974 source: Box::new(eltl.clone()),
975 ef: ef.clone(),
976 };
977
978 assert_eq!(spec.name(), "safety");
979 assert_eq!(spec.source(), &eltl);
980 assert_eq!(spec.error_formula(), &ef);
981 assert_eq!(spec.to_string(), "safety: (<>((l1 >= 1)))");
982 }
983
984 #[test]
985 fn test_create_tas_to_check() {
986 let l1 = Location::new("l1");
987 let l2 = Location::new("l2");
988 let x = Variable::new("x");
989 let n = Parameter::new("n");
990
991 let ta = GeneralThresholdAutomatonBuilder::new("ta".to_string())
992 .with_parameter(n.clone())
993 .unwrap()
994 .with_variable(x.clone())
995 .unwrap()
996 .with_locations([l1.clone(), l2.clone()])
997 .unwrap()
998 .initialize()
999 .build();
1000
1001 let par_constr = BooleanExpression::ComparisonExpression(
1003 Box::new(IntegerExpression::Atom(n.clone())),
1004 ComparisonOp::Gt,
1005 Box::new(IntegerExpression::Const(1)),
1006 );
1007 let loc_constr = BooleanExpression::ComparisonExpression(
1009 Box::new(IntegerExpression::Atom(l1.clone())),
1010 ComparisonOp::Eq,
1011 Box::new(IntegerExpression::Const(0)),
1012 );
1013 let var_constr = BooleanExpression::ComparisonExpression(
1015 Box::new(IntegerExpression::Atom(x.clone())),
1016 ComparisonOp::Eq,
1017 Box::new(IntegerExpression::Const(0)),
1018 );
1019
1020 let restricted = ErrorAtom::new(
1022 InitRestriction::new_parameter_constraint(par_constr.clone())
1023 & InitRestriction::new_location_constraint(loc_constr.clone())
1024 & InitRestriction::new_variable_constraint(var_constr.clone()),
1025 ErrorTarget::new_reach(UpwardsClosedSet::new_cover([l2.clone()])),
1026 );
1027 let unrestricted = ErrorAtom::new(
1028 InitRestriction::new_top(),
1029 ErrorTarget::new_invariant(UpwardsClosedSet::new_cover([l1.clone()])),
1030 );
1031
1032 let spec = ErrorSpec {
1033 name: "spec".to_string(),
1034 source: Box::new(ELTLExpression::True),
1035 ef: Disjunction::from([restricted.clone(), unrestricted.clone()]),
1036 };
1037
1038 let got: Vec<_> = spec.create_tas_to_check(&ta).collect();
1039 assert_eq!(got.len(), 2);
1040
1041 let (target, restricted_ta) = &got[0];
1043 assert_eq!(target, restricted.target());
1044 assert_eq!(restricted_ta.name(), "ta");
1045 assert_eq!(
1046 restricted_ta.resilience_conditions().collect::<Vec<_>>(),
1047 vec![&par_constr]
1048 );
1049 assert_eq!(
1050 restricted_ta
1051 .initial_location_constraints()
1052 .collect::<Vec<_>>(),
1053 vec![&loc_constr]
1054 );
1055 assert_eq!(
1056 restricted_ta
1057 .initial_variable_constraints()
1058 .collect::<Vec<_>>(),
1059 vec![&var_constr]
1060 );
1061
1062 let (target, unrestricted_ta) = &got[1];
1064 assert_eq!(target, unrestricted.target());
1065 assert_eq!(unrestricted_ta, &ta);
1066
1067 let got =
1070 <ErrorSpec as SpecificationTrait<SMTSolverBuilder>>::transform_threshold_automaton(
1071 ta.clone(),
1072 vec![spec.clone(), spec],
1073 &SMTSolverBuilder::default(),
1074 );
1075 assert_eq!(got.len(), 4);
1076 assert_eq!(
1077 (got[0].0.as_str(), &got[0].1),
1078 ("spec", restricted.target())
1079 );
1080 assert_eq!(
1081 (got[1].0.as_str(), &got[1].1),
1082 ("spec", unrestricted.target())
1083 );
1084 assert_eq!(
1085 (got[2].0.as_str(), &got[2].1),
1086 ("spec", restricted.target())
1087 );
1088 assert_eq!(
1089 (got[3].0.as_str(), &got[3].1),
1090 ("spec", unrestricted.target())
1091 );
1092 }
1093
1094 #[test]
1095 fn test_error_atom_accessors_and_predicates() {
1096 let loc_constr = BooleanExpression::ComparisonExpression(
1098 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1099 ComparisonOp::Eq,
1100 Box::new(IntegerExpression::Const(0)),
1101 );
1102 let init = InitRestriction::new_location_constraint(loc_constr);
1103 let target = ErrorTarget::new_reach(UpwardsClosedSet::new_cover([Location::new("l2")]));
1104
1105 let atom = ErrorAtom::new(init.clone(), target.clone());
1106 assert_eq!(atom.init_restriction(), &init);
1107 assert_eq!(atom.target(), &target);
1108 assert!(!atom.is_top());
1109 assert!(!atom.is_bot());
1110
1111 assert!(ErrorAtom::new(InitRestriction::new_top(), ErrorTarget::Top).is_top());
1113 assert!(!ErrorAtom::new(init.clone(), ErrorTarget::Top).is_top());
1114 assert!(
1115 !ErrorAtom::new(
1116 InitRestriction::new_top(),
1117 ErrorTarget::new_reach(UpwardsClosedSet::new_top())
1118 )
1119 .is_top()
1120 );
1121
1122 assert!(
1124 ErrorAtom::new(
1125 InitRestriction::new_top(),
1126 ErrorTarget::new_reach(UpwardsClosedSet::new_bot())
1127 )
1128 .is_bot()
1129 );
1130 }
1131
1132 #[test]
1133 fn test_error_atom_display() {
1134 let target = ErrorTarget::new_reach(UpwardsClosedSet::new_cover([Location::new("l2")]));
1135
1136 let atom = ErrorAtom::new(InitRestriction::new_top(), target.clone());
1138 assert_eq!(atom.to_string(), "(<>((l2 >= 1)))");
1139
1140 let loc_constr = BooleanExpression::ComparisonExpression(
1142 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1143 ComparisonOp::Eq,
1144 Box::new(IntegerExpression::Const(0)),
1145 );
1146 let atom = ErrorAtom::new(InitRestriction::new_location_constraint(loc_constr), target);
1147 assert_eq!(atom.to_string(), "(l1 == 0) && (<>((l2 >= 1)))");
1148 }
1149
1150 #[test]
1151 fn test_ta_specification_for_atom_formula_and_target() {
1152 let x_geq_1 = LIAVariableConstraint::from_integer_expr(
1154 IntegerExpression::Atom(Variable::new("x")),
1155 ComparisonOp::Geq,
1156 IntegerExpression::Const(1),
1157 )
1158 .unwrap();
1159 let y_geq_2 = LIAVariableConstraint::from_integer_expr(
1161 IntegerExpression::Atom(Variable::new("y")),
1162 ComparisonOp::Geq,
1163 IntegerExpression::Const(2),
1164 )
1165 .unwrap();
1166
1167 let pre = UpwardsClosedSet::new_cover([Location::new("l1")])
1168 & UpwardsClosedSet::new_var_constraint(x_geq_1.clone());
1169 let inv = UpwardsClosedSet::new_cover([Location::new("l2")])
1170 & UpwardsClosedSet::new_var_constraint(y_geq_2.clone());
1171
1172 let target = ErrorTarget::new_ensure(pre.clone(), inv.clone());
1174 let mut locs: Vec<_> = target.locs_appearing().into_iter().collect();
1175 locs.sort();
1176 assert_eq!(locs, vec![&Location::new("l1"), &Location::new("l2")]);
1177 assert_eq!(
1178 target.var_constraint().into_iter().collect::<Vec<_>>(),
1179 vec![&x_geq_1, &y_geq_2]
1180 );
1181
1182 let reach = ErrorTarget::new_reach(pre.clone());
1184 assert_eq!(
1185 reach.locs_appearing().into_iter().collect::<Vec<_>>(),
1186 vec![&Location::new("l1")]
1187 );
1188 assert_eq!(
1189 reach.var_constraint().into_iter().collect::<Vec<_>>(),
1190 vec![&x_geq_1]
1191 );
1192
1193 assert_eq!(ErrorTarget::Top.locs_appearing().into_iter().count(), 0);
1195 assert_eq!(ErrorTarget::Top.var_constraint().into_iter().count(), 0);
1196 let atom = ErrorAtom::new(InitRestriction::new_top(), ErrorTarget::Top);
1197 assert_eq!(atom.locs_appearing().into_iter().count(), 0);
1198 assert_eq!(atom.var_constraint().into_iter().count(), 0);
1199
1200 let atom = ErrorAtom::new(InitRestriction::new_top(), target);
1202 let mut locs: Vec<_> = atom.locs_appearing().into_iter().collect();
1203 locs.sort();
1204 assert_eq!(locs, vec![&Location::new("l1"), &Location::new("l2")]);
1205 assert_eq!(
1206 atom.var_constraint().into_iter().collect::<Vec<_>>(),
1207 vec![&x_geq_1, &y_geq_2]
1208 );
1209
1210 let formula: ErrorFormula = Disjunction::from([
1212 ErrorAtom::new(InitRestriction::new_top(), ErrorTarget::new_reach(pre)),
1213 ErrorAtom::new(InitRestriction::new_top(), ErrorTarget::new_invariant(inv)),
1214 ]);
1215 let mut locs: Vec<_> = formula.locs_appearing().into_iter().collect();
1216 locs.sort();
1217 assert_eq!(locs, vec![&Location::new("l1"), &Location::new("l2")]);
1218 assert_eq!(
1219 formula.var_constraint().into_iter().collect::<Vec<_>>(),
1220 vec![&x_geq_1, &y_geq_2]
1221 );
1222 }
1223
1224 #[test]
1225 fn test_error_target_constructors() {
1226 let a = UpwardsClosedSet::new_cover([Location::new("l1")]);
1227 let b = UpwardsClosedSet::new_cover([Location::new("l2")]);
1228
1229 assert_eq!(
1230 ErrorTarget::new_reach(a.clone()),
1231 ErrorTarget::Reach(a.clone())
1232 );
1233 assert_eq!(
1234 ErrorTarget::new_invariant(a.clone()),
1235 ErrorTarget::Invariant(a.clone())
1236 );
1237 assert_eq!(
1238 ErrorTarget::new_ensure(a.clone(), b.clone()),
1239 ErrorTarget::Ensure {
1240 pre: a.clone(),
1241 inv: b.clone()
1242 }
1243 );
1244 assert_eq!(
1245 ErrorTarget::new_repeat(a.clone(), b.clone()),
1246 ErrorTarget::Repeat { pre: a, inv: b }
1247 );
1248 }
1249
1250 #[test]
1251 fn test_error_target_is_bot() {
1252 let set = UpwardsClosedSet::new_cover([Location::new("l1")]);
1253 let bot = UpwardsClosedSet::new_bot();
1254
1255 assert!(!ErrorTarget::new_reach(set.clone()).is_bot());
1256 assert!(ErrorTarget::new_reach(bot.clone()).is_bot());
1257
1258 assert!(!ErrorTarget::new_invariant(set.clone()).is_bot());
1259 assert!(ErrorTarget::new_invariant(bot.clone()).is_bot());
1260
1261 assert!(!ErrorTarget::new_ensure(set.clone(), set.clone()).is_bot());
1263 assert!(ErrorTarget::new_ensure(bot.clone(), set.clone()).is_bot());
1264 assert!(ErrorTarget::new_ensure(set.clone(), bot.clone()).is_bot());
1265
1266 assert!(!ErrorTarget::new_repeat(set.clone(), set.clone()).is_bot());
1267 assert!(ErrorTarget::new_repeat(bot.clone(), set.clone()).is_bot());
1268 assert!(ErrorTarget::new_repeat(set, bot).is_bot());
1269
1270 assert!(!ErrorTarget::Top.is_bot());
1271 }
1272
1273 #[test]
1274 fn test_error_target_is_top() {
1275 let set = UpwardsClosedSet::new_cover([Location::new("l1")]);
1276 let top = UpwardsClosedSet::new_top();
1277
1278 assert!(!ErrorTarget::new_reach(set.clone()).is_top());
1279 assert!(ErrorTarget::new_reach(top.clone()).is_top());
1280
1281 assert!(!ErrorTarget::new_invariant(set.clone()).is_top());
1282 assert!(ErrorTarget::new_invariant(top.clone()).is_top());
1283
1284 assert!(ErrorTarget::new_ensure(top.clone(), top.clone()).is_top());
1286 assert!(!ErrorTarget::new_ensure(set.clone(), top.clone()).is_top());
1287 assert!(!ErrorTarget::new_ensure(top.clone(), set.clone()).is_top());
1288
1289 assert!(ErrorTarget::new_repeat(top.clone(), top.clone()).is_top());
1290 assert!(!ErrorTarget::new_repeat(set.clone(), top.clone()).is_top());
1291 assert!(!ErrorTarget::new_repeat(top, set).is_top());
1292
1293 assert!(ErrorTarget::Top.is_top());
1294 }
1295
1296 #[test]
1297 fn test_error_target_contains_reachability_constraint() {
1298 let reach = UpwardsClosedSet::new_reach([(Location::new("l1"), 1)], [Location::new("l2")]);
1300 let cover = UpwardsClosedSet::new_cover([Location::new("l1")]);
1302
1303 assert!(ErrorTarget::new_reach(reach.clone()).contains_reachability_constraint());
1304 assert!(!ErrorTarget::new_reach(cover.clone()).contains_reachability_constraint());
1305
1306 assert!(!ErrorTarget::new_invariant(reach.clone()).contains_reachability_constraint());
1308 assert!(
1309 !ErrorTarget::new_ensure(reach.clone(), reach.clone())
1310 .contains_reachability_constraint()
1311 );
1312 assert!(!ErrorTarget::new_repeat(reach.clone(), reach).contains_reachability_constraint());
1313
1314 assert!(!ErrorTarget::Top.contains_reachability_constraint());
1315 }
1316
1317 #[test]
1318 fn test_error_target_display() {
1319 let a = UpwardsClosedSet::new_cover([Location::new("l1")]);
1320 let b = UpwardsClosedSet::new_cover([Location::new("l2")]);
1321
1322 assert_eq!(
1323 ErrorTarget::new_reach(a.clone()).to_string(),
1324 "<>((l1 >= 1))"
1325 );
1326 assert_eq!(
1327 ErrorTarget::new_invariant(a.clone()).to_string(),
1328 "[]((l1 >= 1))"
1329 );
1330 assert_eq!(
1331 ErrorTarget::new_ensure(a.clone(), b.clone()).to_string(),
1332 "<>(((l1 >= 1)) && []((l2 >= 1)))"
1333 );
1334 assert_eq!(
1335 ErrorTarget::new_repeat(a, b).to_string(),
1336 "[](((l1 >= 1)) && <>((l2 >= 1)))"
1337 );
1338 assert_eq!(ErrorTarget::Top.to_string(), "true");
1339 }
1340
1341 #[test]
1342 fn test_init_restriction_constructors_and_accessors() {
1343 let top = InitRestriction::new_top();
1344 assert!(top.is_top());
1345 assert!(top.parameter_constraints().is_empty());
1346 assert!(top.location_constraints().is_empty());
1347 assert!(top.variable_constraints().is_empty());
1348
1349 let par_constr = BooleanExpression::ComparisonExpression(
1351 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1352 ComparisonOp::Gt,
1353 Box::new(IntegerExpression::Const(1)),
1354 );
1355 let par = InitRestriction::new_parameter_constraint(par_constr.clone());
1356 assert!(!par.is_top());
1357 assert_eq!(
1358 par.parameter_constraints(),
1359 &Conjunction::from([par_constr])
1360 );
1361 assert!(par.location_constraints().is_empty());
1362 assert!(par.variable_constraints().is_empty());
1363
1364 let loc_constr = BooleanExpression::ComparisonExpression(
1366 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1367 ComparisonOp::Eq,
1368 Box::new(IntegerExpression::Const(0)),
1369 );
1370 let loc = InitRestriction::new_location_constraint(loc_constr.clone());
1371 assert!(!loc.is_top());
1372 assert!(loc.parameter_constraints().is_empty());
1373 assert_eq!(loc.location_constraints(), &Conjunction::from([loc_constr]));
1374 assert!(loc.variable_constraints().is_empty());
1375
1376 let var_constr = BooleanExpression::ComparisonExpression(
1378 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1379 ComparisonOp::Eq,
1380 Box::new(IntegerExpression::Const(0)),
1381 );
1382 let var = InitRestriction::new_variable_constraint(var_constr.clone());
1383 assert!(!var.is_top());
1384 assert!(var.parameter_constraints().is_empty());
1385 assert!(var.location_constraints().is_empty());
1386 assert_eq!(var.variable_constraints(), &Conjunction::from([var_constr]));
1387 }
1388
1389 #[test]
1390 fn test_init_restriction_and() {
1391 let par_constr = BooleanExpression::ComparisonExpression(
1393 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1394 ComparisonOp::Gt,
1395 Box::new(IntegerExpression::Const(1)),
1396 );
1397 let loc_constr = BooleanExpression::ComparisonExpression(
1399 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1400 ComparisonOp::Eq,
1401 Box::new(IntegerExpression::Const(0)),
1402 );
1403 let loc_constr_2 = BooleanExpression::ComparisonExpression(
1405 Box::new(IntegerExpression::Atom(Location::new("l2"))),
1406 ComparisonOp::Eq,
1407 Box::new(IntegerExpression::Const(0)),
1408 );
1409
1410 let got = InitRestriction::new_parameter_constraint(par_constr.clone())
1411 .and(InitRestriction::new_location_constraint(loc_constr.clone()))
1412 & InitRestriction::new_location_constraint(loc_constr_2.clone());
1413
1414 let expected = InitRestriction {
1415 precondition_par: [par_constr].into(),
1416 precondition_loc: [loc_constr.clone(), loc_constr_2].into(),
1417 precondition_var: Conjunction::default(),
1418 };
1419 assert_eq!(got, expected);
1420
1421 let loc = InitRestriction::new_location_constraint(loc_constr);
1423 assert_eq!(loc.clone() & InitRestriction::new_top(), loc);
1424 assert_eq!(loc.clone() & loc.clone(), loc);
1425 }
1426
1427 #[test]
1428 fn test_init_restriction_try_or() {
1429 let par_constr = BooleanExpression::ComparisonExpression(
1431 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1432 ComparisonOp::Gt,
1433 Box::new(IntegerExpression::Const(1)),
1434 );
1435 let loc_constr_1 = BooleanExpression::ComparisonExpression(
1437 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1438 ComparisonOp::Eq,
1439 Box::new(IntegerExpression::Const(0)),
1440 );
1441 let loc_constr_2 = BooleanExpression::ComparisonExpression(
1443 Box::new(IntegerExpression::Atom(Location::new("l2"))),
1444 ComparisonOp::Eq,
1445 Box::new(IntegerExpression::Const(0)),
1446 );
1447 let var_constr = BooleanExpression::ComparisonExpression(
1449 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1450 ComparisonOp::Eq,
1451 Box::new(IntegerExpression::Const(0)),
1452 );
1453
1454 let par = InitRestriction::new_parameter_constraint(par_constr.clone());
1455 let loc_1 = InitRestriction::new_location_constraint(loc_constr_1.clone());
1456 let loc_2 = InitRestriction::new_location_constraint(loc_constr_2.clone());
1457 let var = InitRestriction::new_variable_constraint(var_constr.clone());
1458
1459 assert_eq!(loc_1.try_or(&loc_1), Some(loc_1.clone()));
1461
1462 let got = (par.clone() & loc_1.clone()).try_or(&(par.clone() & loc_2.clone()));
1464 let expected = InitRestriction {
1465 precondition_par: [par_constr].into(),
1466 precondition_loc: [BooleanExpression::BinaryExpression(
1467 Box::new(loc_constr_1.clone()),
1468 BooleanConnective::Or,
1469 Box::new(loc_constr_2.clone()),
1470 )]
1471 .into(),
1472 precondition_var: Conjunction::default(),
1473 };
1474 assert_eq!(got, Some(expected));
1475
1476 let got = (loc_1.clone() & loc_2.clone()).try_or(&loc_1);
1479 let expected = InitRestriction {
1480 precondition_par: Conjunction::default(),
1481 precondition_loc: [BooleanExpression::BinaryExpression(
1482 Box::new(BooleanExpression::BinaryExpression(
1483 Box::new(loc_constr_1.clone()),
1484 BooleanConnective::And,
1485 Box::new(loc_constr_2.clone()),
1486 )),
1487 BooleanConnective::Or,
1488 Box::new(loc_constr_1.clone()),
1489 )]
1490 .into(),
1491 precondition_var: Conjunction::default(),
1492 };
1493 assert_eq!(got, Some(expected));
1494
1495 assert_eq!(
1497 InitRestriction::new_top().try_or(&loc_1),
1498 Some(InitRestriction::new_top())
1499 );
1500 assert_eq!(
1501 loc_1.try_or(&InitRestriction::new_top()),
1502 Some(InitRestriction::new_top())
1503 );
1504
1505 assert_eq!((loc_1.clone() & var.clone()).try_or(&(loc_2 & par)), None);
1507
1508 assert_eq!(loc_1.try_or(&var), None);
1510 }
1511
1512 #[test]
1513 fn test_init_restriction_display() {
1514 assert_eq!(InitRestriction::new_top().to_string(), "true");
1515
1516 let par_constr = BooleanExpression::ComparisonExpression(
1518 Box::new(IntegerExpression::Atom(Parameter::new("n"))),
1519 ComparisonOp::Gt,
1520 Box::new(IntegerExpression::Const(1)),
1521 );
1522 let loc_constr = BooleanExpression::ComparisonExpression(
1524 Box::new(IntegerExpression::Atom(Location::new("l1"))),
1525 ComparisonOp::Eq,
1526 Box::new(IntegerExpression::Const(0)),
1527 );
1528 let var_constr = BooleanExpression::ComparisonExpression(
1530 Box::new(IntegerExpression::Atom(Variable::new("x"))),
1531 ComparisonOp::Eq,
1532 Box::new(IntegerExpression::Const(0)),
1533 );
1534
1535 assert_eq!(
1536 InitRestriction::new_location_constraint(loc_constr.clone()).to_string(),
1537 "l1 == 0"
1538 );
1539
1540 let all = InitRestriction::new_parameter_constraint(par_constr)
1542 & InitRestriction::new_location_constraint(loc_constr)
1543 & InitRestriction::new_variable_constraint(var_constr);
1544 assert_eq!(all.to_string(), "n > 1 && l1 == 0 && x == 0");
1545 }
1546
1547 #[test]
1548 fn test_disjunction_construction_and_predicates() {
1549 let bot: Disjunction<i32> = Disjunction::new_bot();
1550 assert!(bot.is_bot());
1551 assert!(bot.is_empty());
1552 assert_eq!(bot.len(), 0);
1553 assert_eq!(bot, Disjunction::default());
1554
1555 let disj = Disjunction::from([1, 2, 3]);
1556 assert!(!disj.is_bot());
1557 assert!(!disj.is_empty());
1558 assert_eq!(disj.len(), 3);
1559
1560 assert_eq!(disj, [1, 2, 3].into_iter().collect::<Disjunction<_>>());
1562 }
1563
1564 #[test]
1565 fn test_disjunction_element_access() {
1566 let mut disj = Disjunction::from([1, 2, 3]);
1567
1568 assert_eq!(disj.get(1), Some(&2));
1569 assert_eq!(disj.get(3), None);
1570 assert_eq!(disj[0], 1);
1571 assert_eq!(disj.as_ref(), &[1, 2, 3]);
1572
1573 *disj.get_mut(1).unwrap() = 20;
1574 assert_eq!(disj.get_mut(3), None);
1575 disj[0] = 10;
1576 disj.as_mut()[2] = 30;
1577 assert_eq!(disj.as_ref(), &[10, 20, 30]);
1578 }
1579
1580 #[test]
1581 fn test_disjunction_iteration() {
1582 let mut disj = Disjunction::from([1, 2, 3]);
1583
1584 assert_eq!(disj.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
1585 assert_eq!(
1586 (&disj).into_iter().copied().collect::<Vec<_>>(),
1587 vec![1, 2, 3]
1588 );
1589
1590 disj.iter_mut().for_each(|d| *d += 1);
1591 assert_eq!(disj.as_ref(), &[2, 3, 4]);
1592
1593 (&mut disj).into_iter().for_each(|d| *d *= 10);
1594 assert_eq!(disj.as_ref(), &[20, 30, 40]);
1595
1596 assert_eq!(disj.into_iter().collect::<Vec<_>>(), vec![20, 30, 40]);
1597 }
1598
1599 #[test]
1600 fn test_disjunction_eq_or_and_display() {
1601 assert_eq!(Disjunction::from([1, 2]), Disjunction::from([2, 1, 1]));
1603 assert_ne!(Disjunction::from([1, 2]), Disjunction::from([1]));
1604
1605 let got = Disjunction::from([1, 2]).or(Disjunction::from([2, 3]));
1607 assert_eq!(got.as_ref(), &[1, 2, 3]);
1608
1609 assert_eq!(Disjunction::from([1, 2, 3]).to_string(), "1 || 2 || 3");
1610 assert_eq!(Disjunction::<i32>::new_bot().to_string(), "");
1611 }
1612
1613 #[test]
1614 fn test_conjunction_construction_and_predicates() {
1615 let top: Conjunction<i32> = Conjunction::default();
1616 assert!(top.is_empty());
1617 assert_eq!(top.len(), 0);
1618
1619 let conj = Conjunction::from([1, 2, 3]);
1620 assert!(!conj.is_empty());
1621 assert_eq!(conj.len(), 3);
1622
1623 assert_eq!(conj, [1, 2, 3].into_iter().collect::<Conjunction<_>>());
1625 }
1626
1627 #[test]
1628 fn test_conjunction_element_access() {
1629 let mut conj = Conjunction::from([1, 2, 3]);
1630
1631 assert_eq!(conj.get(1), Some(&2));
1632 assert_eq!(conj.get(3), None);
1633 assert_eq!(conj[0], 1);
1634 assert_eq!(conj.as_ref(), &[1, 2, 3]);
1635
1636 *conj.get_mut(1).unwrap() = 20;
1637 assert_eq!(conj.get_mut(3), None);
1638 conj[0] = 10;
1639 conj.as_mut()[2] = 30;
1640 assert_eq!(conj.as_ref(), &[10, 20, 30]);
1641 }
1642
1643 #[test]
1644 fn test_conjunction_iteration() {
1645 let mut conj = Conjunction::from([1, 2, 3]);
1646
1647 assert_eq!(conj.iter().copied().collect::<Vec<_>>(), vec![1, 2, 3]);
1648 assert_eq!(
1649 (&conj).into_iter().copied().collect::<Vec<_>>(),
1650 vec![1, 2, 3]
1651 );
1652
1653 conj.iter_mut().for_each(|c| *c += 1);
1654 assert_eq!(conj.as_ref(), &[2, 3, 4]);
1655
1656 (&mut conj).into_iter().for_each(|c| *c *= 10);
1657 assert_eq!(conj.as_ref(), &[20, 30, 40]);
1658
1659 assert_eq!(conj.into_iter().collect::<Vec<_>>(), vec![20, 30, 40]);
1660 }
1661
1662 #[test]
1663 fn test_conjunction_eq_and_and_display() {
1664 assert_eq!(Conjunction::from([1, 2]), Conjunction::from([2, 1, 1]));
1666 assert_ne!(Conjunction::from([1, 2]), Conjunction::from([1]));
1667
1668 let got = Conjunction::from([1, 2]).and(Conjunction::from([2, 3]));
1670 assert_eq!(got.as_ref(), &[1, 2, 3]);
1671
1672 assert_eq!(Conjunction::from([1, 2, 3]).to_string(), "1 && 2 && 3");
1673 assert_eq!(Conjunction::<i32>::default().to_string(), "");
1674 }
1675}