#[allow(unused_imports)] use super::iset::*; #[allow(unused_imports)] use super::map::*; #[allow(unused_imports)] use super::pervasive::*; #[allow(unused_imports)] use super::prelude::*; verus! { /// `Set` is a finite set type for specifications. /// /// An object `set: Set` is a subset of the set of all values `a: A`. /// Equivalently, it can be thought of as a boolean predicate on `A`. /// /// Sets can be constructed in a few different ways: /// * [`Set::empty`] gives an empty set /// * [`Set::new`] constructs a set from a boolean predicate /// * The [`set!`] macro, to construct small sets of a fixed size /// * By manipulating an existing sequence with [`Set::union`], [`Set::intersect`], /// [`Set::difference`], [`Set::complement`], [`Set::filter`], [`Set::insert`], /// or [`Set::remove`]. /// /// To prove that two sequences are equal, it is usually easiest to use the extensionality /// operator `=~=`. /// /// `Set` only holds finite sets, so it can be used in recursive types. /// For instance, a type `T` can contain a `Set`. #[verifier::ext_equal] #[verifier::external_body] #[verifier::accept_recursive_types(A)] #[cfg_attr(verus_keep_ghost, rustc_diagnostic_item = "verus::vstd::set::Set")] pub struct Set { // To prevent Verus's internal checks from rejecting recursive types, // we use an artificial definition of `Set` that hides its inclusion // of a function from `A` to `bool`. // // To make sure that proofs in this file don't take advantage of // this artificial structure (e.g., to prove that any two `Set`s are // equal), we mark this definition as `external_body`. // // For future work, we may figure out how to have `Set` use a // `Seq`-like representation that is inherently finite, to eliminate // the need for this. We haven't done this yet, though, because it would // introduce the problem of multiple representations of equivalent // sets, which creates a different problem with extensional equality. dummy: core::marker::PhantomData, } impl Set { /// Generates an `ISet` with the same elements pub uninterp spec fn to_iset(self) -> ISet; /// Generates a `Set` from the given `ISet`, governed by /// `axiom_make_set`. Thanks to the axiom `axiom_make_set`, this /// function is known to produce a `Set` with the same elements as /// the given `ISet`, provided that `ISet` is finite. If the given /// `ISet` is infinite, it produces an arbitrary finite set. uninterp spec fn make_set(s: ISet) -> Set; /// This axiom says that `make_set` produces a `Set` with the same /// elements as the given `ISet`, provided that `ISet` is finite. /// (If the given `ISet` is infinite, `make_set` produces an /// arbitrary finite set.) broadcast axiom fn axiom_make_set(s: ISet) requires s.finite(), ensures #![trigger Self::make_set(s).to_iset()] Self::make_set(s).to_iset() == s, ; /// This axiom says that the set represented by a `Set` is always /// finite. broadcast axiom fn axiom_is_finite(self) ensures #![trigger self.to_iset().finite()] self.to_iset().finite(), ; /// The "empty" set. /// /// Usage Example:
/// ```rust /// let empty_set = Set::
::empty(); /// /// assert(empty_set.is_empty()); /// assert(empty_set.complement() =~= Set::::full()); /// assert(Set::::empty().finite()); /// assert(Set::::empty().len() == 0); /// assert(forall |x: A| !Set::::empty().contains(x)); /// ``` /// Axioms around the empty set are:
/// * [`lemma_set_empty_len`]
/// * [`lemma_set_empty`] #[rustc_diagnostic_item = "verus::vstd::set::Set::empty"] pub closed spec fn empty() -> Set
{ Self::make_set(ISet::::empty()) } /// Set whose membership is determined by the given `ISet`, /// but only if that `ISet` is finite. /// /// Usage Examples: /// ```rust /// let iset_a = ISet::new(|x : nat| x < 42); /// let option_set_b = Set::::new(iset_a); /// assert(iset_a.finite() ==> /// option_set_b matches Some(s) && /// forall|x| x < 42 <==> s.contains(x)); /// ``` pub closed spec fn new_from_iset(s: ISet) -> Option> { if s.finite() { Some(Self::make_set(s)) } else { None } } /// Set whose membership is determined by the given boolean predicate, /// but only if the predicate produces a finite set. (If it produces an /// infinite set, the result of this function is None.) /// /// Usage Examples: /// ```rust /// let option_set_a = Set::new(|x : nat| x < 42); /// let option_set_b = Set::::new(|x| some_predicate(x)); /// assert(ISet::new(|x| some_predicate(x)).finite()) ==> /// option_set_b matches Some(s) && /// forall|x| some_predicate(x) <==> s.contains(x)); /// ``` pub closed spec fn new(f: spec_fn(A) -> bool) -> Option> { Self::new_from_iset(ISet::new(f)) } /// The "full" set, i.e., set containing every element of type `A`. /// Note that if `A` is infinite, then this produces None. #[rustc_diagnostic_item = "verus::vstd::set::Set::full"] pub open spec fn full() -> Option> { Set::new(|a: A| true) } /// Predicate indicating if the set contains the given element. #[rustc_diagnostic_item = "verus::vstd::set::Set::contains"] #[verifier::inline] pub open spec fn contains(self, a: A) -> bool { self.to_iset().contains(a) } /// Predicate indicating if the set contains the given element: supports `self has a` syntax. #[verifier::inline] pub open spec fn spec_has(self, a: A) -> bool { self.contains(a) } /// Returns `true` if the first argument is a subset of the second. #[rustc_diagnostic_item = "verus::vstd::set::Set::subset_of"] pub open spec fn subset_of(self, s2: Set) -> bool { forall|a: A| self.contains(a) ==> s2.contains(a) } #[verifier::inline] pub open spec fn spec_le(self, s2: Set) -> bool { self.subset_of(s2) } /// Returns a new set with the given element inserted. /// If that element is already in the set, then an identical set is returned. #[rustc_diagnostic_item = "verus::vstd::set::Set::insert"] pub closed spec fn insert(self, a: A) -> Set { Self::make_set(self.to_iset().insert(a)) } /// Returns a new set with the given element removed. /// If that element is already absent from the set, then an identical set is returned. #[rustc_diagnostic_item = "verus::vstd::set::Set::remove"] pub closed spec fn remove(self, a: A) -> Set { Self::make_set(self.to_iset().remove(a)) } /// Union of two sets. pub closed spec fn union(self, s2: Set) -> Set { Self::make_set(self.to_iset().union(s2.to_iset())) } /// `+` operator, synonymous with `union` #[verifier::inline] pub open spec fn spec_add(self, s2: Set) -> Set { self.union(s2) } /// Intersection of two sets. pub closed spec fn intersect(self, s2: Set) -> Set { Self::make_set(self.to_iset().intersect(s2.to_iset())) } /// `*` operator, synonymous with `intersect` #[verifier::inline] pub open spec fn spec_mul(self, s2: Set) -> Set { self.intersect(s2) } /// Set difference, i.e., the set of all elements in the first one but not in the second. pub closed spec fn difference(self, s2: Set) -> Set { Self::make_set(self.to_iset().difference(s2.to_iset())) } /// `-` operator, synonymous with `difference` #[verifier::inline] pub open spec fn spec_sub(self, s2: Set) -> Set { self.difference(s2) } /// Set complement (within the space of all possible elements in `A`). /// Returns None if this would be an infinite set. pub open spec fn complement(self) -> Option> { Set::new(|a| !self.contains(a)) } /// Set of all elements in the given set which satisfy the predicate `f`. pub closed spec fn filter(self, f: spec_fn(A) -> bool) -> Set { Self::make_set(self.to_iset().filter(f)) } /// Returns `true` if the set is finite. #[deprecated(note = "Every Set is always finite, so this is always true.")] pub open spec fn finite(self) -> bool { true } /// Returns `true` if this set is congruent to (contains the same elements as) /// a given ISet. pub open spec fn congruent(self, s2: ISet) -> bool { forall|a: A| #![all_triggers] self.contains(a) <==> s2.contains(a) } /// Cardinality of the set. pub closed spec fn len(self) -> nat { self.to_iset().len() } /// Chooses an arbitrary element of the set. /// /// This is often useful for proofs by induction. /// /// (Note that, although the result is arbitrary, it is still a _deterministic_ function /// like any other `spec` function.) pub open spec fn choose(self) -> A { choose|a: A| self.contains(a) } /// Returns `true` if the sets are disjoint, i.e., if their interesection is /// the empty set. pub open spec fn disjoint(self, s2: Self) -> bool { forall|a: A| self.contains(a) ==> !s2.contains(a) } } /// Sets `s1` and `s2` are considered equal if and only if they contain all of the same elements. /// This has to be an axiom because `Set` is `external_body`. pub broadcast proof fn axiom_set_ext_equal(s1: Set, s2: Set) ensures #[trigger] (s1 =~= s2) <==> (forall|a: A| s1.contains(a) == s2.contains(a)), { admit(); } /// Sets `s1` and `s2` are considered equal if and only if they contain all of the same elements. /// This has to be an axiom because `Set` is `external_body`. pub broadcast proof fn axiom_set_ext_equal_deep(s1: Set, s2: Set) ensures #[trigger] (s1 =~~= s2) <==> s1 =~= s2, { admit(); } /// A member of a `Set` is less than that `Set`. pub broadcast axiom fn axiom_set_decreases_to_member(s: Set, a: A) requires #[trigger] s.contains(a), ensures #[trigger] (decreases_to!(s => a)), ; broadcast use super::iset::group_iset_lemmas; pub mod fold { use super::*; impl Set { /// Folds the set, applying `f` to perform the fold. The next element for the fold is chosen by /// the choose operator. /// /// Given a set `s = {x0, x1, x2, ..., xn}`, applying this function `s.fold(init, f)` /// returns `f(...f(f(init, x0), x1), ..., xn)`. #[verifier::inline] pub open spec fn fold(self, z: B, f: spec_fn(B, A) -> B) -> B recommends super::super::iset::fold::is_fun_commutative(f), { self.to_iset().fold(z, f) } } } /// The empty set contains no elements pub broadcast proof fn lemma_set_empty(a: A) ensures !(#[trigger] Set::empty().contains(a)), { broadcast use Set::axiom_make_set; } /// If `Set::::new(f)` produces `Some(s)`, then `s` contains `a` /// if and only if `f(a)` is true. pub broadcast proof fn lemma_set_new(f: spec_fn(A) -> bool, a: A) requires Set::::new(f) is Some, ensures #[trigger] Set::::new(f).unwrap().contains(a) == f(a), { broadcast use Set::axiom_make_set; } /// If `ISet::::new(f)` is finite, then `Set::::new(f)` /// produces `Some(s)`. Useful for triggering the `lemma_set_new` /// to show that `s` contains `a` if and only if `f(a)` is true. pub broadcast proof fn lemma_set_new_some(f: spec_fn(A) -> bool) requires ISet::::new(f).finite(), ensures #[trigger] Set::::new(f) is Some, { broadcast use Set::axiom_make_set; } /// If an iset `s` is finite, then `Set::new_from_iset(s)` has the same /// contents as `s`. pub broadcast proof fn lemma_set_new_from_iset(s: ISet) requires s.finite(), ensures #![trigger Set::::new_from_iset(s)] Set::::new_from_iset(s) is Some, Set::::new_from_iset(s).unwrap().to_iset() == s, { broadcast use Set::axiom_make_set; assert(ISet::new(|a: A| s.contains(a)) =~= s); } /// The result of inserting element `a` into set `s` must contains `a`. pub broadcast proof fn lemma_set_insert_same(s: Set, a: A) ensures #[trigger] s.insert(a).contains(a), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// If `a1` does not equal `a2`, then the result of inserting element `a2` into set `s` /// must contain `a1` if and only if the set contained `a1` before the insertion of `a2`. pub broadcast proof fn lemma_set_insert_different(s: Set, a1: A, a2: A) requires a1 != a2, ensures #[trigger] s.insert(a2).contains(a1) == s.contains(a1), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The result of removing element `a` from set `s` must not contain `a`. pub broadcast proof fn lemma_set_remove_same(s: Set, a: A) ensures !(#[trigger] s.remove(a).contains(a)), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// Removing an element `a` from a set `s` and then inserting `a` back into the set` /// is equivalent to the original set `s`. pub broadcast proof fn lemma_set_remove_insert(s: Set, a: A) requires s.contains(a), ensures (#[trigger] s.remove(a)).insert(a) == s, { assert forall|aa| #![all_triggers] s.remove(a).insert(a).contains(aa) implies s.contains( aa, ) by { if a == aa { } else { lemma_set_remove_different(s, aa, a); lemma_set_insert_different(s.remove(a), aa, a); } }; assert forall|aa| #![all_triggers] s.contains(aa) implies s.remove(a).insert(a).contains( aa, ) by { if a == aa { lemma_set_insert_same(s.remove(a), a); } else { lemma_set_remove_different(s, aa, a); lemma_set_insert_different(s.remove(a), aa, a); } }; axiom_set_ext_equal(s.remove(a).insert(a), s); } /// If `a1` does not equal `a2`, then the result of removing element `a2` from set `s` /// must contain `a1` if and only if the set contained `a1` before the removal of `a2`. pub broadcast proof fn lemma_set_remove_different(s: Set, a1: A, a2: A) requires a1 != a2, ensures #[trigger] s.remove(a2).contains(a1) == s.contains(a1), { broadcast use axiom_set_ext_equal; broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The union of sets `s1` and `s2` contains element `a` if and only if /// `s1` contains `a` and/or `s2` contains `a`. pub broadcast proof fn lemma_set_union(s1: Set, s2: Set, a: A) ensures #[trigger] s1.union(s2).contains(a) == (s1.contains(a) || s2.contains(a)), { broadcast use axiom_set_ext_equal; broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The intersection of sets `s1` and `s2` contains element `a` if and only if /// both `s1` and `s2` contain `a`. pub broadcast proof fn lemma_set_intersect(s1: Set, s2: Set, a: A) ensures #[trigger] s1.intersect(s2).contains(a) == (s1.contains(a) && s2.contains(a)), { broadcast use axiom_set_ext_equal; broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The set difference between `s1` and `s2` contains element `a` if and only if /// `s1` contains `a` and `s2` does not contain `a`. pub broadcast proof fn lemma_set_difference(s1: Set, s2: Set, a: A) ensures #[trigger] s1.difference(s2).contains(a) == (s1.contains(a) && !s2.contains(a)), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The complement of set `s` contains element `a` if and only if `s` does not contain `a`. pub broadcast proof fn lemma_set_complement(s: Set, a: A) requires ISet::new(|a| !s.contains(a)).finite(), ensures #[trigger] s.complement().unwrap().contains(a) == !s.contains(a), { broadcast use Set::axiom_make_set; } /// The filter of set `s` using function `f` contains element `a` if and only if `s` contains `a` /// and `f(a)` is true. pub broadcast proof fn lemma_set_filter(s: Set, f: spec_fn(A) -> bool, a: A) ensures #[trigger] s.filter(f).contains(a) == (s.contains(a) && f(a)), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } // Lemmas about len // The following, with lemma_set_ext_equal, are enough to build libraries about len. /// The empty set has length 0. pub broadcast proof fn lemma_set_empty_len() ensures #[trigger] Set::::empty().len() == 0, { broadcast use Set::axiom_make_set; } /// The result of inserting an element `a` into a finite set `s` has length /// `s.len() + 1` if `a` is not already in `s` and length `s.len()` otherwise. pub broadcast proof fn lemma_set_insert_len(s: Set, a: A) ensures #[trigger] s.insert(a).len() == s.len() + (if s.contains(a) { 0int } else { 1 }), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// The result of removing an element `a` from a finite set `s` has length /// `s.len() - 1` if `a` is in `s` and length `s.len()` otherwise. pub broadcast proof fn lemma_set_remove_len(s: Set, a: A) ensures s.len() == #[trigger] s.remove(a).len() + (if s.contains(a) { 1int } else { 0 }), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// If a finite set `s` contains any element, it has length greater than 0. pub broadcast proof fn lemma_set_contains_len(s: Set, a: A) requires #[trigger] s.contains(a), ensures #[trigger] s.len() != 0, { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// A finite set `s` contains the element `s.choose()` if it has length greater than 0. pub broadcast proof fn lemma_set_choose_len(s: Set) requires #[trigger] s.len() != 0, ensures #[trigger] s.contains(s.choose()), { assert(s.to_iset().contains(s.to_iset().choose())); } /// Converting a `Set` to an `ISet` produces a finite result. pub broadcast proof fn lemma_to_iset_finite(s: Set) ensures #[trigger] s.to_iset().finite(), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } /// Converting a `Set` to an `ISet` produces a result with the same /// length. pub broadcast proof fn lemma_to_iset_len(s: Set) ensures #[trigger] s.to_iset().len() == s.len(), { broadcast use Set::axiom_make_set; broadcast use Set::axiom_is_finite; } pub broadcast group group_set_lemmas { axiom_set_ext_equal, axiom_set_ext_equal_deep, axiom_set_decreases_to_member, lemma_set_empty, lemma_set_new, lemma_set_new_from_iset, lemma_set_new_some, lemma_set_insert_same, lemma_set_insert_different, lemma_set_remove_same, lemma_set_remove_insert, lemma_set_remove_different, lemma_set_union, lemma_set_intersect, lemma_set_difference, lemma_set_complement, lemma_set_filter, lemma_set_empty_len, lemma_set_insert_len, lemma_set_remove_len, lemma_set_contains_len, lemma_set_choose_len, lemma_set_new, lemma_to_iset_finite, lemma_to_iset_len, } // Macros #[doc(hidden)] #[macro_export] macro_rules! set_internal { [$($elem:expr),* $(,)?] => { $crate::vstd::set::Set::empty() $(.insert($elem))* }; } #[macro_export] macro_rules! set { [$($tail:tt)*] => { $crate::vstd::prelude::verus_proof_macro_exprs!($crate::vstd::set::set_internal!($($tail)*)) }; } pub use set_internal; pub use set; } // verus!