#[allow(unused_imports)] use super::calc_macro::*; #[allow(unused_imports)] use super::multiset::Multiset; #[allow(unused_imports)] use super::pervasive::*; #[allow(unused_imports)] use super::prelude::*; #[allow(unused_imports)] use super::relations::*; #[allow(unused_imports)] use super::seq::*; #[allow(unused_imports)] use super::set::*; verus! { broadcast use group_seq_lemmas; impl Seq { /// Applies the function `f` to each element of the sequence, and returns /// the resulting sequence. /// The `int` parameter of `f` is the index of the element being mapped. pub open spec fn map(self, f: spec_fn(int, A) -> B) -> Seq { Seq::new(self.len(), |i: int| f(i, self[i])) } /// Applies the function `f` to each element of the sequence, and returns /// the resulting sequence. pub open spec fn map_values(self, f: spec_fn(A) -> B) -> Seq { Seq::new(self.len(), |i: int| f(self[i])) } /// Applies the function `f` to each element of the sequence, /// producing a sequence of sequences, and then concatenates (flattens) /// those into a single flat sequence of `B`. /// /// ## Example /// /// ```rust /// fn example() { /// let s = seq![1, 2, 3]; /// let result = s.flat_map(|x| seq![x, x]); /// assert_eq!(result, seq![1, 1, 2, 2, 3, 3]); /// } /// `` pub open spec fn flat_map(self, f: spec_fn(A) -> Seq) -> Seq { self.map_values(f).flatten() } /// Add a reference (&) to each element of the sequence pub open spec fn as_ref(&self) -> Seq<&A> { Seq::new(self.len(), |i: int| &self[i]) } pub broadcast proof fn lemma_as_ref_index(self, i: int) requires 0 <= i < self.len(), ensures #![trigger self.as_ref(), self[i]] *self.as_ref()[i] == self[i], { } /// Is true if the calling sequence is a prefix of the given sequence 'other'. /// /// ## Example /// /// ```rust /// proof fn prefix_test() { /// let pre: Seq = seq![1, 2, 3]; /// let whole: Seq = seq![1, 2, 3, 4, 5]; /// assert(pre.is_prefix_of(whole)); /// } /// ``` pub open spec fn is_prefix_of(self, other: Self) -> bool { self.len() <= other.len() && self =~= other[..self.len()] } /// Is true if the calling sequence is a suffix of the given sequence 'other'. /// /// ## Example /// /// ```rust /// proof fn suffix_test() { /// let end: Seq = seq![3, 4, 5]; /// let whole: Seq = seq![1, 2, 3, 4, 5]; /// assert(end.is_suffix_of(whole)); /// } /// ``` pub open spec fn is_suffix_of(self, other: Self) -> bool { &&& self.len() <= other.len() &&& self =~= other[other.len() - self.len()..other.len()] } /// Sorts the sequence according to the given leq function /// /// ## Example /// /// ```rust /// {{#include ../../../../examples/multiset.rs:sorted_by_leq}} /// ``` pub closed spec fn sort_by(self, leq: spec_fn(A, A) -> bool) -> Seq recommends total_ordering(leq), decreases self.len(), { if self.len() <= 1 { self } else { let split_index = self.len() / 2; let left = self[..split_index]; let right = self[split_index..]; let left_sorted = left.sort_by(leq); let right_sorted = right.sort_by(leq); merge_sorted_with(left_sorted, right_sorted, leq) } } /// Tests if all elements in the sequence satisfy the predicate. /// /// ## Example /// /// ```rust /// fn example() { /// let s = seq![2, 4, 6, 8]; /// assert!(s.all(|x| x % 2 == 0)); /// } /// ``` pub open spec fn all(self, pred: spec_fn(A) -> bool) -> bool { forall|i: int| 0 <= i < self.len() ==> pred(#[trigger] self[i]) } /// Tests if any element in the sequence satisfies the predicate. /// /// ## Example /// /// ```rust /// fn example() { /// let s = seq![1, 2, 3, 4]; /// assert!(s.any(|x| x > 3)); /// } /// ``` pub open spec fn any(self, pred: spec_fn(A) -> bool) -> bool { exists|i: int| 0 <= i < self.len() && #[trigger] pred(self[i]) } /// Checks that exactly one element in the sequence satisfies the given predicate. /// ## Example /// /// ```rust /// fn example() { /// let s = seq![1, 2, 3]; /// assert!(s.exactly_one(|x| x == 2)); /// } /// ``` pub open spec fn exactly_one(self, pred: spec_fn(A) -> bool) -> bool { self.filter(pred).len() == 1 } pub proof fn lemma_sort_by_ensures(self, leq: spec_fn(A, A) -> bool) requires total_ordering(leq), ensures self.to_multiset() =~= self.sort_by(leq).to_multiset(), sorted_by(self.sort_by(leq), leq), forall|x: A| !self.contains(x) ==> !(#[trigger] self.sort_by(leq).contains(x)), decreases self.len(), { if self.len() <= 1 { } else { let split_index = self.len() / 2; let left = self[..split_index]; let right = self[split_index..]; assert(self =~= left + right); let left_sorted = left.sort_by(leq); left.lemma_sort_by_ensures(leq); let right_sorted = right.sort_by(leq); right.lemma_sort_by_ensures(leq); lemma_merge_sorted_with_ensures(left_sorted, right_sorted, leq); lemma_multiset_commutative(left, right); lemma_multiset_commutative(left_sorted, right_sorted); assert forall|x: A| !self.contains(x) implies !(#[trigger] self.sort_by(leq).contains( x, )) by { broadcast use group_to_multiset_ensures; assert(!self.contains(x) ==> self.to_multiset().count(x) == 0); } } } /// Returns the sequence containing only the elements of the original sequence /// such that pred(element) is true. /// /// ## Example /// /// ```rust /// proof fn filter_test() { /// let seq: Seq = seq![1, 2, 3, 4, 5]; /// let even: Seq = seq.filter(|x| x % 2 == 0); /// reveal_with_fuel(Seq::::filter, 6); //Needed for Verus to unfold the recursive definition of filter /// assert(even =~= seq![2, 4]); /// } /// ``` #[verifier::opaque] pub open spec fn filter(self, pred: spec_fn(A) -> bool) -> Self decreases self.len(), { if self.len() == 0 { self } else { let subseq = self.drop_last().filter(pred); if pred(self.last()) { subseq.push(self.last()) } else { subseq } } } pub broadcast proof fn lemma_filter_len(self, pred: spec_fn(A) -> bool) ensures // the filtered list can't grow #[trigger] self.filter(pred).len() <= self.len(), decreases self.len(), { reveal(Seq::filter); let out = self.filter(pred); if 0 < self.len() { self.drop_last().lemma_filter_len(pred); } } pub broadcast proof fn lemma_filter_pred(self, pred: spec_fn(A) -> bool, i: int) requires 0 <= i < self.filter(pred).len(), ensures pred(#[trigger] self.filter(pred)[i]), { // TODO: remove this after proved filter_lemma is proved #[allow(deprecated)] self.filter_lemma(pred); } pub broadcast proof fn lemma_filter_contains(self, pred: spec_fn(A) -> bool, i: int) requires 0 <= i < self.len() && pred(self[i]), ensures #[trigger] self.filter(pred).contains(self[i]), { // TODO: remove this after proved filter_lemma is proved #[allow(deprecated)] self.filter_lemma(pred); } // deprecated since the triggers inside of 2 of the conjuncts are blocked #[cfg_attr(not(verus_verify_core), deprecated = "Use `broadcast use group_filter_ensures` instead" )] pub proof fn filter_lemma(self, pred: spec_fn(A) -> bool) ensures // we don't keep anything bad // TODO(andrea): recommends didn't catch this error, where i isn't known to be in // self.filter(pred).len() //forall |i: int| 0 <= i < self.len() ==> pred(#[trigger] self.filter(pred)[i]), forall|i: int| 0 <= i < self.filter(pred).len() ==> pred(#[trigger] self.filter(pred)[i]), // we keep everything we should forall|i: int| 0 <= i < self.len() && pred(self[i]) ==> #[trigger] self.filter(pred).contains( self[i], ), // the filtered list can't grow #[trigger] self.filter(pred).len() <= self.len(), decreases self.len(), { reveal(Seq::filter); let out = self.filter(pred); if 0 < self.len() { self.drop_last().filter_lemma(pred); assert forall|i: int| 0 <= i < out.len() implies pred(out[i]) by { if i < out.len() - 1 { assert(self.drop_last().filter(pred)[i] == out.drop_last()[i]); // trigger drop_last assert(pred(out[i])); // TODO(andrea): why is this line required? It's the conclusion of the assert-forall. } } assert forall|i: int| 0 <= i < self.len() && pred(self[i]) implies #[trigger] out.contains(self[i]) by { if i == self.len() - 1 { assert(self[i] == out[out.len() - 1]); // witness to contains } else { let subseq = self.drop_last().filter(pred); assert(subseq.contains(self.drop_last()[i])); // trigger recursive invocation let j = choose|j| 0 <= j < subseq.len() && subseq[j] == self[i]; assert(out[j] == self[i]); // TODO(andrea): same, seems needless } } } } pub broadcast proof fn filter_distributes_over_add(a: Self, b: Self, pred: spec_fn(A) -> bool) ensures #[trigger] (a + b).filter(pred) == a.filter(pred) + b.filter(pred), decreases b.len(), { reveal(Seq::filter); if 0 < b.len() { Self::drop_last_distributes_over_add(a, b); Self::filter_distributes_over_add(a, b.drop_last(), pred); if pred(b.last()) { Self::push_distributes_over_add( a.filter(pred), b.drop_last().filter(pred), b.last(), ); } } else { Self::add_empty_right(a, b); Self::add_empty_right(a.filter(pred), b.filter(pred)); } } /// Returns the sequence containing each element at index `i` of the original sequence /// such that pred(i) is true. /// /// ## Example /// /// ```rust /// proof fn filter_test() { /// let seq: Seq = seq![1, 2, 3, 4, 5]; /// let even_indexed_vals: Seq = seq.filter_index(|i:int| i % 2 == 0); /// reveal_with_fuel(Seq::<_>::filter_index, 6); // Needed for Verus to unfold the recursive definition of filter_index /// assert(even_indexed_vals =~= seq![1, 3, 5]); /// } /// ``` /// Note that the predicate can also refer to elements of the sequence: /// /// ```rust /// proof fn filter_test() { /// let seq: Seq = seq![1, 2, 3, 4, 5]; /// let big_even_indexed_vals: Seq = seq.filter_index(|i:int| i % 2 == 0 && seq[i] >= 3); /// reveal_with_fuel(Seq::<_>::filter_index, 6); // Needed for Verus to unfold the recursive definition of filter_index /// assert(big_even_indexed_vals =~= seq![3, 5]); /// } /// ``` #[verifier::opaque] pub open spec fn filter_index(self, pred: spec_fn(int) -> bool) -> Self decreases self.len(), { if self.len() == 0 { self } else { let subseq = self.drop_last().filter_index(pred); if pred(self.len() - 1) { subseq.push(self.last()) } else { subseq } } } /// Filtering can't increase the sequence's length broadcast proof fn lemma_filter_index_len(self, pred: spec_fn(int) -> bool) ensures #[trigger] (self.filter_index(pred).len()) <= self.len(), decreases self.len(), { reveal(Seq::filter_index); if self.len() != 0 { self.drop_last().lemma_filter_index_len(pred); } } // Helper for one of lemma_filter_index's postconditions proof fn lemma_filter_index_source(self, pred: spec_fn(int) -> bool) ensures self.filter_index_range(pred), decreases self.len(), { reveal(Seq::filter_index); if self.len() != 0 { let s_rest = self.drop_last(); assert(s_rest.len() == self.len() - 1); s_rest.lemma_filter_index_source(pred); let rest = s_rest.filter_index(pred); let result = self.filter_index(pred); let last_idx = (self.len() - 1) as int; assert(forall|k: int| 0 <= k < s_rest.len() ==> #[trigger] s_rest[k] == self[k]); if pred(last_idx) { assert(result =~= rest.push(self.last())); } else { assert(result =~= rest); } assert forall|i: int| 0 <= i < result.len() implies (exists|j: int| 0 <= j < self.len() && #[trigger] result[i] == #[trigger] self[j] && pred(j)) by { if pred(last_idx) && i == rest.len() { assert(result[i] == self[last_idx]); } else { assert(result[i] == rest[i]); let j_rest = choose|j: int| 0 <= j < s_rest.len() && rest[i] == s_rest[j] && pred(j); assert(self[j_rest] == s_rest[j_rest]); } } } } // Helper for another one of lemma_filter_index's postconditions proof fn lemma_filter_index_witness(self, pred: spec_fn(int) -> bool) ensures self.filter_index_domain(pred), decreases self.len(), { reveal(Seq::filter_index); if self.len() != 0 { let s_rest = self.drop_last(); assert(s_rest.len() == self.len() - 1); s_rest.lemma_filter_index_witness(pred); s_rest.lemma_filter_index_len(pred); let rest = s_rest.filter_index(pred); let result = self.filter_index(pred); let last_idx = (self.len() - 1) as int; assert(forall|k: int| 0 <= k < s_rest.len() ==> #[trigger] s_rest[k] == self[k]); if pred(last_idx) { assert(result =~= rest.push(self.last())); } else { assert(result =~= rest); } s_rest.lemma_filter_index_source(pred); assert forall|i: int| 0 <= i < result.len() implies (exists|j: int| 0 <= j < self.len() && #[trigger] result[i] == #[trigger] self[j] && pred(j)) by { if pred(last_idx) && i == rest.len() { assert(result[i] == self[last_idx]); } else { assert(result[i] == rest[i]); let j_rest = choose|j: int| 0 <= j < s_rest.len() && rest[i] == s_rest[j] && pred(j); assert(self[j_rest] == s_rest[j_rest]); } } assert forall|j: int| 0 <= j < self.len() && pred(j) implies (exists|i: int| 0 <= i < self.filter_index(pred).len() && #[trigger] self[j] == self.filter_index( pred, )[i]) by { if j == last_idx { assert(result =~= rest.push(self.last())); assert(self.filter_index(pred)[rest.len() as int] == self[j]); } else { assert(rest.contains(s_rest[j])); let w = choose|i: int| 0 <= i < rest.len() && rest[i] == s_rest[j]; assert(self.filter_index(pred)[w] == self[j]); } } } } /// Every resulting value of filter_index came from self at an acceptable index pub open spec fn filter_index_range(self, pred: spec_fn(int) -> bool) -> bool { forall|i| 0 <= i < self.filter_index(pred).len() ==> (exists|j| 0 <= j < self.len() && #[trigger] self.filter_index(pred)[i] == #[trigger] self[j] && pred(j)) } /// Every value in self at an acceptable index is in the result of filter_index pub open spec fn filter_index_domain(self, pred: spec_fn(int) -> bool) -> bool { forall|j| 0 <= j < self.len() && pred(j) ==> #[trigger] self.filter_index(pred).contains(self[j]) } /// Properties of filter_index pub broadcast proof fn lemma_filter_index(self, pred: spec_fn(int) -> bool) ensures // Filtering can't increase the sequence's length (#[trigger] self.filter_index(pred)).len() <= self.len(), // Every resulting value came from source at an acceptable index self.filter_index_range(pred), // Every value in self at an acceptable index is in the result self.filter_index_domain(pred), decreases self.len(), { self.lemma_filter_index_len(pred); self.lemma_filter_index_source(pred); self.lemma_filter_index_witness(pred); } /// `filter_index` depends only on the predicate's values on the valid index range, /// so two pointwise-equal (but distinct) predicate closures yield the same result. pub proof fn filter_index_ext(self, p: spec_fn(int) -> bool, q: spec_fn(int) -> bool) requires forall|i| 0 <= i < self.len() ==> #[trigger] p(i) == q(i), ensures self.filter_index(p) == self.filter_index(q), decreases self.len(), { reveal(Seq::filter_index); if self.len() != 0 { self.drop_last().filter_index_ext(p, q); } } /// Head decomposition for `filter_index` to better match its use in loops pub proof fn lemma_filter_index_head(self, pred: spec_fn(int) -> bool) requires self.len() > 0, ensures pred(0) ==> self.filter_index(pred) == seq![self[0]] + self.drop_first().filter_index( |i: int| pred(i + 1), ), !pred(0) ==> self.filter_index(pred) == self.drop_first().filter_index( |i: int| pred(i + 1), ), decreases self.len(), { reveal(Seq::filter_index); let p2 = |i: int| pred(i + 1); let t = self.drop_first(); if self.len() == 1 { reveal_with_fuel(Seq::filter_index, 2); assert(t.len() == 0); assert(self.drop_last().len() == 0); if pred(0) { assert(self.filter_index(pred) =~= seq![self[0]] + t.filter_index(p2)); } else { assert(self.filter_index(pred) =~= t.filter_index(p2)); } } else { let sdl = self.drop_last(); sdl.lemma_filter_index_head(pred); assert(t.drop_last() =~= sdl.drop_first()); let p2b = |i: int| pred(i + 1); sdl.drop_first().filter_index_ext(p2, p2b); if pred(0) { assert(self.filter_index(pred) =~= seq![self[0]] + t.filter_index(p2)); } else { assert(self.filter_index(pred) =~= t.filter_index(p2)); } } } pub broadcast proof fn add_empty_left(a: Self, b: Self) requires a.len() == 0, ensures #[trigger] (a + b) == b, { assert(a + b =~= b); } pub broadcast proof fn add_empty_right(a: Self, b: Self) requires b.len() == 0, ensures #[trigger] (a + b) == a, { assert(a + b =~= a); } pub broadcast proof fn push_distributes_over_add(a: Self, b: Self, elt: A) ensures #[trigger] (a + b).push(elt) == a + b.push(elt), { assert((a + b).push(elt) =~= a + b.push(elt)); } /// Returns the maximum value in a non-empty sequence, given sorting function leq pub open spec fn max_via(self, leq: spec_fn(A, A) -> bool) -> A recommends self.len() > 0, decreases self.len(), { if self.len() > 1 { if leq(self[0], self[1..].max_via(leq)) { self[1..].max_via(leq) } else { self[0] } } else { self[0] } } /// Returns the minimum value in a non-empty sequence, given sorting function leq pub open spec fn min_via(self, leq: spec_fn(A, A) -> bool) -> A recommends self.len() > 0, decreases self.len(), { if self.len() > 1 { let subseq = self[1..]; let elt = subseq.min_via(leq); if leq(elt, self[0]) { elt } else { self[0] } } else { self[0] } } // TODO is_sorted -- extract from summer_school e22 pub open spec fn contains(self, needle: A) -> bool { exists|i: int| 0 <= i < self.len() && self[i] == needle } /// Returns an index where `needle` appears in the sequence. /// Returns an arbitrary value if the sequence does not contain the `needle`. pub open spec fn index_of(self, needle: A) -> int { choose|i: int| 0 <= i < self.len() && self[i] == needle } /// For an element that occurs at least once in a sequence, if its first occurence /// is at index i, Some(i) is returned. Otherwise, None is returned pub closed spec fn index_of_first(self, needle: A) -> (result: Option) { if self.contains(needle) { Some(self.first_index_helper(needle)) } else { None } } // Recursive helper function for index_of_first spec fn first_index_helper(self, needle: A) -> int recommends self.contains(needle), decreases self.len(), { if self.len() <= 0 { -1 //arbitrary, will never get to this case } else if self[0] == needle { 0 } else { 1 + self[1..].first_index_helper(needle) } } pub proof fn index_of_first_ensures(self, needle: A) ensures match self.index_of_first(needle) { Some(index) => { &&& self.contains(needle) &&& 0 <= index < self.len() &&& self[index] == needle &&& forall|j: int| 0 <= j < index < self.len() ==> self[j] != needle }, None => { !self.contains(needle) }, }, decreases self.len(), { if self.contains(needle) { let index = self.index_of_first(needle).unwrap(); if self.len() <= 0 { } else if self[0] == needle { } else { assert(Seq::empty().push(self.first()).add(self.drop_first()) =~= self); self.drop_first().index_of_first_ensures(needle); } } } /// For an element that occurs at least once in a sequence, if its last occurence /// is at index i, Some(i) is returned. Otherwise, None is returned pub closed spec fn index_of_last(self, needle: A) -> Option { if self.contains(needle) { Some(self.last_index_helper(needle)) } else { None } } // Recursive helper function for last_index_of spec fn last_index_helper(self, needle: A) -> int recommends self.contains(needle), decreases self.len(), { if self.len() <= 0 { -1 //arbitrary, will never get to this case } else if self.last() == needle { self.len() - 1 } else { self.drop_last().last_index_helper(needle) } } pub proof fn index_of_last_ensures(self, needle: A) ensures match self.index_of_last(needle) { Some(index) => { &&& self.contains(needle) &&& 0 <= index < self.len() &&& self[index] == needle &&& forall|j: int| 0 <= index < j < self.len() ==> self[j] != needle }, None => { !self.contains(needle) }, }, decreases self.len(), { if self.contains(needle) { let index = self.index_of_last(needle).unwrap(); if self.len() <= 0 { } else if self.last() == needle { } else { assert(self.drop_last().push(self.last()) =~= self); self.drop_last().index_of_last_ensures(needle); } } } /// Drops the last element of a sequence and returns a sequence whose length is /// thereby 1 smaller. /// /// If the input sequence is empty, the result is meaningless and arbitrary. pub open spec fn drop_last(self) -> Seq recommends self.len() >= 1, { self.subrange(0, self.len() as int - 1) } /// Dropping the last element of a concatenation of `a` and `b` is equivalent /// to skipping the last element of `b` and then concatenating `a` and `b` pub proof fn drop_last_distributes_over_add(a: Self, b: Self) requires 0 < b.len(), ensures (a + b).drop_last() == a + b.drop_last(), { } pub open spec fn drop_first(self) -> Seq recommends self.len() >= 1, { self.subrange(1, self.len() as int) } /// returns `true` if the sequence has no duplicate elements pub open spec fn no_duplicates(self) -> bool { forall|i, j| (0 <= i < self.len() && 0 <= j < self.len() && i != j) ==> self[i] != self[j] } /// Returns `true` if two sequences are disjoint pub open spec fn disjoint(self, other: Self) -> bool { forall|i: int, j: int| 0 <= i < self.len() && 0 <= j < other.len() ==> self[i] != other[j] } /// Converts a sequence into a set pub closed spec fn to_set(self) -> Set { Set::range(0, self.len() as int).map(|i| self.index(i)) } pub broadcast proof fn to_set_ensures(self) ensures #![trigger(self.to_set())] // to_set works for all indices forall|i| 0 <= i < self.len() ==> #[trigger] self.to_set().contains(self[i]), // to_set finds everything .contains finds forall|a| #[trigger] self.to_set().contains(a) <==> self.contains(a), { broadcast use super::set::group_set_lemmas; broadcast use super::set_lib::range_set_properties; assert forall|i| 0 <= i < self.len() implies #[trigger] self.to_set().contains(self[i]) by { Set::range(0, self.len() as int).lemma_map_contains(|i: int| self.index(i), self[i]); assert(Set::range(0, self.len() as int).contains(i)); assert(self.to_set().contains(self[i])); } assert forall|a| #[trigger] self.to_set().contains(a) <==> self.contains(a) by { Set::range(0, self.len() as int).lemma_map_contains(|i: int| self.index(i), a); if self.to_set().contains(a) { let i = choose|i: int| #[trigger] Set::range(0, self.len() as int).contains(i) && self.index(i) == a; assert(0 <= i < self.len()); assert(self.contains(a)); } if self.contains(a) { let i = choose|i: int| 0 <= i < self.len() && self[i] == a; assert(self.to_set().contains(self[i])); assert(a == self[i]); } } } pub open spec fn to_iset(self) -> ISet { self.to_set().to_iset() } /// Converts a sequence into a multiset pub closed spec fn to_multiset(self) -> Multiset decreases self.len(), { if self.len() == 0 { Multiset::::empty() } else { Multiset::::empty().insert(self.first()).add(self.drop_first().to_multiset()) } } // Parts of verified lemma used to be an axiom in the Dafny prelude // Note: the inner triggers in this lemma are blocked by `to_multiset_len` /// Proof of function to_multiset() correctness pub broadcast proof fn to_multiset_ensures(self) ensures forall|a: A| #[trigger] (self.push(a).to_multiset()) =~= self.to_multiset().insert(a), // to_multiset_build forall|i: int| 0 <= i < self.len() ==> #[trigger] (self.remove(i).to_multiset()) =~= self.to_multiset().remove(self[i]), // to_multiset_remove self.len() == #[trigger] self.to_multiset().len(), // to_multiset_len forall|a: A| self.contains(a) <==> #[trigger] self.to_multiset().count(a) > 0, // to_multiset_contains { broadcast use group_seq_properties; } /// Insert item a at index i, shifting remaining elements (if any) to the right pub open spec fn insert(self, i: int, a: A) -> Seq recommends 0 <= i <= self.len(), { self.subrange(0, i).push(a) + self.subrange(i, self.len() as int) } /// Proof of correctness and expected properties of insert function pub proof fn insert_ensures(self, pos: int, elt: A) requires 0 <= pos <= self.len(), ensures self.insert(pos, elt).len() == self.len() + 1, forall|i: int| 0 <= i < pos ==> #[trigger] self.insert(pos, elt)[i] == self[i], forall|i: int| pos <= i < self.len() ==> self.insert(pos, elt)[i + 1] == self[i], self.insert(pos, elt)[pos] == elt, { } /// Remove item at index i, shifting remaining elements to the left pub open spec fn remove(self, i: int) -> Seq recommends 0 <= i < self.len(), { self.subrange(0, i) + self.subrange(i + 1, self.len() as int) } /// Proof of function remove() correctness pub proof fn remove_ensures(self, i: int) requires 0 <= i < self.len(), ensures self.remove(i).len() == self.len() - 1, forall|index: int| 0 <= index < i ==> #[trigger] self.remove(i)[index] == self[index], forall|index: int| i <= index < self.len() - 1 ==> #[trigger] self.remove(i)[index] == self[index + 1], { } /// If a given element occurs at least once in a sequence, the sequence without /// its first occurrence is returned. Otherwise the same sequence is returned. pub open spec fn remove_value(self, val: A) -> Seq { let index = self.index_of_first(val); match index { Some(i) => self.remove(i), None => self, } } /// Returns the sequence that is in reverse order to a given sequence. pub open spec fn reverse(self) -> Seq decreases self.len(), { if self.len() == 0 { Seq::empty() } else { Seq::new(self.len(), |i: int| self[self.len() - 1 - i]) } } /// Zips two sequences of equal length into one sequence that consists of pairs. /// If the two sequences are different lengths, returns an empty sequence pub open spec fn zip_with(self, other: Seq) -> Seq<(A, B)> recommends self.len() == other.len(), decreases self.len(), { if self.len() != other.len() { Seq::empty() } else if self.len() == 0 { Seq::empty() } else { Seq::new(self.len(), |i: int| (self[i], other[i])) } } /// Truncates both sequences to the minimum length, then zips them. pub open spec fn zip_truncate(self, other: Seq) -> Seq<(A, B)> { if self.len() == other.len() { // Simplify the triggers involved in the common case self.zip_with(other) } else if self.len() < other.len() { self.zip_with(other[..self.len()]) } else { self[..other.len()].zip_with(other) } } /// Folds the sequence to the left, applying `f` to perform the fold. /// /// Equivalent to `Iterator::fold` in Rust. /// /// Given a sequence `s = [x0, x1, x2, ..., xn]`, applying this function `s.fold_left(b, f)` /// returns `f(...f(f(b, x0), x1), ..., xn)`. pub open spec fn fold_left(self, b: B, f: spec_fn(B, A) -> B) -> (res: B) decreases self.len(), { if self.len() == 0 { b } else { f(self.drop_last().fold_left(b, f), self.last()) } } /// Equivalent to [`Self::fold_left`] but defined by breaking off the leftmost element when /// recursing, rather than the rightmost. See [`Self::lemma_fold_left_alt`] that proves /// equivalence. pub open spec fn fold_left_alt(self, b: B, f: spec_fn(B, A) -> B) -> (res: B) decreases self.len(), { if self.len() == 0 { b } else { self[1..].fold_left_alt(f(b, self[0]), f) } } /// A lemma that proves how [`Self::fold_left`] distributes over splitting a sequence. pub broadcast proof fn lemma_fold_left_split(self, b: B, f: spec_fn(B, A) -> B, k: int) requires 0 <= k <= self.len(), ensures self[k..].fold_left((#[trigger] self[..k].fold_left(b, f)), f) == self.fold_left(b, f), decreases self.len(), { reveal_with_fuel(Seq::fold_left, 2); if k == self.len() { assert(self[0..] == self); } else { self.drop_last().lemma_fold_left_split(b, f, k); assert(self.drop_last()[k..self.drop_last().len()] =~= self[k..self.len() - 1]); assert(self.drop_last()[..k] =~= self[..k]); assert(self[k..].drop_last() =~= self[k..self.len() - 1]); } } /// An auxiliary lemma for proving [`Self::lemma_fold_left_alt`]. proof fn aux_lemma_fold_left_alt(self, b: B, f: spec_fn(B, A) -> B, k: int) requires 0 < k <= self.len(), ensures self[k..].fold_left_alt(self[..k].fold_left_alt(b, f), f) == self.fold_left_alt(b, f), decreases k, { reveal_with_fuel(Seq::fold_left_alt, 2); if k == 1 { // trivial base case } else { self[1..].aux_lemma_fold_left_alt(f(b, self[0]), f, k - 1); assert(self[1..][k - 1..self[1..].len()] =~= self[k..]); assert(self[1..][..k - 1] =~= self[1..k]); assert(self[..k][1..self[..k].len()] =~= self[1..k]); } } /// [`Self::fold_left`] and [`Self::fold_left_alt`] are equivalent. pub proof fn lemma_fold_left_alt(self, b: B, f: spec_fn(B, A) -> B) ensures self.fold_left(b, f) == self.fold_left_alt(b, f), decreases self.len(), { reveal_with_fuel(Seq::fold_left, 2); reveal_with_fuel(Seq::fold_left_alt, 2); if self.len() <= 1 { // trivial base cases } else { self.aux_lemma_fold_left_alt(b, f, self.len() - 1); self[self.len() - 1..].lemma_fold_left_alt(self.drop_last().fold_left_alt(b, f), f); self[..self.len() - 1].lemma_fold_left_alt(b, f); } } /// [`Self::fold_left`] on the reversed sequence is equivalent to /// [`Self::fold_right`] on the original sequence with corresponding folding operator pub proof fn lemma_reverse_fold_left(self, v: B, f: spec_fn(B, A) -> B) ensures self.reverse().fold_left(v, f) == self.fold_right(|a: A, b: B| f(b, a), v), { assert(self.reverse().reverse() =~= self); let g = |a: A, b: B| f(b, a); assert(f =~= |b: B, a: A| g(a, b)); self.reverse().lemma_reverse_fold_right(v, |a: A, b: B| f(b, a)) } /// Folds the sequence to the right, applying `f` to perform the fold. /// /// Equivalent to `DoubleEndedIterator::rfold` in Rust. /// /// Given a sequence `s = [x0, x1, x2, ..., xn]`, applying this function `s.fold_right(b, f)` /// returns `f(x0, f(x1, f(x2, ..., f(xn, b)...)))`. pub open spec fn fold_right(self, f: spec_fn(A, B) -> B, b: B) -> (res: B) decreases self.len(), { if self.len() == 0 { b } else { self.drop_last().fold_right(f, f(self.last(), b)) } } /// Equivalent to [`Self::fold_right`] but defined by breaking off the leftmost element when /// recursing, rather than the rightmost. See [`Self::lemma_fold_right_alt`] that proves /// equivalence. pub open spec fn fold_right_alt(self, f: spec_fn(A, B) -> B, b: B) -> (res: B) decreases self.len(), { if self.len() == 0 { b } else { f(self[0], self[1..].fold_right_alt(f, b)) } } /// A lemma that proves how [`Self::fold_right`] distributes over splitting a sequence. pub broadcast proof fn lemma_fold_right_split(self, f: spec_fn(A, B) -> B, b: B, k: int) requires 0 <= k <= self.len(), ensures self[..k].fold_right(f, (#[trigger] self[k..].fold_right(f, b))) == self.fold_right( f, b, ), decreases self.len(), { reveal_with_fuel(Seq::fold_right, 2); if k == self.len() { assert(self[..k] == self); } else if k == self.len() - 1 { // trivial base case } else { self[..self.len() - 1].lemma_fold_right_split(f, f(self.last(), b), k); assert(self[..self.len() - 1][..k] =~= self[..k]); assert(self[..self.len() - 1][k..self[..self.len() - 1].len()] =~= self[k..self.len() - 1]); assert(self[k..].drop_last() =~= self[k..self.len() - 1]); } } // Lemma that proves it's possible to commute a commutative operator across fold_right. pub proof fn lemma_fold_right_commute_one(self, a: A, f: spec_fn(A, B) -> B, v: B) requires commutative_foldr(f), ensures self.fold_right(f, f(a, v)) == f(a, self.fold_right(f, v)), decreases self.len(), { if self.len() > 0 { self.drop_last().lemma_fold_right_commute_one(a, f, f(self.last(), v)); } } /// [`Self::fold_right`] and [`Self::fold_right_alt`] are equivalent. pub proof fn lemma_fold_right_alt(self, f: spec_fn(A, B) -> B, b: B) ensures self.fold_right(f, b) == self.fold_right_alt(f, b), decreases self.len(), { reveal_with_fuel(Seq::fold_right, 2); reveal_with_fuel(Seq::fold_right_alt, 2); if self.len() <= 1 { // trivial base cases } else { self[1..].lemma_fold_right_alt(f, b); self.lemma_fold_right_split(f, b, 1); } } /// [`Self::fold_right`] on the reversed sequence is equivalent to /// [`Self::fold_left`] on the original sequence with corresponding folding operator pub proof fn lemma_reverse_fold_right(self, v: B, f: spec_fn(A, B) -> B) ensures self.reverse().fold_right(f, v) == self.fold_left(v, |b: B, a: A| f(a, b)), decreases self.len(), { let g = |b: B, a: A| f(a, b); if self.len() > 0 { let last = self.last(); let s0 = self.drop_last(); assert(self.reverse() =~= seq![last] + s0.reverse()); let res1 = self.reverse().fold_right(f, v); let res2 = self.fold_left(v, g); assert(res1 == self.reverse().fold_right_alt(f, v)) by { self.reverse().lemma_fold_right_alt(f, v) } assert(res2 == g(s0.fold_left(v, g), last)); assert(self.reverse().first() == last); assert(self.reverse()[1..self.reverse().len()] =~= s0.reverse()); assert(res1 == f(last, s0.reverse().fold_right_alt(f, v))); assert(res1 == f(last, s0.reverse().fold_right(f, v))) by { s0.reverse().lemma_fold_right_alt(f, v) } assert(res2 == g(s0.fold_left(v, g), last)); s0.lemma_reverse_fold_right(v, f); } } // Proven lemmas /// Given a sequence with no duplicates, each element occurs only /// once in its conversion to a multiset pub proof fn lemma_multiset_has_no_duplicates(self) requires self.no_duplicates(), ensures forall|x: A| self.to_multiset().contains(x) ==> self.to_multiset().count(x) == 1, decreases self.len(), { broadcast use super::multiset::group_multiset_axioms; if self.len() == 0 { assert(forall|x: A| self.to_multiset().contains(x) ==> self.to_multiset().count(x) == 1); } else { broadcast use group_seq_properties; assert(self.drop_last().push(self.last()) =~= self); self.drop_last().lemma_multiset_has_no_duplicates(); } } /// If, in a sequence's conversion to a multiset, each element occurs only once, /// the sequence has no duplicates. pub proof fn lemma_multiset_has_no_duplicates_conv(self) requires forall|x: A| self.to_multiset().contains(x) ==> self.to_multiset().count(x) == 1, ensures self.no_duplicates(), { broadcast use super::multiset::group_multiset_axioms; assert forall|i, j| (0 <= i < self.len() && 0 <= j < self.len() && i != j) implies self[i] != self[j] by { let mut a = if (i < j) { i } else { j }; let mut b = if (i < j) { j } else { i }; if (self[a] == self[b]) { let s0 = self[..b]; let s1 = self[b..]; assert(self == s0 + s1); broadcast use group_to_multiset_ensures; lemma_multiset_commutative(s0, s1); assert(self.to_multiset().count(self[a]) >= 2); } } } /// Conversion of a sequence to multiset is equivalent to conversion of its reversion to multiset pub proof fn lemma_reverse_to_multiset(self) ensures self.reverse().to_multiset() =~= self.to_multiset(), decreases self.len(), { broadcast use group_seq_properties; broadcast use super::multiset::group_multiset_axioms; if self.len() > 0 { let s2 = self.drop_first(); let e = self.first(); assert(self =~= seq![e] + s2); assert(self.to_multiset() =~= seq![e].to_multiset().add(s2.to_multiset())) by { lemma_multiset_commutative(seq![e], s2) } assert(self.reverse() =~= s2.reverse().push(e)); assert(self.reverse().to_multiset() =~= s2.reverse().to_multiset().insert(e)); s2.lemma_reverse_to_multiset(); } } /// The concatenation of two subsequences derived from a non-empty sequence, /// the first obtained from skipping the last element, the second consisting only /// of the last element, is the original sequence. pub proof fn lemma_add_last_back(self) requires 0 < self.len(), ensures #[trigger] self.drop_last().push(self.last()) =~= self, { } /// If a predicate is true at every index of a sequence, /// it is true for every member of the sequence as a collection. /// Useful for converting quantifiers between the two forms /// to satisfy a precondition in the latter form. pub proof fn lemma_indexing_implies_membership(self, f: spec_fn(A) -> bool) requires forall|i: int| 0 <= i < self.len() ==> #[trigger] f(#[trigger] self[i]), ensures forall|x: A| #[trigger] self.contains(x) ==> #[trigger] f(x), { assert(forall|i: int| 0 <= i < self.len() ==> #[trigger] self.contains(self[i])); } /// If a predicate is true for every member of a sequence as a collection, /// it is true at every index of the sequence. /// Useful for converting quantifiers between the two forms /// to satisfy a precondition in the latter form. pub proof fn lemma_membership_implies_indexing(self, f: spec_fn(A) -> bool) requires forall|x: A| #[trigger] self.contains(x) ==> #[trigger] f(x), ensures forall|i: int| 0 <= i < self.len() ==> #[trigger] f(self[i]), { assert forall|i: int| 0 <= i < self.len() implies #[trigger] f(self[i]) by { assert(self.contains(self[i])); } } /// A sequence that is sliced at the pos-th element, concatenated /// with that same sequence sliced from the pos-th element, is equal to the /// original unsliced sequence. pub proof fn lemma_split_at(self, pos: int) requires 0 <= pos <= self.len(), ensures self[..pos] + self[pos..] =~= self, { } /// Any element in a slice is included in the original sequence. pub proof fn lemma_element_from_slice(self, new: Seq, a: int, b: int, pos: int) requires 0 <= a <= b <= self.len(), new == self[a..b], a <= pos < b, ensures pos - a < new.len(), new[pos - a] == self[pos], { } /// A slice (from s2..e2) of a slice (from s1..e1) of a sequence is equal to just a /// slice (s1+s2..s1+e2) of the original sequence. pub proof fn lemma_slice_of_slice(self, s1: int, e1: int, s2: int, e2: int) requires 0 <= s1 <= e1 <= self.len(), 0 <= s2 <= e2 <= e1 - s1, ensures self[s1..e1][s2..e2] =~= self[s1 + s2..s1 + e2], { } /// A sequence of unique items, when converted to a set, produces a set with matching length pub proof fn unique_seq_to_set(self) requires self.no_duplicates(), ensures self.len() == self.to_set().len(), decreases self.len(), { broadcast use super::set::group_set_lemmas; seq_to_set_equal_rec::(self); if self.len() == 0 { } else { let rest = self.drop_last(); rest.unique_seq_to_set(); seq_to_set_equal_rec::(rest); assert(!rest.contains(self.last())); assert(!seq_to_set_rec(rest).contains(self.last())) by { seq_to_set_rec_contains::(rest); } assert(seq_to_set_rec(rest).insert(self.last()).len() == seq_to_set_rec(rest).len() + 1); } } /// The cardinality of a set of elements is always less than or /// equal to that of the full sequence of elements. pub proof fn lemma_cardinality_of_set(self) ensures self.to_set().len() <= self.len(), { broadcast use super::set_lib::range_set_properties; super::set_lib::lemma_map_size_bound::( Set::range(0, self.len() as int), self.to_set(), |i: int| self.index(i), ); } /// A sequence is of length 0 if and only if its conversion to /// a set results in the empty set. pub proof fn lemma_cardinality_of_empty_set_is_0(self) ensures self.to_set().len() == 0 <==> self.len() == 0, { broadcast use super::set::group_set_lemmas; self.to_set_ensures(); assert(self.len() == 0 ==> self.to_set().len() == 0) by { self.lemma_cardinality_of_set() } assert(!(self.len() == 0) ==> !(self.to_set().len() == 0)) by { if self.len() > 0 { assert(self.to_set().contains(self[0])); assert(self.to_set().remove(self[0]).len() <= self.to_set().len()); } } } /// A sequence with cardinality equal to its set has no duplicates. /// Inverse property of that shown in lemma unique_seq_to_set pub proof fn lemma_no_dup_set_cardinality(self) requires self.to_set().len() == self.len(), ensures self.no_duplicates(), decreases self.len(), { broadcast use super::set::group_set_lemmas; self.to_set_ensures(); self.drop_first().to_set_ensures(); if self.len() == 0 { } else { assert(self =~= Seq::empty().push(self.first()).add(self.drop_first())); if self.drop_first().contains(self.first()) { // If there is a duplicate, then we show that |s.to_set()| == |s| cannot hold. assert(self.to_set() =~= self.drop_first().to_set()); assert(self.to_set().len() <= self.drop_first().len()) by { self.drop_first().lemma_cardinality_of_set() } } else { assert(self.to_set().len() == 1 + self.drop_first().to_set().len()) by { assert(self.drop_first().to_set().insert(self.first()) =~= self.to_set()); } self.drop_first().lemma_no_dup_set_cardinality(); } } } /// Mapping a function over a sequence and converting to a set is the same /// as mapping it over the sequence converted to a set. pub broadcast proof fn lemma_to_set_map_commutes(self, f: spec_fn(A) -> B) ensures #[trigger] self.to_set().map(f) =~= self.map_values(f).to_set(), { broadcast use crate::vstd::group_vstd_default; assert forall|elem: B| self.to_set().map(f).contains(elem) <==> self.map_values(f).to_set().contains(elem) by { if self.to_set().map(f).contains(elem) { let x = choose|x: A| self.to_set().contains(x) && f(x) == elem; let i = choose|i: int| 0 <= i < self.len() && self[i] == x; assert(self.map_values(f)[i] == elem); } if self.map_values(f).to_set().contains(elem) { let i = choose|i: int| 0 <= i < self.map_values(f).len() && self.map_values(f)[i] == elem; let x = self[i]; assert(self.to_set().contains(x)); } }; } /// Mapping a function over a sequence and converting to an ISet is the same /// as mapping it over the sequence converted to an ISet. pub broadcast proof fn lemma_to_iset_map_commutes(self, f: spec_fn(A) -> B) ensures #[trigger] self.to_iset().map(f) =~= self.map_values(f).to_iset(), { broadcast use crate::vstd::group_vstd_default; assert forall|elem: B| self.to_iset().map(f).contains(elem) <==> self.map_values(f).to_iset().contains( elem, ) by { if self.to_iset().map(f).contains(elem) { let x = choose|x: A| self.to_iset().contains(x) && f(x) == elem; let i = choose|i: int| 0 <= i < self.len() && self[i] == x; assert(self.map_values(f)[i] == elem); } if self.map_values(f).to_iset().contains(elem) { let i = choose|i: int| 0 <= i < self.map_values(f).len() && self.map_values(f)[i] == elem; let x = self[i]; assert(self.to_iset().contains(x)); } }; } /// Appending an element to a sequence and converting to a Set is equal /// to converting to a Set and inserting the element. pub broadcast proof fn lemma_to_set_insert_commutes(sq: Seq, elt: A) ensures #[trigger] (sq + seq![elt]).to_set() =~= sq.to_set().insert(elt), { broadcast use crate::vstd::group_vstd_default; broadcast use lemma_seq_concat_contains_all_elements; broadcast use lemma_seq_empty_contains_nothing; broadcast use lemma_seq_contains_after_push; broadcast use super::seq::group_seq_lemmas; broadcast use super::set_lib::group_set_properties; } /// Appending an element to a sequence and converting to an ISet is equal /// to converting to an ISet and inserting the element. pub broadcast proof fn lemma_to_iset_insert_commutes(sq: Seq, elt: A) ensures #[trigger] (sq + seq![elt]).to_iset() =~= sq.to_iset().insert(elt), { broadcast use crate::vstd::group_vstd_default; broadcast use lemma_seq_concat_contains_all_elements; broadcast use lemma_seq_empty_contains_nothing; broadcast use lemma_seq_contains_after_push; broadcast use super::seq::group_seq_lemmas; broadcast use super::set_lib::group_set_properties; } /// Update a subrange of a sequence starting at `off` to values `vs`. /// Expects that the updated subrange `off` up to `off+vs.len()` fits /// in the existing sequence. pub open spec fn update_subrange_with(self, off: int, vs: Self) -> Self recommends 0 <= off, off + vs.len() <= self.len(), { Seq::new( self.len(), |i: int| if off <= i < off + vs.len() { vs[i - off] } else { self[i] }, ) } /// Skipping `i` elements and then 1 more is equivalent to skipping `i + 1` elements. /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![1, 2, 3, 4]; /// s.lemma_seq_skip_skip(2); /// assert(s[2..][1..] =~= s[3..]); /// } /// ``` pub broadcast proof fn lemma_seq_skip_skip(self, i: int) ensures 0 <= i < self.len() ==> self[i..][1..] =~= #[trigger] self[i + 1..], { broadcast use group_seq_properties; } /// If an element is contained in a sequence, then there exists an index where that element appears. /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![10, 20, 30]; /// assert(s.contains(20)); /// let idx = s.lemma_contains_to_index(20); /// assert(s[idx] == 20); /// } /// ``` pub proof fn lemma_contains_to_index(self, elem: A) -> (idx: int) requires self.contains(elem), ensures 0 <= idx < self.len() && self[idx] == elem, decreases self.len(), { broadcast use group_seq_properties; if self[0] == elem { 0 } else { let i = self[1..].lemma_contains_to_index(elem); i + 1 } } /// If a predicate holds for the first element and for all elements in the tail, /// then it holds for the entire sequence. /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![2, 4, 6, 8]; /// let is_even = |x| x % 2 == 0; /// assert(is_even(s[0])); /// assert(s[1..].all(is_even)); /// s.lemma_all_from_head_tail(is_even); /// assert(s.all(is_even)); /// } /// ``` pub proof fn lemma_all_from_head_tail(self, pred: spec_fn(A) -> bool) requires self.len() > 0, pred(self[0]) && self[1..].all(|x| pred(x)), ensures self.all(|x| pred(x)), { broadcast use group_seq_properties; assert(seq![self[0]] + self[1..] == self); } /// If a predicate holds for any element in the sequence and does not hold for the first element, /// then the predicate must hold for some element in the tail. /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![1, 4, 6, 8]; /// let is_even = |x| x % 2 == 0; /// assert(s.any(is_even)); /// assert(!is_even(s[0])); /// s.lemma_any_tail(is_even); /// assert(s[1..].any(is_even)); /// } /// ``` pub proof fn lemma_any_tail(self, pred: spec_fn(A) -> bool) requires self.any(|x| pred(x)), ensures !pred(self[0]) ==> self[1..].any(|x| pred(x)), { broadcast use group_seq_properties; } /// Removes duplicate elements from a sequence, maintaining the order of first appearance. /// Takes a `seen` sequence parameter to track previously encountered elements. /// /// ## Example /// /// ```rust /// fn example() { /// let s = seq![1, 2, 1, 3, 2, 4]; /// let seen = seq![]; /// let result = s.remove_duplicates(seen); /// assert_eq!(result, seq![1, 2, 3, 4]); /// /// let seen2 = seq![2, 3]; /// let result2 = s.remove_duplicates(seen2); /// assert_eq!(result2, seq![1, 4]); /// } /// ``` pub open spec fn remove_duplicates(self, seen: Seq) -> Seq decreases self.len(), { if self.len() == 0 { seen } else if seen.contains(self[0]) { self[1..].remove_duplicates(seen) } else { self[1..].remove_duplicates(seen + seq![self[0]]) } } /// Properties of remove_duplicates: /// - The output contains x if and only if x was in the input sequence or seen set /// - The output length is at most the sum of input and seen lengths /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![1, 2, 1, 3]; /// let seen = seq![2]; /// s.lemma_remove_duplicates_properties(seen); /// assert(s.remove_duplicates(seen).contains(1)); /// assert(s.remove_duplicates(seen).contains(3)); /// assert(!s.remove_duplicates(seen).contains(2)); /// assert(s.remove_duplicates(seen).len() <= s.len() + seen.len()); /// } /// ``` pub broadcast proof fn lemma_remove_duplicates_properties(self, seen: Seq) ensures forall|x| (self + seen).contains(x) <==> #[trigger] self.remove_duplicates(seen).contains(x), #[trigger] self.remove_duplicates(seen).len() <= self.len() + seen.len(), decreases self.len(), { broadcast use group_seq_properties; if self.len() == 0 { } else if seen.contains(self[0]) { let rest = self[1..]; rest.lemma_remove_duplicates_properties(seen); } else { let rest = self[1..]; rest.lemma_remove_duplicates_properties(seen + seq![self[0]]); } } /// Shows that removing duplicates from a sequence is equivalent to: /// 1. First removing duplicates from the prefix up to index i (with the given seen set) /// 2. Using that result as the new seen set for removing duplicates from the suffix after i /// /// ## Example /// /// ```rust /// proof fn example() { /// let s = seq![1, 2, 1, 3, 2, 4]; /// let seen = seq![]; /// s.lemma_remove_duplicates_append_index(seen, 2); /// assert(s.remove_duplicates(seen) /// =~= seq![1, 3, 2, 4].remove_duplicates(seq![1, 2].remove_duplicates(seen))); /// } /// ``` pub proof fn lemma_remove_duplicates_append_index(self, i: int, seen: Seq) requires 0 <= i < self.len(), ensures self.remove_duplicates(seen) == self[i..].remove_duplicates( self[..i].remove_duplicates(seen), ), decreases self.len(), { broadcast use { group_seq_properties, lemma_seq_skip_of_skip, Seq::lemma_remove_duplicates_properties, }; if i == 0 { } else if i == self.len() { assert(self[..i] == self); } else { assert(self[1..][..i - 1] == self[1..i]); assert(self[..i][1..] == self[1..i]); assert(self[1..][..i - 1] == self[..i][1..]); if seen.contains(self[0]) { self[1..].lemma_remove_duplicates_append_index(i - 1, seen); } else { self[1..].lemma_remove_duplicates_append_index(i - 1, seen + seq![self[0]]); } } } /// For two sequences, skipping one element after concatenation equals concatenating /// the result of skipping one element of the first sequence (which must be non-empty) /// with the second sequence. /// /// ## Example /// ```rust /// proof fn example() { /// let s1 = seq![1, 2]; /// let s2 = seq![3, 4, 5]; /// /// lemma_skip1_concat(s1, s2); /// assert((s1 + s2)[1..] =~= seq![2, 3, 4, 5]); /// } /// ``` proof fn lemma_skip1_concat(xs: Seq, ys: Seq) requires xs.len() > 0, ensures (xs + ys)[1..] == xs[1..] + ys, { broadcast use group_seq_properties; assert((xs + ys)[1..] == xs[1..] + ys); } /// When appending an element `x` to a sequence: /// - If `x` is in `self + seen`, removing duplicates equals removing duplicates from self /// - If `x` is not in (self + seen), removing duplicates equals removing duplicates from self, /// concatenated with `[x]` /// /// ## Example /// ```rust /// proof fn example() { /// let s1 = seq![1, 2]; /// let seen = seq![]; /// assert!(!s1.contains(3)); /// lemma_remove_duplicates_append(s1, 3, seen); /// assert((s1 + seq![3]).remove_duplicates(seen) =~= s1.remove_duplicates(seen) + seq![3]); /// } /// ``` pub proof fn lemma_remove_duplicates_append(self, x: A, seen: Seq) ensures (self + seen).contains(x) ==> (self + seq![x]).remove_duplicates(seen) == self.remove_duplicates(seen), !(self + seen).contains(x) ==> (self + seq![x]).remove_duplicates(seen) == self.remove_duplicates(seen) + seq![x], decreases self.len(), { broadcast use group_seq_properties; reveal_with_fuel(Seq::remove_duplicates, 2); if self.len() != 0 { let head = self[0]; let tail = self[1..]; let seen2 = if seen.contains(head) { seen } else { seen + seq![head] }; tail.lemma_remove_duplicates_append(x, seen2); assert((self + seq![x])[1..] == tail + seq![x]) by { Seq::lemma_skip1_concat(self, seq![x]); }; } } /// If all elements in a sequence fail the predicate, /// filtering by that predicate yields an empty sequence /// /// ## Example /// ```rust /// proof fn example() { /// let s = seq![1, 2, 3]; /// let pred = |x| x > 5; /// lemma_all_neg_filter_empty(s, pred); /// assert(s.filter(pred).len() == 0); /// } /// ``` pub proof fn lemma_all_neg_filter_empty(self, pred: spec_fn(A) -> bool) requires self.all(|x: A| !pred(x)), ensures self.filter(pred).len() == 0, decreases self.len(), { broadcast use group_seq_properties; reveal(Seq::filter); if self.len() != 0 { let rest = self.drop_last(); rest.lemma_all_neg_filter_empty(pred); rest.lemma_filter_len_push(pred, self.last()); let neg_pred = |x| !pred(x); assert(neg_pred(self.last())); } } /// Applies an Option-returning function to each element, keeping only successful (Some) results /// /// ## Example /// ```rust /// let s = seq![1, 2, 3]; /// let f = |x| if x % 2 == 0 { Some(x * 2) } else { None }; /// assert(s.filter_map(f) =~= seq![4]); /// ``` pub open spec fn filter_map(self, f: spec_fn(A) -> Option) -> Seq decreases self.len(), { // We're defining this by starting at the end of the list since it makes it // easier to reason about in the common case of looping over a vector in the // implementation. if self.len() == 0 { Seq::empty() } else { let rest = self.drop_last(); match f(self.last()) { Option::Some(s) => rest.filter_map(f) + seq![s], Option::None => rest.filter_map(f), } } } /// If an element exists in the filtered sequence, /// it must exist in the original sequence /// ``` pub broadcast proof fn lemma_filter_contains_rev(self, p: spec_fn(A) -> bool, elem: A) requires #[trigger] self.filter(p).contains(elem), ensures self.contains(elem), decreases self.len(), { broadcast use group_seq_properties; reveal(Seq::filter); if self.len() == 0 { } else { let rest = self.drop_last(); let last = self.last(); if !p(last) || last != elem { rest.lemma_filter_contains_rev(p, elem); } } } /// If an element exists in filter_map's output, /// there must be an input element that mapped to it /// ``` pub broadcast proof fn lemma_filter_map_contains(self, f: spec_fn(A) -> Option, elt: B) requires #[trigger] self.filter_map(f).contains(elt), ensures exists|t: A| #[trigger] self.contains(t) && f(t) == Some(elt), decreases self.len(), { broadcast use group_seq_properties; if self.len() == 0 { } else { let last = self.last(); let rest = self.drop_last(); if f(last) == Some(elt) { assert(self.contains(last)); } else { rest.lemma_filter_map_contains(f, elt); let t = choose|t: A| #[trigger] rest.contains(t) && f(t) == Some(elt); assert(self.contains(t)); } } } /// Taking k+1 elements is the same as taking k elements plus the kth element /// /// ## Example /// ```rust /// let s = seq![1, 2, 3]; /// lemma_take_plus_one(s, 1); /// seq![1, 2] == seq![1] + seq![2] /// ``` pub proof fn lemma_take_succ(xs: Seq, k: int) requires 0 <= k < xs.len(), ensures xs[..k + 1] =~= xs[..k] + seq![xs[k]], { broadcast use group_seq_properties; } /// filter_map on a single element sequence /// either produces a new single element sequence (if f returns Some) /// or an empty sequence (if f returns None) pub proof fn lemma_filter_map_singleton(a: A, f: spec_fn(A) -> Option) ensures seq![a].filter_map(f) =~= match f(a) { Option::Some(b) => seq![b], Option::None => Seq::empty(), }, { reveal_with_fuel(Seq::filter_map, 2); } /// filter_map of take(i+1) equals /// filter_map of take(i) plus maybe the mapped i'th element /// /// ## Example /// ```rust /// let s = seq![1, 2, 3]; /// let f = |x| if x % 2 == 0 { Some(x * 2) } else { None }; /// s.lemma_filter_map_take_succ(s, f, 1); /// assert(s[..2].filter_map(f) == s[..1].filter_map(f) + seq![f(s[1]).unwrap()]); /// assert(s[..2].filter_map(f) == seq![] + seq![4]); /// ``` pub broadcast proof fn lemma_filter_map_take_succ(self, f: spec_fn(A) -> Option, i: int) requires 0 <= i < self.len(), ensures #[trigger] self[..i + 1].filter_map(f) =~= self[..i].filter_map(f) + (match f(self[i]) { Option::Some(s) => seq![s], Option::None => Seq::empty(), }), decreases self.len(), { broadcast use group_seq_properties; if i != 0 { self.drop_last().lemma_filter_map_take_succ(f, i - 1); assert(self[..i + 1].drop_last() == self[..i]); } } /// An alternative implementation of filter that processes the sequence recursively from /// left to right, in contrast to the standard filter which processes from right to left. pub open spec fn filter_alt(self, p: spec_fn(A) -> bool) -> Seq { if self.len() == 0 { Seq::empty() } else { let rest = self.drop_first().filter(p); let first = self.first(); if p(first) { seq![first] + rest } else { rest } } } /// When filtering (x + sequence), if x satisfies the predicate, x is prepended to /// the filtered sequence. Otherwise, only the filtered sequence remains. /// /// ## Example /// ```rust /// proof fn filter_prepend_test() { /// let s = seq![2, 3, 4]; /// let is_even = |x: int| x % 2 == 0; /// let with_five = seq![5] + s; /// assert(with_five.filter(is_even) =~= seq![2, 4]); // 5 filtered out /// let with_six = seq![6] + s; /// assert(with_six.filter(is_even) =~= seq![6, 2, 4]); // 6 included /// } /// ``` pub broadcast proof fn lemma_filter_prepend(self, x: A, p: spec_fn(A) -> bool) ensures #[trigger] (seq![x] + self).filter(p) == (if p(x) { seq![x] } else { Seq::empty() }) + self.filter(p), decreases self.len(), { broadcast use group_seq_properties; reveal(Seq::filter); let lhs = (seq![x] + self).filter(p); let rhs = (if p(x) { seq![x] } else { Seq::empty() }) + self.filter(p); if self.len() == 0 { assert(lhs =~= rhs); } else { let tail_seq = if p(self.last()) { seq![self.last()] } else { Seq::empty() }; assert(((seq![x] + self).drop_last()) =~= seq![x] + self.drop_last()); let sub = (seq![x] + self.drop_last()).filter(p); assert(lhs =~= sub + tail_seq); assert(rhs =~= (if p(x) { seq![x] } else { Seq::empty() }) + self.drop_last().filter(p) + tail_seq); self.drop_last().lemma_filter_prepend(x, p); } } /// The filter() and filter_alt() methods produce equivalent results for any sequence pub proof fn lemma_filter_eq_filter_alt(self, p: spec_fn(A) -> bool) ensures self.filter(p) =~= self.filter_alt(p), decreases self.len(), { broadcast use group_seq_properties; broadcast use Seq::lemma_filter_prepend; reveal(Seq::filter); if self.len() == 0 { } else { let first = self.first(); let but_first = self.drop_first(); assert(self =~= seq![first] + but_first); self.drop_first().lemma_filter_eq_filter_alt(p); } } /// Filtering preserves the prefix relationship between sequences. /// /// ## Example /// ```rust /// proof fn filter_monotone_test() { /// let s = seq![1, 2, 3]; /// let ys = seq![1, 2, 3, 4, 5]; /// let is_even = |x: int| x % 2 == 0; /// assert(s.is_prefix_of(ys)); /// assert(s.filter(is_even).is_prefix_of(ys.filter(is_even))); /// assert(s.filter(is_even) =~= seq![2]); /// assert(ys.filter(is_even) =~= seq![2, 4]); /// } /// ``` pub proof fn lemma_filter_monotone(self, ys: Seq, p: spec_fn(A) -> bool) requires self.is_prefix_of(ys), ensures self.filter(p).is_prefix_of(ys.filter(p)), decreases self.len(), { broadcast use group_seq_properties; self.lemma_filter_eq_filter_alt(p); ys.lemma_filter_eq_filter_alt(p); if self.len() == 0 { } else { self.drop_first().lemma_filter_monotone(ys.drop_first(), p); } } /// The length of filter(take(i)) is never greater than the length of filter(entire_sequence). /// /// ## Example /// ```rust /// proof fn filter_take_len_test() { /// let s = seq![1, 2, 3, 4, 5]; /// let is_even = |x: int| x % 2 == 0; /// let i = 3; /// assert(s[..i] =~= seq![1, 2, 3]); /// assert(s[..i].filter(is_even) =~= seq![2]); /// assert(s.filter(is_even) =~= seq![2, 4]); /// assert(s.filter(is_even).len() >= s[..i].filter(is_even).len()); /// } /// ``` pub proof fn lemma_filter_take_len(self, p: spec_fn(A) -> bool, i: int) requires 0 <= i <= self.len(), ensures self.filter(p).len() >= self[..i].filter(p).len(), decreases i, { broadcast use group_seq_properties; broadcast use Seq::lemma_filter_len_push; broadcast use Seq::lemma_filter_push; self[..i].lemma_filter_monotone(self, p); } /// Filtering a prefix of a sequence produces the same number or fewer elements /// as filtering the entire sequence. /// /// ## Example /// ```rust /// proof fn filter_take_len_test() { /// let s = seq![1, 2, 3, 4, 5]; /// let is_even = |x: int| x % 2 == 0; /// assert(s.filter(is_even).len() >= s[..3].filter(is_even).len()); /// } /// ``` pub broadcast proof fn lemma_filter_len_push(self, p: spec_fn(A) -> bool, elem: A) ensures #[trigger] self.push(elem).filter(p).len() == self.filter(p).len() + (if p(elem) { 1int } else { 0int }), { broadcast use group_seq_properties; broadcast use Seq::lemma_filter_push; } /// If an index i is valid for a sequence (0 ≤ i < len), then the element at that index /// is contained in the sequence. pub broadcast proof fn lemma_index_contains(self, i: int) requires 0 <= i < self.len(), ensures self.contains(#[trigger] self[i]), { } /// Taking i+1 elements from a sequence is equivalent to taking i elements /// and then pushing the element at index i. pub broadcast proof fn lemma_take_succ_push(self, i: int) requires 0 <= i < self.len(), ensures #[trigger] self[..i + 1] =~= self[..i].push(self[i]), { broadcast use group_seq_properties; } /// Taking the full length of a sequence returns the sequence itself. pub broadcast proof fn lemma_take_len(self) ensures #[trigger] self[..self.len()] == self, { broadcast use group_seq_properties; } /// Taking i+1 elements and checking if any element satisfies predicate p is equivalent to: /// either taking i elements and checking if any satisfies p, OR checking if the i-th element satisfies p. /// /// ## Example /// ```rust /// proof fn take_any_succ_test() { /// let s = seq![1, 2, 3]; /// let is_even = |x| x % 2 == 0; /// let i = 1; /// assert(s[..i + 1].any(is_even) == (s[..i].any(is_even) || is_even(s[i]))); /// } /// ``` pub broadcast proof fn lemma_take_any_succ(self, p: spec_fn(A) -> bool, i: int) requires 0 <= i < self.len(), ensures #[trigger] self[..i + 1].any(p) <==> self[..i].any(p) || p(self[i]), { broadcast use group_seq_properties; self.lemma_take_succ_push(i); if self[..i + 1].any(p) { let x = choose|x: A| self[..i + 1].contains(x) && #[trigger] p(x); assert(self[..i].contains(x) || x == self[i]); } if self[..i].any(p) { let x = choose|x: A| self[..i].contains(x) && #[trigger] p(x); assert(self[..i + 1].contains(x)); } if p(self[i]) { assert(self[..i + 1].contains(self[i])); } } /// A sequence has no duplicates iff mapping an injective function over it /// produces a sequence with no duplicates. /// /// ## Example /// ```rust /// proof fn no_duplicates_injective_test() { /// let s = seq![1, 2]; /// let f = |x| x + 1; // injective function /// assert(s.no_duplicates() == s.map_values(f).no_duplicates()); /// } /// ``` pub proof fn lemma_no_duplicates_injective(self, f: spec_fn(A) -> B) requires injective(f), ensures self.no_duplicates() <==> self.map_values(f).no_duplicates(), { broadcast use group_seq_properties; broadcast use super::set_lib::group_set_properties; let mapped = self.map_values(f); assert(mapped.len() == self.len()); if mapped.no_duplicates() { assert forall|i: int, j: int| 0 <= i < j < mapped.len() implies self[i] != self[j] by { assert(mapped[i] == f(self[i])); assert(mapped[j] == f(self[j])); } } } /// Pushing an element and then mapping a function over a sequence is equivalent to /// mapping the function over the sequence and then pushing the function applied to that element. /// /// ## Example /// ```rust /// proof fn push_map_test() { /// let s = seq![1, 2]; /// let f = |x| x + 1; /// assert(s.push(3).map_values(f) =~= s.map_values(f).push(f(3))); /// } /// ``` pub broadcast proof fn lemma_push_map_commute(self, f: spec_fn(A) -> B, x: A) ensures self.map_values(f).push(f(x)) =~= #[trigger] self.push(x).map_values(f), decreases self.len(), { broadcast use group_seq_properties; } /// Converting a sequence to a set after pushing an element is equivalent to /// converting to a set first and then inserting that element. /// /// ## Example /// ```rust /// proof fn push_to_set_test() { /// let s = seq![1, 2]; /// assert(s.push(3).to_set() =~= s.to_set().insert(3)); /// } /// ``` pub broadcast proof fn lemma_push_to_set_commute(self, elem: A) ensures #[trigger] self.push(elem).to_set() =~= self.to_set().insert(elem), { broadcast use {group_seq_properties, super::set::group_set_lemmas, Seq::to_set_ensures}; let lhs = self.push(elem).to_set(); let rhs = self.to_set().insert(elem); assert forall|x: A| rhs.contains(x) implies lhs.contains(x) by { lemma_seq_contains_after_push(self, elem, x); } } /// Filtering a sequence after pushing an element is equivalent to: /// if the element satisfies the predicate, filter the sequence and push the element /// otherwise, just filter the sequence without the element. /// /// ## Example /// ```rust /// proof fn filter_push_test() { /// let s = seq![1, 2]; /// let is_even = |x| x % 2 == 0; /// assert(s.push(4).filter(is_even) == s.filter(is_even).push(4)); /// assert(s.push(3).filter(is_even) == s.filter(is_even)); /// } /// ``` pub broadcast proof fn lemma_filter_push(self, elem: A, pred: spec_fn(A) -> bool) ensures #[trigger] self.push(elem).filter(pred) == if pred(elem) { self.filter(pred).push(elem) } else { self.filter(pred) }, { broadcast use group_seq_properties; reveal(Seq::filter); assert(self.push(elem).drop_last() =~= self); } /// If two sequences have the same length and `i` is a valid index, /// then the pair `(a[i], b[i])` is contained in their zip. /// /// ## Example /// ```rust /// proof fn zip_contains_test() { /// let a = seq![1, 2]; /// let b = seq!["a", "b"]; /// assert(a.zip_with(b).contains((a[0], b[0]))); /// assert(a.zip_with(b).contains((a[1], b[1]))); /// } /// ``` pub proof fn lemma_zip_with_contains_index(self, b: Seq, i: int) requires 0 <= i < self.len(), self.len() == b.len(), ensures self.zip_with(b).contains((self[i], b[i])), { assert(self.zip_with(b)[i] == (self[i], b[i])); } /// Proves equivalence between checking a predicate over zipped sequences and checking /// corresponding elements by index. Requires sequences of equal length. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2]; /// let ys = seq![2, 3]; /// let f = |x, y| x < y; /// assert(xs.zip_with(ys).all(|(x, y)| f(x, y)) <==> /// forall|i| 0 <= i < xs.len() ==> f(xs[i], ys[i])); /// // We can now prove specific index relationships /// assert(xs[0] < ys[0]); // 1 < 2 /// assert(xs[1] < ys[1]); // 2 < 3 /// } /// ``` pub proof fn lemma_zip_with_uncurry_all(self, b: Seq, f: spec_fn(A, B) -> bool) requires self.len() == b.len(), ensures self.zip_with(b).all(|p: (A, B)| f(p.0, p.1)) <==> forall|i: int| 0 <= i < self.len() ==> f(self[i], b[i]), { broadcast use group_seq_properties; let zipped = self.zip_with(b); let f_uncurr = |p: (A, B)| f(p.0, p.1); let lhs = zipped.all(f_uncurr); let rhs = (forall|i: int| 0 <= i < self.len() ==> f(self[i], b[i])); if lhs { assert forall|i: int| 0 <= i < self.len() implies f(self[i], b[i]) by { self.lemma_zip_with_contains_index(b, i); assert(forall|j| 0 <= j < zipped.len() ==> f_uncurr(zipped[j])); } } } /// flat_mapping after pushing an element is the same as /// flat_mapping first and then appending f of that element. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2]; /// let f = |x| seq![x, x + 1]; /// assert(xs.push(3).flat_map(f) =~= xs.flat_map(f) + f(3)); /// // xs.push(3).flat_map(f) = [1,2,2,3,3,4] /// // xs.flat_map(f) + f(3) = [1,2,2,3] + [3,4] /// } /// ``` pub proof fn lemma_flat_map_push(self, f: spec_fn(A) -> Seq, elem: A) ensures self.push(elem).flat_map(f) =~= self.flat_map(f) + f(elem), decreases self.len(), { broadcast use group_seq_properties; broadcast use Seq::lemma_flatten_push; broadcast use Seq::lemma_push_map_commute; } /// flat_mapping a sequence up to index i+1 is equivalent to /// flat_mapping up to index i and appending f of the element at index i. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2, 3]; /// let f = |x| seq![x, x + 1]; /// /// assert(xs[..2].flat_map(f) =~= xs[..1].flat_map(f) + f(xs[1])); /// // xs[..2].flat_map(f) = [1,2,2,3] /// // xs[..1].flat_map(f) + f(2) = [1,2] + [2,3] /// } /// ``` pub broadcast proof fn lemma_flat_map_take_append(self, f: spec_fn(A) -> Seq, i: int) requires 0 <= i < self.len(), ensures #[trigger] self[..i + 1].flat_map(f) =~= self[..i].flat_map(f) + f(self[i]), decreases i, { broadcast use group_seq_properties; self.lemma_take_succ_push(i); self[..i].lemma_flat_map_push(f, self[i]); } /// flat_mapping a sequence with a single element /// is equivalent to applying the function f to that element. pub broadcast proof fn lemma_flat_map_singleton(self, f: spec_fn(A) -> Seq) requires #[trigger] self.len() == 1, ensures #[trigger] self.flat_map(f) == f(self[0]), { broadcast use Seq::lemma_flatten_singleton; } /// Mapping a sequence's first i+1 elements equals /// mapping its first i elements plus f of the i-th element. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2, 3]; /// let f = |x| x * 2; /// /// assert(xs[..2].map_values(f) =~= xs[..1].map_values(f).push(f(xs[1]))); /// // Left: [1,2].map(f) = [2,4] /// // Right: [1].map(f).push(f(2)) = [2].push(4) /// } /// ``` pub broadcast proof fn lemma_map_take_succ(self, f: spec_fn(A) -> B, i: int) requires 0 <= i < self.len(), ensures #[trigger] self[..i + 1].map_values(f) =~= self[..i].map_values(f).push(f(self[i])), { broadcast use group_seq_properties; self.lemma_take_succ_push(i); } /// If a sequence is a prefix of another sequence, /// their elements match at all indices within the prefix length. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2, 3]; /// let prefix = seq![1, 2]; /// assert(prefix.is_prefix_of(xs)); /// assert(prefix[0] == xs[0] && prefix[1] == xs[1]); /// } /// ``` pub broadcast proof fn lemma_prefix_index_eq(self, prefix: Seq) requires #[trigger] prefix.is_prefix_of(self), ensures forall|i: int| 0 <= i < prefix.len() ==> prefix[i] == self[i], { } /// If a concatenated sequence (prefix1 + prefix2) is a prefix of another sequence, /// then prefix1 by itself is also a prefix of that sequence. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2, 3, 4]; /// let prefix1 = seq![1, 2]; /// let prefix2 = seq![3]; /// assert((prefix1 + prefix2).is_prefix_of(xs)); /// assert(prefix1.is_prefix_of(xs)); /// } /// ``` pub broadcast proof fn lemma_prefix_concat(self, prefix1: Seq, prefix2: Seq) requires #[trigger] (prefix1 + prefix2).is_prefix_of(self), ensures prefix1.is_prefix_of(self), { broadcast use Seq::lemma_prefix_index_eq; } /// If `prefix1 + [t]` is a prefix of a sequence, /// `prefix1` is a prefix of `prefix2`, /// `prefix2` is a prefix of the sequence, /// `prefix1` and `prefix2` are different, and /// `prefix1` doesn't contain `t`, /// then `prefix2` must contain t. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![1, 2, 3, 4]; /// let prefix1 = seq![1]; /// let prefix2 = seq![1, 2]; /// let t = 2; /// assert((prefix1 + seq![t]).is_prefix_of(xs)); /// assert(prefix1.is_prefix_of(prefix2)); /// assert(prefix2.is_prefix_of(xs)); /// assert(prefix2.contains(t)); /// } /// ``` pub broadcast proof fn lemma_prefix_chain_contains(self, prefix1: Seq, prefix2: Seq, t: A) requires #[trigger] (prefix1 + seq![t]).is_prefix_of(self), #[trigger] prefix1.is_prefix_of(prefix2), prefix2.is_prefix_of(self), prefix1 != prefix2, !prefix1.contains(t), ensures prefix2.contains(t), { broadcast use Seq::lemma_prefix_concat; broadcast use Seq::lemma_prefix_index_eq; assert(prefix2[prefix1.len() as int] == t); } /// If `prefix1 + [t]` and `prefix2 + [t]` are both prefixes of a sequence, /// and neither `prefix1` nor `prefix2` contains `t`, /// then `prefix1` equals `prefix2`. pub broadcast proof fn lemma_prefix_append_unique(self, prefix1: Seq, prefix2: Seq, t: A) requires #[trigger] (prefix1 + seq![t]).is_prefix_of(self), #[trigger] (prefix2 + seq![t]).is_prefix_of(self), !prefix1.contains(t), !prefix2.contains(t), ensures prefix1 == prefix2, { broadcast use Seq::lemma_prefix_concat; broadcast use Seq::lemma_prefix_index_eq; broadcast use Seq::lemma_prefix_chain_contains; if prefix1 != prefix2 { assert(prefix1.is_prefix_of(prefix2) || prefix2.is_prefix_of(prefix1)); } } /// If a predicate `p` is true for all elements in a sequence, /// and `p` is true for an element `e`, then `p` remains true for all elements /// after pushing `e` to the sequence. /// /// # Example /// ```rust /// proof fn example() { /// let xs = seq![2, 4, 6]; /// let is_even = |x| x % 2 == 0; /// assert(xs.all(is_even)); /// assert(is_even(8)); /// assert(xs.push(8).all(is_even)); /// } /// ``` pub broadcast proof fn lemma_all_push(self, p: spec_fn(A) -> bool, elem: A) requires self.all(p), p(elem), ensures #[trigger] self.push(elem).all(p), { broadcast use group_seq_properties; assert forall|x: A| self.push(elem).contains(x) implies p(x) by { lemma_seq_contains_after_push(self, elem, x); } } /// Two sequences are equal when concatenated with the same prefix /// iff those two sequences are equal. pub proof fn lemma_concat_injective(self, s1: Seq, s2: Seq) ensures (self + s1 == self + s2) <==> (s1 == s2), { broadcast use group_seq_properties; assert((self + s1)[self.len()..] == s1); } pub broadcast group group_seq_extra { Seq::<_>::lemma_seq_skip_skip, Seq::<_>::lemma_remove_duplicates_properties, Seq::<_>::lemma_filter_contains_rev, Seq::<_>::lemma_filter_map_take_succ, Seq::<_>::lemma_filter_prepend, Seq::<_>::lemma_filter_len_push, Seq::<_>::lemma_take_len, Seq::<_>::lemma_take_any_succ, Seq::<_>::lemma_push_map_commute, Seq::<_>::lemma_push_to_set_commute, Seq::<_>::lemma_filter_push, Seq::<_>::lemma_flat_map_take_append, Seq::<_>::lemma_flat_map_singleton, Seq::<_>::lemma_map_take_succ, Seq::<_>::lemma_prefix_index_eq, Seq::<_>::lemma_prefix_concat, Seq::<_>::lemma_prefix_chain_contains, Seq::<_>::lemma_prefix_append_unique, Seq::<_>::lemma_all_push, } } impl Seq<&A> { /// Dereference each element of the sequence pub open spec fn unref(self) -> Seq { Seq::new(self.len(), |i: int| *self[i]) } } impl Seq<(&A, &B)> { /// Dereference each element of each tuple in the sequence pub open spec fn unref(self) -> Seq<(A, B)> { Seq::new(self.len(), |i: int| (*self[i].0, *self[i].1)) } } /// Filtering a sequence and then viewing its elements produces the same result as /// viewing the elements first and then filtering with the corresponding predicate. /// The predicates p and sp must be equivalent under view. /// /// # Example /// ```rust /// proof fn example() { /// let s = seq!["hello".to_string(), "world".to_string()]; /// let p = |x: String| x.len() > 4; /// let sp = |x: Seq| x.len() > 4; /// /// let way1 = s.filter(p).map_values(|x| x.view()); /// let way2 = s.map_values(|x| x.view()).filter(sp); /// assert(way1 == way2); /// } /// ``` pub proof fn lemma_filter_view_commute( s: Seq, p: spec_fn(S) -> bool, sp: spec_fn(S::V) -> bool, ) requires forall|s: S| p(s) <==> sp(s.view()), ensures s.filter(p).map_values(|x: S| x.view()) == s.map_values(|x: S| x.view()).filter(sp), decreases s.len(), { broadcast use group_seq_properties; broadcast use Seq::lemma_push_map_commute; broadcast use Seq::lemma_filter_push; reveal(Seq::filter); let view = |x: S| x.view(); if s.len() > 0 { let rest = s.drop_last(); let last = s.last(); assert(s =~= rest.push(last)); assert(s.map_values(view).last() == view(last)); lemma_filter_view_commute(rest, p, sp); } } /// A sequence has exactly one element satisfying a predicate iff /// viewing all elements and filtering with the corresponding predicate /// produces a sequence with exactly one element. /// /// # Example /// ```rust /// proof fn example() { /// let s = seq!["hello".to_string(), "world".to_string()]; /// let p = |x: String| x.len() == 5; /// let sp = |x: Seq| x.len() == 5; /// /// assert(s.exactly_one(p) <==> s.map_values(|x| x.view()).exactly_one(sp)); /// } /// ``` pub proof fn lemma_exactly_one_view( s: Seq, p: spec_fn(S) -> bool, sp: spec_fn(S::V) -> bool, ) requires forall|s: S| p(s) <==> sp(s.view()), injective(|x: S| x.view()), ensures s.exactly_one(p) <==> s.map_values(|x: S| x.view()).exactly_one(sp), { lemma_filter_view_commute(s, p, sp); } impl Seq<(A, B)> { /// Unzips a sequence that contains pairs into two separate sequences. pub closed spec fn unzip(self) -> (Seq, Seq) { (Seq::new(self.len(), |i: int| self[i].0), Seq::new(self.len(), |i: int| self[i].1)) } /// Proof of correctness and expected properties of unzip function pub proof fn unzip_ensures(self) ensures self.unzip().0.len() == self.unzip().1.len(), self.unzip().0.len() == self.len(), self.unzip().1.len() == self.len(), forall|i: int| 0 <= i < self.len() ==> (#[trigger] self.unzip().0[i], #[trigger] self.unzip().1[i]) == self[i], decreases self.len(), { if self.len() > 0 { self.drop_last().unzip_ensures(); } } /// Unzipping a sequence of sequences and then zipping the resulting two sequences /// back together results in the original sequence of sequences. pub proof fn lemma_zip_of_unzip(self) ensures self.unzip().0.zip_with(self.unzip().1) =~= self, { } } impl Seq> { /// Flattens a sequence of sequences into a single sequence by concatenating /// subsequences, starting from the first element. /// /// ## Example /// /// ```rust /// proof fn flatten_test() { /// let seq: Seq> = seq![seq![1, 2, 3], seq![4, 5, 6], seq![7, 8, 9]]; /// let flat: Seq = seq.flatten(); /// reveal_with_fuel(Seq::>::flatten, 5); //Needed for Verus to unfold the recursive definition of flatten /// assert(flat =~= seq![1, 2, 3, 4, 5, 6, 7, 8, 9]); /// } /// ``` pub open spec fn flatten(self) -> Seq decreases self.len(), { if self.len() == 0 { Seq::empty() } else { self.first().add(self.drop_first().flatten()) } } /// Flattens a sequence of sequences into a single sequence by concatenating /// subsequences in reverse order, i.e. starting from the last element. /// This is equivalent to a call to `flatten`, but with concatenation operation /// applied along the oppositive associativity for the sake of proof reasoning in that direction. pub open spec fn flatten_alt(self) -> Seq decreases self.len(), { if self.len() == 0 { Seq::empty() } else { self.drop_last().flatten_alt().add(self.last()) } } /// Flattening a sequence of a sequence x, where x has length 1, /// results in a sequence equivalent to the single element of x pub proof fn lemma_flatten_one_element(self) ensures self.len() == 1 ==> self.flatten() == self.first(), { broadcast use Seq::add_empty_right; if self.len() == 1 { assert(self.flatten() =~= self.first().add(self.drop_first().flatten())); } } /// The length of a flattened sequence of sequences x is greater than or /// equal to any of the lengths of the elements of x. pub proof fn lemma_flatten_length_ge_single_element_length(self, i: int) requires 0 <= i < self.len(), ensures self.flatten_alt().len() >= self[i].len(), decreases self.len(), { if self.len() == 1 { self.lemma_flatten_one_element(); self.lemma_flatten_and_flatten_alt_are_equivalent(); } else if i < self.len() - 1 { self.drop_last().lemma_flatten_length_ge_single_element_length(i); } else { assert(self.flatten_alt() == self.drop_last().flatten_alt().add(self.last())); } } /// The length of a flattened sequence of sequences x is less than or equal /// to the length of x multiplied by a number greater than or equal to the /// length of the longest sequence in x. pub proof fn lemma_flatten_length_le_mul(self, j: int) requires forall|i: int| 0 <= i < self.len() ==> (#[trigger] self[i]).len() <= j, ensures self.flatten_alt().len() <= self.len() * j, decreases self.len(), { broadcast use group_seq_properties; if self.len() == 0 { } else { self.drop_last().lemma_flatten_length_le_mul(j); assert((self.len() - 1) * j == (self.len() * j) - (1 * j)) by (nonlinear_arith); //TODO: use math library after imported } } /// Flattening sequences of sequences in order (starting from the beginning) /// and in reverse order (starting from the end) results in the same sequence. pub proof fn lemma_flatten_and_flatten_alt_are_equivalent(self) ensures self.flatten() =~= self.flatten_alt(), decreases self.len(), { broadcast use {Seq::add_empty_right, Seq::push_distributes_over_add}; if self.len() != 0 { self.drop_last().lemma_flatten_and_flatten_alt_are_equivalent(); // let s = self.drop_last().flatten(); // let s2 = self.drop_last().flatten_alt(); // assert(s == s2); seq![self.last()].lemma_flatten_one_element(); assert(seq![self.last()].flatten() == self.last()); lemma_flatten_concat(self.drop_last(), seq![self.last()]); assert((self.drop_last() + seq![self.last()]).flatten() == self.drop_last().flatten() + self.last()); assert(self.drop_last() + seq![self.last()] =~= self); assert(self.flatten_alt() == self.drop_last().flatten_alt() + self.last()); } } /// Flattening a sequence of sequences after pushing a new sequence is equivalent to /// concatenating that sequence to the original flattened result. pub broadcast proof fn lemma_flatten_push(self, elem: Seq) ensures #[trigger] self.push(elem).flatten() =~= self.flatten() + elem, decreases self.len(), { broadcast use group_seq_properties; assert(self.push(elem).last() == elem); assert(self.push(elem).drop_last() =~= self); calc! { (==) self.push(elem).flatten(); { self.push(elem).lemma_flatten_and_flatten_alt_are_equivalent(); } self.push(elem).flatten_alt(); {} self.flatten_alt() + elem; { self.lemma_flatten_and_flatten_alt_are_equivalent(); } self.flatten() + elem; } } /// Flattening a sequence containing a single sequence yields that inner sequence. pub broadcast proof fn lemma_flatten_singleton(self) requires #[trigger] self.len() == 1, ensures #[trigger] self.flatten() == self[0], { assert(self.flatten() == self[0] + self.drop_first().flatten()); assert(self.flatten() == self[0]); } pub broadcast group group_seq_flatten { Seq::<_>::lemma_flatten_push, Seq::<_>::lemma_flatten_singleton, } } /********************************* Extrema in Sequences *********************************/ impl Seq { /// Returns the maximum integer value in a non-empty sequence of integers. pub open spec fn max(self) -> int recommends 0 < self.len(), decreases self.len(), { if self.len() == 1 { self[0] } else if self.len() == 0 { 0 } else { let later_max = self.drop_first().max(); if self[0] >= later_max { self[0] } else { later_max } } } /// Proof of correctness and expected properties for max function pub proof fn max_ensures(self) ensures forall|x: int| self.contains(x) ==> x <= self.max(), forall|i: int| 0 <= i < self.len() ==> self[i] <= self.max(), self.len() == 0 || self.contains(self.max()), decreases self.len(), { if self.len() <= 1 { } else { let elt = self.drop_first().max(); assert(self.drop_first().contains(elt)) by { self.drop_first().max_ensures() } assert forall|i: int| 0 <= i < self.len() implies self[i] <= self.max() by { assert(i == 0 || self[i] == self.drop_first()[i - 1]); assert(forall|j: int| 0 <= j < self.drop_first().len() ==> self.drop_first()[j] <= self.drop_first().max()) by { self.drop_first().max_ensures() } } } } /// Returns the minimum integer value in a non-empty sequence of integers. pub open spec fn min(self) -> int recommends 0 < self.len(), decreases self.len(), { if self.len() == 1 { self[0] } else if self.len() == 0 { 0 } else { let later_min = self.drop_first().min(); if self[0] <= later_min { self[0] } else { later_min } } } /// Proof of correctness and expected properties for min function pub proof fn min_ensures(self) ensures forall|x: int| self.contains(x) ==> self.min() <= x, forall|i: int| 0 <= i < self.len() ==> self.min() <= self[i], self.len() == 0 || self.contains(self.min()), decreases self.len(), { if self.len() <= 1 { } else { let elt = self.drop_first().min(); assert(self[1..].contains(elt)) by { self.drop_first().min_ensures() } assert forall|i: int| 0 <= i < self.len() implies self.min() <= self[i] by { assert(i == 0 || self[i] == self.drop_first()[i - 1]); assert(forall|j: int| 0 <= j < self.drop_first().len() ==> self.drop_first().min() <= self.drop_first()[j]) by { self.drop_first().min_ensures() } } } } pub closed spec fn sort(self) -> Self { self.sort_by(|x: int, y: int| x <= y) } pub proof fn lemma_sort_ensures(self) ensures self.to_multiset() =~= self.sort().to_multiset(), sorted_by(self.sort(), |x: int, y: int| x <= y), { self.lemma_sort_by_ensures(|x: int, y: int| x <= y); } /// The maximum element in a non-empty sequence is greater than or equal to /// the maxima of its non-empty subsequences. pub proof fn lemma_subrange_max(self, from: int, to: int) requires 0 <= from < to <= self.len(), ensures self[from..to].max() <= self.max(), { self.max_ensures(); self[from..to].max_ensures(); } /// The minimum element in a non-empty sequence is less than or equal to /// the minima of its non-empty subsequences. pub proof fn lemma_subrange_min(self, from: int, to: int) requires 0 <= from < to <= self.len(), ensures self[from..to].min() >= self.min(), { self.min_ensures(); self[from..to].min_ensures(); } } // Helper function to aid with merge sort spec fn merge_sorted_with(left: Seq, right: Seq, leq: spec_fn(A, A) -> bool) -> Seq recommends sorted_by(left, leq), sorted_by(right, leq), total_ordering(leq), decreases left.len(), right.len(), { if left.len() == 0 { right } else if right.len() == 0 { left } else if leq(left.first(), right.first()) { Seq::::empty().push(left.first()) + merge_sorted_with(left.drop_first(), right, leq) } else { Seq::::empty().push(right.first()) + merge_sorted_with(left, right.drop_first(), leq) } } proof fn lemma_merge_sorted_with_ensures(left: Seq, right: Seq, leq: spec_fn(A, A) -> bool) requires sorted_by(left, leq), sorted_by(right, leq), total_ordering(leq), ensures (left + right).to_multiset() =~= merge_sorted_with(left, right, leq).to_multiset(), sorted_by(merge_sorted_with(left, right, leq), leq), decreases left.len(), right.len(), { // TODO: lemma_seq_skip_of_skip and lemma_seq_skip_index2 cause a lot of QIs broadcast use group_seq_properties; if left.len() == 0 { assert(left + right =~= right); } else if right.len() == 0 { assert(left + right =~= left); } else if leq(left.first(), right.first()) { let result = Seq::::empty().push(left.first()) + merge_sorted_with( left.drop_first(), right, leq, ); lemma_merge_sorted_with_ensures(left.drop_first(), right, leq); let rest = merge_sorted_with(left.drop_first(), right, leq); assert(rest.len() == 0 || rest.first() == left.drop_first().first() || rest.first() == right.first()) by { if left.drop_first().len() == 0 { } else if leq(left.drop_first().first(), right.first()) { assert(rest =~= Seq::::empty().push(left.drop_first().first()) + merge_sorted_with(left.drop_first().drop_first(), right, leq)); } else { assert(rest =~= Seq::::empty().push(right.first()) + merge_sorted_with( left.drop_first(), right.drop_first(), leq, )); } } lemma_new_first_element_still_sorted_by(left.first(), rest, leq); assert((left.drop_first() + right) =~= (left + right).drop_first()); } else { let result = Seq::::empty().push(right.first()) + merge_sorted_with( left, right.drop_first(), leq, ); lemma_merge_sorted_with_ensures(left, right.drop_first(), leq); let rest = merge_sorted_with(left, right.drop_first(), leq); assert(rest.len() == 0 || rest.first() == left.first() || rest.first() == right.drop_first().first()) by { assert(left.len() > 0); if right.drop_first().len() == 0 { /*assert(rest =~= left);*/ } else if leq(left.first(), right.drop_first().first()) { //right might be length 1 assert(rest =~= Seq::::empty().push(left.first()) + merge_sorted_with( left.drop_first(), right.drop_first(), leq, )); } else { assert(rest =~= Seq::::empty().push(right.drop_first().first()) + merge_sorted_with(left, right.drop_first().drop_first(), leq)); } } lemma_new_first_element_still_sorted_by( right.first(), merge_sorted_with(left, right.drop_first(), leq), leq, ); lemma_seq_union_to_multiset_commutative(left, right); assert((right.drop_first() + left) =~= (right + left).drop_first()); lemma_seq_union_to_multiset_commutative(right.drop_first(), left); } } /// The maximum of the concatenation of two non-empty sequences is greater than or /// equal to the maxima of its two non-empty subsequences. pub proof fn lemma_max_of_concat(x: Seq, y: Seq) requires 0 < x.len() && 0 < y.len(), ensures x.max() <= (x + y).max(), y.max() <= (x + y).max(), forall|elt: int| (x + y).contains(elt) ==> elt <= (x + y).max(), decreases x.len(), { broadcast use group_seq_properties; x.max_ensures(); y.max_ensures(); (x + y).max_ensures(); assert(x.drop_first().len() == x.len() - 1); if x.len() == 1 { assert(y.max() <= (x + y).max()) by { assert((x + y).contains(y.max())); } } else { assert(x.max() <= (x + y).max()) by { assert(x.contains(x.max())); assert((x + y).contains(x.max())); } assert(x.drop_first() + y =~= (x + y).drop_first()); lemma_max_of_concat(x.drop_first(), y); } } /// The minimum of the concatenation of two non-empty sequences is less than or /// equal to the minimum of its two non-empty subsequences. pub proof fn lemma_min_of_concat(x: Seq, y: Seq) requires 0 < x.len() && 0 < y.len(), ensures (x + y).min() <= x.min(), (x + y).min() <= y.min(), forall|elt: int| (x + y).contains(elt) ==> (x + y).min() <= elt, decreases x.len(), { x.min_ensures(); y.min_ensures(); (x + y).min_ensures(); broadcast use group_seq_properties; if x.len() == 1 { assert((x + y).min() <= y.min()) by { assert((x + y).contains(y.min())); } } else { assert((x + y).min() <= x.min()) by { assert((x + y).contains(x.min())); } assert((x + y).min() <= y.min()) by { assert((x + y).contains(y.min())); } assert(x.drop_first() + y =~= (x + y).drop_first()); lemma_max_of_concat(x.drop_first(), y) } } /************************* Sequence to Multiset Conversion **************************/ /// push(a) o to_multiset = to_multiset o insert(a) pub broadcast proof fn to_multiset_build(s: Seq, a: A) ensures #![trigger s.push(a).to_multiset()] s.push(a).to_multiset() =~= s.to_multiset().insert(a), decreases s.len(), { broadcast use super::multiset::group_multiset_axioms; if s.len() == 0 { assert(s.to_multiset() =~= Multiset::::empty()); assert(s.push(a).drop_first() =~= Seq::::empty()); assert(s.push(a).to_multiset() =~= Multiset::::empty().insert(a).add( Seq::::empty().to_multiset(), )); } else { to_multiset_build(s.drop_first(), a); assert(s.drop_first().push(a).to_multiset() =~= s.drop_first().to_multiset().insert(a)); assert(s.push(a).drop_first() =~= s.drop_first().push(a)); } } pub broadcast proof fn to_multiset_remove(s: Seq, i: int) requires 0 <= i < s.len(), ensures #![trigger s.remove(i).to_multiset()] s.remove(i).to_multiset() == s.to_multiset().remove(s[i]), { broadcast use super::multiset::group_multiset_axioms; let s0 = s[..i]; let s1 = s[i..]; let s2 = s[i + 1..]; lemma_seq_union_to_multiset_commutative(s0, s2); lemma_seq_union_to_multiset_commutative(s0, s1); assert(s == s0 + s1); assert(s2 + s0 == (s1 + s0).drop_first()); assert(s.remove(i).to_multiset() =~= s.to_multiset().remove(s[i])); } pub broadcast proof fn to_multiset_insert(s: Seq, i: int, a: A) requires 0 <= i <= s.len(), ensures #![trigger s.insert(i, a).to_multiset()] s.insert(i, a).to_multiset() == s.to_multiset().insert(a), decreases s.len(), { broadcast use super::multiset::group_multiset_axioms; let s0 = s[..i]; let s1 = s[i..]; assert(s =~= s0 + s1); assert(s.insert(i, a) =~= s0 + seq![a] + s1); assert(((s0 + seq![a]) + s1).to_multiset() =~= ((seq![a] + s0) + s1).to_multiset()) by { broadcast use lemma_multiset_commutative; }; assert((seq![a] + s0 + s1).drop_first() == s0 + s1); assert(s.insert(i, a).to_multiset() =~= s.to_multiset().insert(a)); } /// to_multiset() preserves length pub broadcast proof fn to_multiset_len(s: Seq) ensures s.len() == #[trigger] s.to_multiset().len(), decreases s.len(), { broadcast use super::multiset::group_multiset_axioms; if s.len() == 0 { assert(s.to_multiset() =~= Multiset::::empty()); assert(s.len() == 0); } else { to_multiset_len(s.drop_first()); assert(s.len() == s.drop_first().len() + 1); assert(s.to_multiset().len() == s.drop_first().to_multiset().len() + 1); } } /// to_multiset() contains only the elements of the sequence pub broadcast proof fn to_multiset_contains(s: Seq, a: A) ensures #![trigger s.to_multiset().count(a)] s.contains(a) <==> s.to_multiset().count(a) > 0, decreases s.len(), { broadcast use super::multiset::group_multiset_axioms; if s.len() != 0 { // ==> if s.contains(a) { if s.first() == a { to_multiset_build(s, a); assert(s.to_multiset() =~= Multiset::::empty().insert(s.first()).add( s.drop_first().to_multiset(), )); assert(Multiset::::empty().insert(s.first()).contains(s.first())); } else { to_multiset_contains(s.drop_first(), a); assert(s[1..] =~= s.drop_first()); lemma_seq_skip_contains(s, 1, a); assert(s.to_multiset().count(a) == s.drop_first().to_multiset().count(a)); assert(s.contains(a) <==> s.to_multiset().count(a) > 0); } } // <== if s.to_multiset().count(a) > 0 { to_multiset_contains(s.drop_first(), a); assert(s.contains(a) <==> s.to_multiset().count(a) > 0); } else { assert(s.contains(a) <==> s.to_multiset().count(a) > 0); } } } pub broadcast proof fn to_multiset_update(s: Seq, i: int, a: A) requires 0 <= i < s.len(), ensures #[trigger] s.update(i, a).to_multiset() == s.to_multiset().insert(a).remove(s[i]), decreases s.len(), { broadcast use { super::seq_lib::lemma_seq_take_len, super::multiset::group_multiset_properties, super::multiset::group_multiset_axioms, to_multiset_insert, to_multiset_remove, to_multiset_contains, lemma_update_is_remove_insert, }; assert(s.update(i, a).to_multiset() =~= s.to_multiset().insert(a).remove(s[i])); } /// Lemma showing that update is equivalent to a remove followed by an insertae pub broadcast proof fn lemma_update_is_remove_insert(s: Seq, i: int, a: A) requires 0 <= i < s.len(), ensures #[trigger] s.update(i, a) =~= s.remove(i).insert(i, a), decreases s.len(), { } /// The last element of two concatenated sequences, the second one being non-empty, will be the /// last element of the latter sequence. pub proof fn lemma_append_last(s1: Seq, s2: Seq) requires 0 < s2.len(), ensures (s1 + s2).last() == s2.last(), { } /// The concatenation of sequences is associative pub proof fn lemma_concat_associative(s1: Seq, s2: Seq, s3: Seq) ensures s1.add(s2.add(s3)) =~= s1.add(s2).add(s3), { } /// Recursive definition of seq to set conversion spec fn seq_to_set_rec(seq: Seq) -> Set decreases seq.len(), { if seq.len() == 0 { Set::empty() } else { seq_to_set_rec(seq.drop_last()).insert(seq.last()) } } // Helper function showing that the resulting set contains all elements of the sequence proof fn seq_to_set_rec_contains(seq: Seq) ensures forall|a| #[trigger] seq.contains(a) <==> seq_to_set_rec(seq).contains(a), decreases seq.len(), { broadcast use super::set::group_set_lemmas; if seq.len() > 0 { assert(forall|a| #[trigger] seq.drop_last().contains(a) <==> seq_to_set_rec(seq.drop_last()).contains(a)) by { seq_to_set_rec_contains(seq.drop_last()); } assert(seq =~= seq.drop_last().push(seq.last())); assert forall|a| #[trigger] seq.contains(a) <==> seq_to_set_rec(seq).contains(a) by { if !seq.drop_last().contains(a) { if a == seq.last() { assert(seq.contains(a)); assert(seq_to_set_rec(seq).contains(a)); } else { assert(!seq_to_set_rec(seq).contains(a)); } } } } } // Helper function showing that the recursive definition matches the set comprehension one proof fn seq_to_set_equal_rec(seq: Seq) ensures seq.to_set() == seq_to_set_rec(seq), decreases seq.len(), { broadcast use super::set::group_set_lemmas; seq.to_set_ensures(); assert(forall|n| seq.contains(n) <==> #[trigger] seq_to_set_rec(seq).contains(n)) by { seq_to_set_rec_contains(seq); } assert(seq.to_set() =~= seq_to_set_rec(seq)); } pub proof fn seq_to_set_distributes_over_add(s1: Seq, s2: Seq) ensures s1.to_set() + s2.to_set() =~= (s1 + s2).to_set(), { broadcast use super::group_vstd_default; broadcast use super::set_lib::group_set_properties; broadcast use group_seq_properties; } /// If sequences a and b don't have duplicates, and there are no /// elements in common between them, then the concatenated sequence /// a + b will not contain duplicates either. pub proof fn lemma_no_dup_in_concat(a: Seq, b: Seq) requires a.no_duplicates(), b.no_duplicates(), forall|i: int, j: int| 0 <= i < a.len() && 0 <= j < b.len() ==> a[i] != b[j], ensures #[trigger] (a + b).no_duplicates(), { } /// Flattening sequences of sequences is distributive over concatenation. That is, concatenating /// the flattening of two sequences of sequences is the same as flattening the /// concatenation of two sequences of sequences. pub proof fn lemma_flatten_concat(x: Seq>, y: Seq>) ensures (x + y).flatten() =~= x.flatten() + y.flatten(), decreases x.len(), { if x.len() == 0 { assert(x + y =~= y); } else { assert((x + y).drop_first() =~= x.drop_first() + y); assert(x.first() + (x.drop_first() + y).flatten() =~= x.first() + x.drop_first().flatten() + y.flatten()) by { lemma_flatten_concat(x.drop_first(), y); } } } /// Flattening sequences of sequences in reverse order is distributive over concatentation. /// That is, concatenating the flattening of two sequences of sequences in reverse /// order is the same as flattening the concatenation of two sequences of sequences /// in reverse order. pub proof fn lemma_flatten_alt_concat(x: Seq>, y: Seq>) ensures (x + y).flatten_alt() =~= x.flatten_alt() + y.flatten_alt(), decreases y.len(), { if y.len() == 0 { assert(x + y =~= x); } else { assert((x + y).drop_last() =~= x + y.drop_last()); assert((x + y.drop_last()).flatten_alt() + y.last() =~= x.flatten_alt() + y.drop_last().flatten_alt() + y.last()) by { lemma_flatten_alt_concat(x, y.drop_last()); } } } /// The multiset of a concatenated sequence `a + b` is equivalent to the multiset of the /// concatenated sequence `b + a`. pub broadcast proof fn lemma_seq_union_to_multiset_commutative(a: Seq, b: Seq) ensures #[trigger] (a + b).to_multiset() =~= (b + a).to_multiset(), { broadcast use super::multiset::group_multiset_axioms; lemma_multiset_commutative(a, b); lemma_multiset_commutative(b, a); } /// The multiset of a concatenated sequence `a + b` is equivalent to the multiset of just /// sequence `a` added to the multiset of just sequence `b`. pub broadcast proof fn lemma_multiset_commutative(a: Seq, b: Seq) ensures #[trigger] (a + b).to_multiset() =~= a.to_multiset().add(b.to_multiset()), decreases a.len(), { broadcast use super::multiset::group_multiset_axioms; if a.len() == 0 { assert(a + b =~= b); } else { lemma_multiset_commutative(a.drop_first(), b); assert(a.drop_first() + b =~= (a + b).drop_first()); } } /// Any two sequences that are sorted by a total order and that have the same elements are equal. pub proof fn lemma_sorted_unique(x: Seq, y: Seq, leq: spec_fn(A, A) -> bool) requires sorted_by(x, leq), sorted_by(y, leq), total_ordering(leq), x.to_multiset() == y.to_multiset(), ensures x =~= y, decreases x.len(), y.len(), { broadcast use super::multiset::group_multiset_axioms; broadcast use group_to_multiset_ensures; if x.len() == 0 || y.len() == 0 { } else { assert(x.to_multiset().contains(x[0])); assert(x.to_multiset().contains(y[0])); let i = choose|i: int| #![trigger x.spec_index(i) ] 0 <= i < x.len() && x[i] == y[0]; assert(leq(x[i], x[0])); assert(leq(x[0], x[i])); assert(x.drop_first().to_multiset() =~= x.to_multiset().remove(x[0])); assert(y.drop_first().to_multiset() =~= y.to_multiset().remove(y[0])); lemma_sorted_unique(x.drop_first(), y.drop_first(), leq); assert(x.drop_first() =~= y.drop_first()); assert(x.first() == y.first()); assert(x =~= Seq::::empty().push(x.first()).add(x.drop_first())); assert(x =~= y); } } // This verified lemma used to be an axiom in the Dafny prelude pub broadcast proof fn lemma_seq_contains(s: Seq, x: A) ensures #[trigger] s.contains(x) <==> exists|i: int| 0 <= i < s.len() && #[trigger] s[i] == x, { } // This verified lemma used to be an axiom in the Dafny prelude /// The empty sequence contains nothing pub broadcast proof fn lemma_seq_empty_contains_nothing(x: A) ensures !(#[trigger] Seq::::empty().contains(x)), { } // This verified lemma used to be an axiom in the Dafny prelude // Note: Dafny only does one way implication, but theoretically it could go both ways /// A sequence with length 0 is equivalent to the empty sequence pub broadcast proof fn lemma_seq_empty_equality(s: Seq) ensures #[trigger] s.len() == 0 ==> s =~= Seq::::empty(), { } // This verified lemma used to be an axiom in the Dafny prelude /// The concatenation of two sequences contains only the elements /// of the two sequences pub broadcast proof fn lemma_seq_concat_contains_all_elements(x: Seq, y: Seq, elt: A) ensures #[trigger] (x + y).contains(elt) <==> x.contains(elt) || y.contains(elt), decreases x.len(), { if x.len() == 0 && y.len() > 0 { assert((x + y) =~= y); } else { assert forall|elt: A| #[trigger] x.contains(elt) implies #[trigger] (x + y).contains( elt, ) by { let index = choose|i: int| 0 <= i < x.len() && x[i] == elt; assert((x + y)[index] == elt); } assert forall|elt: A| #[trigger] y.contains(elt) implies #[trigger] (x + y).contains( elt, ) by { let index = choose|i: int| 0 <= i < y.len() && y[i] == elt; assert((x + y)[index + x.len()] == elt); } } } // This verified lemma used to be an axiom in the Dafny prelude /// After pushing an element onto a sequence, the sequence contains that element pub broadcast proof fn lemma_seq_contains_after_push(s: Seq, v: A, x: A) ensures #[trigger] s.push(v).contains(x) <==> v == x || s.contains(x), { assert forall|elt: A| #[trigger] s.contains(elt) implies #[trigger] s.push(v).contains(elt) by { let index = choose|i: int| 0 <= i < s.len() && s[i] == elt; assert(s.push(v)[index] == elt); } assert(s.push(v)[s.len() as int] == v); } // This verified lemma used to be an axiom in the Dafny prelude /// The subrange of a sequence contains only the elements within the indices `start` and `stop` /// of the original sequence. pub broadcast proof fn lemma_seq_subrange_elements(s: Seq, start: int, stop: int, x: A) requires 0 <= start <= stop <= s.len(), ensures #[trigger] s[start..stop].contains(x) <==> (exists|i: int| 0 <= start <= i < stop <= s.len() && #[trigger] s[i] == x), { assert((exists|i: int| 0 <= start <= i < stop <= s.len() && s[i] == x) ==> s[start..stop].contains(x)) by { if exists|i: int| 0 <= start <= i < stop <= s.len() && s[i] == x { let index = choose|i: int| 0 <= start <= i < stop <= s.len() && s[i] == x; assert(s[start..stop][index - start] == s[index]); } } } // Definition of a commutative fold_right operator. pub open spec fn commutative_foldr(f: spec_fn(A, B) -> B) -> bool { forall|x: A, y: A, v: B| #[trigger] f(x, f(y, v)) == f(y, f(x, v)) } // Definition of a commutative fold_left operator. pub open spec fn commutative_foldl(f: spec_fn(B, A) -> B) -> bool { forall|x: A, y: A, v: B| #[trigger] f(f(v, x), y) == f(f(v, y), x) } // For a commutative fold_right operator, any folding order // (i.e., any permutation) produces the same result. pub proof fn lemma_fold_right_permutation(l1: Seq, l2: Seq, f: spec_fn(A, B) -> B, v: B) requires commutative_foldr(f), l1.to_multiset() == l2.to_multiset(), ensures l1.fold_right(f, v) == l2.fold_right(f, v), decreases l1.len(), { broadcast use group_to_multiset_ensures; if l1.len() > 0 { let a = l1.last(); let i = l2.index_of(a); let l2r = l2[i + 1..].fold_right(f, v); assert(l1.to_multiset().count(a) > 0); l1.drop_last().lemma_fold_right_commute_one(a, f, v); l2[..i].lemma_fold_right_commute_one(a, f, l2r); l2.lemma_fold_right_split(f, v, i + 1); l2.remove(i).lemma_fold_right_split(f, v, i); assert(l2[..i + 1].drop_last() == l2[..i]); assert(l1.drop_last() == l1.remove(l1.len() - 1)); assert(l2.remove(i)[..i] == l2[..i]); assert(l2.remove(i)[i..l2.remove(i).len()] == l2[i + 1..l2.len()]); lemma_fold_right_permutation(l1.drop_last(), l2.remove(i), f, v); } else { assert(l2.to_multiset().len() == 0); } } // For a commutative fold_left operator, any folding order // (i.e., any permutation) produces the same result. pub proof fn lemma_fold_left_permutation(l1: Seq, l2: Seq, f: spec_fn(B, A) -> B, v: B) requires commutative_foldl(f), l1.to_multiset() == l2.to_multiset(), ensures l1.fold_left(v, f) == l2.fold_left(v, f), { let g = |a: A, b: B| f(b, a); assert(f =~= |b: B, a: A| g(a, b)); assert(l1.fold_left(v, f) == l1.reverse().fold_right(g, v)) by { l1.lemma_reverse_fold_right(v, g) }; assert(l2.fold_left(v, f) == l2.reverse().fold_right(g, v)) by { l2.lemma_reverse_fold_right(v, g) }; assert(l1.reverse().to_multiset() =~= l2.reverse().to_multiset()) by { l1.lemma_reverse_to_multiset(); l2.lemma_reverse_to_multiset(); } assert(forall|x: A| #[trigger] l1.reverse().contains(x) ==> l1.contains(x)); assert(forall|x: A| #[trigger] l2.reverse().contains(x) ==> l2.contains(x)); lemma_fold_right_permutation(l1.reverse(), l2.reverse(), g, v); } /************************** Lemmas about Take/Skip ***************************/ // This verified lemma used to be an axiom in the Dafny prelude /// Taking the first `n` elements of a sequence results in a sequence of length `n`, /// as long as `n` is within the bounds of the original sequence. pub broadcast proof fn lemma_seq_take_len(s: Seq, n: int) ensures 0 <= n <= s.len() ==> #[trigger] s[..n].len() == n, { } // This verified lemma used to be an axiom in the Dafny prelude /// The resulting sequence after taking the first `n` elements from sequence `s` contains /// element `x` if and only if `x` is contained in the first `n` elements of `s`. pub broadcast proof fn lemma_seq_take_contains(s: Seq, n: int, x: A) requires 0 <= n <= s.len(), ensures #[trigger] s[..n].contains(x) <==> (exists|i: int| 0 <= i < n <= s.len() && #[trigger] s[i] == x), { assert((exists|i: int| 0 <= i < n <= s.len() && #[trigger] s[i] == x) ==> s[..n].contains(x)) by { if exists|i: int| 0 <= i < n <= s.len() && #[trigger] s[i] == x { let index = choose|i: int| 0 <= i < n <= s.len() && #[trigger] s[i] == x; assert(s[..n][index] == s[index]); } } } // This verified lemma used to be an axiom in the Dafny prelude /// If `j` is a valid index less than `n`, then the `j`th element of the sequence `s` /// is the same as `j`th element of the sequence after taking the first `n` elements of `s`. pub broadcast proof fn lemma_seq_take_index(s: Seq, n: int, j: int) ensures 0 <= j < n <= s.len() ==> #[trigger] s[..n][j] == s[j], { } pub proof fn subrange_of_matching_take(a: Seq, b: Seq, s: int, e: int, l: int) requires a[..l] == b[..l], l <= a.len(), l <= b.len(), 0 <= s <= e <= l, ensures a[s..e] == b[s..e], { assert forall|i| 0 <= i < e - s implies #[trigger] a[s..e][i] == b[s..e][i] by { assert(a[s..e][i] == a[..l][i + s]); // assert( b[s..e][i] == b[..l][i + s] ); // either trigger will do } // trigger extn equality (verus issue #1257) assert(a[s..e] == b[s..e]); } // This verified lemma used to be an axiom in the Dafny prelude /// Skipping the first `n` elements of a sequence gives a sequence of length `n` less than /// the original sequence's length. pub broadcast proof fn lemma_seq_skip_len(s: Seq, n: int) ensures 0 <= n <= s.len() ==> #[trigger] s[n..].len() == s.len() - n, { } // This verified lemma used to be an axiom in the Dafny prelude /// The resulting sequence after skipping the first `n` elements from sequence `s` contains /// element `x` if and only if `x` is contained in `s` before index `n`. pub broadcast proof fn lemma_seq_skip_contains(s: Seq, n: int, x: A) requires 0 <= n <= s.len(), ensures #[trigger] s[n..].contains(x) <==> (exists|i: int| 0 <= n <= i < s.len() && #[trigger] s[i] == x), { assert((exists|i: int| 0 <= n <= i < s.len() && #[trigger] s[i] == x) ==> s[n..].contains(x)) by { let index = choose|i: int| 0 <= n <= i < s.len() && #[trigger] s[i] == x; lemma_seq_skip_index(s, n, index - n); } } // This verified lemma used to be an axiom in the Dafny prelude /// If `j` is a valid index less than `s.len() - n`, then the `j`th element of the sequence /// `s[n..]` is the same as the `j+n`th element of the sequence `s`. pub broadcast proof fn lemma_seq_skip_index(s: Seq, n: int, j: int) ensures 0 <= n && 0 <= j < (s.len() - n) ==> #[trigger] s[n..][j] == s[j + n], { } // This verified lemma used to be an axiom in the Dafny prelude /// If `k` is a valid index between `n` (inclusive) and the length of sequence `s` (exclusive), /// then the `k-n`th element of the sequence `s[n..]` is the same as the `k`th element of the /// original sequence `s`. pub broadcast proof fn lemma_seq_skip_index2(s: Seq, n: int, k: int) ensures 0 <= n <= k < s.len() ==> (#[trigger] s[n..])[k - n] == #[trigger] s[k], { } // This verified lemma used to be an axiom in the Dafny prelude /// If `n` is the length of sequence `a`, then taking the first `n` elements of the concatenation /// `a + b` is equivalent to the sequence `a` and skipping the first `n` elements of the concatenation /// `a + b` is equivalent to the sequence `b`. pub broadcast proof fn lemma_seq_append_take_skip(a: Seq, b: Seq, n: int) ensures #![trigger (a + b)[..n]] #![trigger (a + b)[n..]] // #![trigger (a + b)[..n]] // #![trigger (a + b)[n..]] n == a.len() ==> ((a + b)[..n] =~= a && (a + b)[n..] =~= b), { } /************* Lemmas about the Commutability of Take and Skip with Update ************/ // This verified lemma used to be an axiom in the Dafny prelude /// If `i` is in the first `n` indices of sequence `s`, updating sequence `s` at index `i` with /// value `v` and then taking the first `n` elements is equivalent to first taking the first `n` /// elements of `s` and then updating index `i` to value `v`. pub broadcast proof fn lemma_seq_take_update_commut1(s: Seq, i: int, v: A, n: int) ensures #![trigger s.update(i, v)[..n]] 0 <= i < n <= s.len() ==> #[trigger] s.update(i, v)[..n] =~= s[..n].update(i, v), { } // This verified lemma used to be an axiom in the Dafny prelude /// If `i` is a valid index after the first `n` indices of sequence `s`, updating sequence `s` at /// index `i` with value `v` and then taking the first `n` elements is equivalent to just taking the first `n` /// elements of `s` without the update. pub broadcast proof fn lemma_seq_take_update_commut2(s: Seq, i: int, v: A, n: int) ensures 0 <= n <= i < s.len() ==> #[trigger] s.update(i, v)[..n] =~= s[..n], { } // This verified lemma used to be an axiom in the Dafny prelude /// If `i` is a valid index after the first `n` indices of sequence `s`, updating sequence `s` at /// index `i` with value `v` and then skipping the first `n` elements is equivalent to skipping the first `n` /// elements of `s` and then updating index `i-n` to value `v`. pub broadcast proof fn lemma_seq_skip_update_commut1(s: Seq, i: int, v: A, n: int) ensures 0 <= n <= i < s.len() ==> #[trigger] s.update(i, v)[n..] =~= s[n..].update(i - n, v), { } // This verified lemma used to be an axiom in the Dafny prelude /// If `i` is a valid index in the first `n` indices of sequence `s`, updating sequence `s` at /// index `i` with value `v` and then skipping the first `n` elements is equivalent to just skipping /// the first `n` elements without the update. pub broadcast proof fn lemma_seq_skip_update_commut2(s: Seq, i: int, v: A, n: int) ensures 0 <= i < n <= s.len() ==> #[trigger] s.update(i, v)[n..] =~= s[n..], { } // This verified lemma used to be an axiom in the Dafny prelude /// Pushing element `v` onto the end of sequence `s` and then skipping the first `n` elements is /// equivalent to skipping the first `n` elements of `s` and then pushing `v` onto the end. pub broadcast proof fn lemma_seq_skip_build_commut(s: Seq, v: A, n: int) ensures #![trigger s.push(v)[n..]] 0 <= n <= s.len() ==> s.push(v)[n..] =~= s[n..].push(v), { } // This verified lemma used to be an axiom in the Dafny prelude /// `s[0..]` is equivalent to `s`. pub broadcast proof fn lemma_seq_skip_nothing(s: Seq, n: int) ensures n == 0 ==> #[trigger] s[n..] =~= s, { } // This verified lemma used to be an axiom in the Dafny prelude /// `s[..0]` is equivalent to the empty sequence. pub broadcast proof fn lemma_seq_take_nothing(s: Seq, n: int) ensures n == 0 ==> #[trigger] s[..n] =~= Seq::::empty(), { } // This verified lemma used to be an axiom in the Dafny prelude /// If `m + n` is less than or equal to the length of sequence `s`, then skipping the first `m` elements /// and then skipping the first `n` elements of the resulting sequence is equivalent to just skipping /// the first `m + n` elements. pub broadcast proof fn lemma_seq_skip_of_skip(s: Seq, m: int, n: int) ensures (0 <= m && 0 <= n && m + n <= s.len()) ==> #[trigger] s[m..][n..] =~= s[m + n..], { } #[doc(hidden)] #[verifier::inline] pub open spec fn check_argument_is_seq(s: Seq) -> Seq { s } /// Prove two sequences `s1` and `s2` are equal by proving that their elements are equal at each index. /// /// More precisely, `assert_seqs_equal!` requires: /// * `s1` and `s2` have the same length (`s1.len() == s2.len()`), and /// * for all `i` in the range `0 <= i < s1.len()`, we have `s1[i] == s2[i]`. /// /// The property that equality follows from these facts is often called _extensionality_. /// /// `assert_seqs_equal!` can handle many trivial-looking /// identities without any additional help: /// /// ```rust /// proof fn subrange_concat(s: Seq, i: int) { /// requires([ /// 0 <= i && i <= s.len(), /// ]); /// /// let t1 = s[..i]; /// let t2 = s[i..]; /// let t = t1.add(t2); /// /// assert_seqs_equal!(s == t); /// /// assert(s == t); /// } /// ``` /// /// In more complex cases, a proof may be required for the equality of each element pair. /// For example, /// /// ```rust /// proof fn bitvector_seqs() { /// let s = Seq::::new(5, |i| i as u64); /// let t = Seq::::new(5, |i| i as u64 | 0); /// /// assert_seqs_equal!(s == t, i => { /// // Need to show that s[i] == t[i] /// // Prove that the elements are equal by appealing to a bitvector solver: /// let j = i as u64; /// assert_bit_vector(j | 0 == j); /// assert(s[i] == t[i]); /// }); /// } /// ``` #[macro_export] macro_rules! assert_seqs_equal { [$($tail:tt)*] => { $crate::vstd::prelude::verus_proof_macro_exprs!($crate::vstd::seq_lib::assert_seqs_equal_internal!($($tail)*)) }; } #[macro_export] #[doc(hidden)] macro_rules! assert_seqs_equal_internal { (::vstd::spec_eq($s1:expr, $s2:expr)) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2) }; (::vstd::prelude::spec_eq($s1:expr, $s2:expr)) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2) }; (::vstd::prelude::spec_eq($s1:expr, $s2:expr), $idx:ident => $bblock:block) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2, $idx => $bblock) }; (crate::prelude::spec_eq($s1:expr, $s2:expr)) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2) }; (crate::prelude::spec_eq($s1:expr, $s2:expr), $idx:ident => $bblock:block) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2, $idx => $bblock) }; (crate::verus_builtin::spec_eq($s1:expr, $s2:expr)) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2) }; (crate::verus_builtin::spec_eq($s1:expr, $s2:expr), $idx:ident => $bblock:block) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2, $idx => $bblock) }; ($s1:expr, $s2:expr $(,)?) => { $crate::vstd::seq_lib::assert_seqs_equal_internal!($s1, $s2, idx => { }) }; ($s1:expr, $s2:expr, $idx:ident => $bblock:block) => { #[verifier::spec] let s1 = $crate::vstd::seq_lib::check_argument_is_seq($s1); #[verifier::spec] let s2 = $crate::vstd::seq_lib::check_argument_is_seq($s2); $crate::vstd::prelude::assert_by($crate::vstd::prelude::equal(s1, s2), { $crate::vstd::prelude::assert_(s1.len() == s2.len()); $crate::vstd::prelude::assert_forall_by(|$idx : $crate::vstd::prelude::int| { $crate::vstd::prelude::requires($crate::vstd::prelude::verus_proof_expr!(0 <= $idx && $idx < s1.len())); $crate::vstd::prelude::ensures($crate::vstd::prelude::equal(s1.index($idx), s2.index($idx))); { $bblock } }); $crate::vstd::prelude::assert_($crate::vstd::prelude::ext_equal(s1, s2)); }); } } pub broadcast group group_filter_ensures { Seq::lemma_filter_len, Seq::lemma_filter_pred, Seq::lemma_filter_contains, } pub broadcast group group_seq_lib_default { Seq::to_set_ensures, group_filter_ensures, Seq::lemma_filter_index, Seq::add_empty_left, Seq::add_empty_right, Seq::push_distributes_over_add, Seq::filter_distributes_over_add, Seq::lemma_fold_right_split, Seq::lemma_fold_left_split, Seq::lemma_as_ref_index, } pub broadcast group group_to_multiset_ensures { to_multiset_build, to_multiset_remove, to_multiset_len, to_multiset_contains, to_multiset_insert, to_multiset_update, } // include all the Dafny prelude lemmas pub broadcast group group_seq_properties { lemma_seq_contains, lemma_seq_empty_contains_nothing, lemma_seq_empty_equality, lemma_seq_concat_contains_all_elements, lemma_seq_contains_after_push, lemma_seq_subrange_elements, lemma_seq_take_len, lemma_seq_take_contains, lemma_seq_take_index, lemma_seq_skip_len, lemma_seq_skip_contains, lemma_seq_skip_index, lemma_seq_skip_index2, lemma_seq_append_take_skip, lemma_seq_take_update_commut1, lemma_seq_take_update_commut2, lemma_seq_skip_update_commut1, lemma_seq_skip_update_commut2, lemma_seq_skip_build_commut, lemma_seq_skip_nothing, lemma_seq_take_nothing, // Removed the following from group due to bad verification performance // for `lemma_merge_sorted_with_ensures` // lemma_seq_skip_of_skip, group_to_multiset_ensures, } #[doc(hidden)] pub use assert_seqs_equal_internal; pub use assert_seqs_equal; } // verus!