Skip to main content

creusot_std/logic/
fset.rs

1//! A logical/ghost finite set.
2
3#[cfg(creusot)]
4use crate::resolve::structural_resolve;
5use crate::{logic::Mapping, prelude::*};
6use core::marker::PhantomData;
7
8/// A finite set type usable in pearlite and `ghost!` blocks.
9///
10/// If you need an infinite set, see [`Set`](super::Set).
11///
12/// # Ghost
13///
14/// Since [`std::collections::HashSet`] and
15/// [`std::collections::BTreeSet`] have finite
16/// capacity, this could cause some issues in ghost code:
17/// ```rust,creusot,compile_fail
18/// ghost! {
19///     let mut set = HashSet::new();
20///     for _ in 0..=usize::MAX as u128 + 1 {
21///         set.insert(0); // cannot fail, since we are in a ghost block
22///     }
23///     proof_assert!(set.len() <= usize::MAX@); // by definition
24///     proof_assert!(set.len() > usize::MAX@); // uh-oh
25/// }
26/// ```
27///
28/// This type is designed for this use-case, with no restriction on the capacity.
29#[opaque]
30#[builtin("set.Fset.fset")]
31pub struct FSet<T>(PhantomData<T>);
32
33impl<T> FSet<T> {
34    /// Returns the empty set.
35    #[logic]
36    #[builtin("set.Fset.empty", ascription)]
37    pub fn empty() -> Self {
38        dead
39    }
40
41    /// Returns `true` if `e` is in the set.
42    #[logic(open, inline)]
43    pub fn contains(self, e: T) -> bool {
44        Self::mem(e, self)
45    }
46
47    /// [`Self::contains`], but with the order of arguments flipped.
48    ///
49    /// This is how the function is defined in why3.
50    #[doc(hidden)]
51    #[logic]
52    #[builtin("set.Fset.mem")]
53    pub fn mem(_: T, _: Self) -> bool {
54        dead
55    }
56
57    /// Returns a new set, where `e` has been added if it was not present.
58    #[logic(open, inline)]
59    pub fn insert(self, e: T) -> Self {
60        Self::add(e, self)
61    }
62
63    /// [`Self::insert`], but with the order of arguments flipped.
64    ///
65    /// This is how the function is defined in why3.
66    #[doc(hidden)]
67    #[logic]
68    #[builtin("set.Fset.add")]
69    pub fn add(_: T, _: Self) -> Self {
70        dead
71    }
72
73    /// Returns `true` if the set contains no elements.
74    #[logic]
75    #[builtin("set.Fset.is_empty")]
76    pub fn is_empty(self) -> bool {
77        dead
78    }
79
80    /// Returns a new set, where `e` is no longer present.
81    #[logic(open, inline)]
82    pub fn remove(self, e: T) -> Self {
83        Self::rem(e, self)
84    }
85
86    /// [`Self::remove`], but with the order of arguments flipped.
87    ///
88    /// This is how the function is defined in why3.
89    #[doc(hidden)]
90    #[logic]
91    #[builtin("set.Fset.remove")]
92    pub fn rem(_: T, _: Self) -> Self {
93        dead
94    }
95
96    /// Returns a new set, which is the union of `self` and `other`.
97    ///
98    /// An element is in the result if it is in `self` _or_ if it is in `other`.
99    #[logic]
100    #[builtin("set.Fset.union")]
101    pub fn union(self, other: Self) -> Self {
102        let _ = other;
103        dead
104    }
105
106    /// Returns a new set, which is the union of `self` and `other`.
107    ///
108    /// An element is in the result if it is in `self` _or_ if it is in `other`.
109    #[logic]
110    #[builtin("set.Fset.inter")]
111    pub fn intersection(self, other: Self) -> Self {
112        let _ = other;
113        dead
114    }
115
116    /// Returns a new set, which is the difference of `self` with `other`.
117    ///
118    /// An element is in the result if and only if it is in `self` but not in `other`.
119    #[logic]
120    #[builtin("set.Fset.diff")]
121    pub fn difference(self, other: Self) -> Self {
122        let _ = other;
123        dead
124    }
125
126    /// Returns `true` if every element of `self` is in `other`.
127    #[logic]
128    #[builtin("set.Fset.subset")]
129    pub fn is_subset(self, other: Self) -> bool {
130        let _ = other;
131        dead
132    }
133
134    /// Returns `true` if every element of `other` is in `self`.
135    #[logic(open, inline)]
136    pub fn is_superset(self, other: Self) -> bool {
137        Self::is_subset(other, self)
138    }
139
140    /// Returns `true` if `self` and `other` are disjoint.
141    #[logic]
142    #[builtin("set.Fset.disjoint")]
143    pub fn disjoint(self, other: Self) -> bool {
144        let _ = other;
145        dead
146    }
147
148    /// Returns the number of elements in the set, also called its length.
149    #[logic]
150    #[builtin("set.Fset.cardinal")]
151    pub fn len(self) -> Int {
152        dead
153    }
154
155    /// Get an arbitrary element of the set.
156    ///
157    /// # Returns
158    ///
159    /// - If the set is nonempty, the result is guaranteed to be in the set
160    /// - If the set is empty, the result is unspecified
161    #[logic]
162    #[builtin("set.Fset.pick")]
163    pub fn peek(self) -> T {
164        dead
165    }
166
167    /// Extensional equality
168    ///
169    /// Returns `true` if `self` and `other` contain exactly the same elements.
170    ///
171    /// This is in fact equivalent with normal equality.
172    #[logic(open)]
173    #[ensures(result == (self == other))]
174    pub fn ext_eq(self, other: Self) -> bool {
175        pearlite! {
176            forall <e: T> self.contains(e) == other.contains(e)
177        }
178    }
179
180    /// Returns the set containing only `x`.
181    #[logic(open)]
182    #[ensures(forall<y: T> result.contains(y) == (x == y))]
183    pub fn singleton(x: T) -> Self {
184        FSet::empty().insert(x)
185    }
186
187    /// Returns the union of sets `f(t)` over all `t: T`.
188    #[logic(open)]
189    #[ensures(forall<y: U> result.contains(y) == exists<x: T> self.contains(x) && f.get(x).contains(y))]
190    #[variant(self.len())]
191    pub fn unions<U>(self, f: Mapping<T, FSet<U>>) -> FSet<U> {
192        if self.len() == 0 {
193            FSet::empty()
194        } else {
195            let x = self.peek();
196            f.get(x).union(self.remove(x).unions(f))
197        }
198    }
199
200    /// Flipped `map`.
201    #[logic]
202    #[builtin("set.Fset.map")]
203    pub fn fmap<U>(_: Mapping<T, U>, _: Self) -> FSet<U> {
204        dead
205    }
206
207    /// Returns the image of a set by a function.
208    #[logic(open)]
209    pub fn map<U>(self, f: Mapping<T, U>) -> FSet<U> {
210        FSet::fmap(f, self)
211    }
212
213    /// Returns the subset of elements of `self` which satisfy the predicate `f`.
214    #[logic]
215    #[builtin("set.Fset.filter")]
216    pub fn filter(self, f: Mapping<T, bool>) -> Self {
217        let _ = f;
218        dead
219    }
220
221    /// Returns the set of sequences whose head is in `s` and whose tail is in `ss`.
222    #[logic(open)]
223    #[ensures(forall<xs: Seq<T>> result.contains(xs) == (0 < xs.len() && s.contains(xs[0]) && ss.contains(xs.tail())))]
224    pub fn cons(s: FSet<T>, ss: FSet<Seq<T>>) -> FSet<Seq<T>> {
225        proof_assert!(forall<x:T, xs: Seq<T>> xs.push_front(x).tail() == xs);
226        proof_assert!(forall<xs: Seq<T>> 0 < xs.len() ==> xs.tail().push_front(xs[0]) == xs);
227        s.unions(|x| ss.map(|xs: Seq<_>| xs.push_front(x)))
228    }
229
230    /// Returns the set of concatenations of a sequence in `s` and a sequence in `t`.
231    #[logic(open)]
232    #[ensures(forall<xs: Seq<T>> result.contains(xs) == (exists<ys: Seq<T>, zs: Seq<T>> s.contains(ys) && t.contains(zs) && xs == ys.concat(zs)))]
233    pub fn concat(s: FSet<Seq<T>>, t: FSet<Seq<T>>) -> FSet<Seq<T>> {
234        s.unions(|ys: Seq<_>| t.map(|zs| ys.concat(zs)))
235    }
236
237    /// Returns the set of sequences of length `n` whose elements are in `self`.
238    #[logic(open)]
239    #[requires(n >= 0)]
240    #[ensures(forall<xs: Seq<T>> result.contains(xs) == (xs.len() == n && forall<x: T> xs.contains(x) ==> self.contains(x)))]
241    #[variant(n)]
242    pub fn replicate(self, n: Int) -> FSet<Seq<T>> {
243        pearlite! {
244            if n == 0 {
245                proof_assert! { forall<xs: Seq<T>> xs.len() == 0 ==> xs == Seq::empty() };
246                FSet::singleton(Seq::empty())
247            } else {
248                proof_assert! { forall<xs: Seq<T>, i> 0 < i && i < xs.len() ==> xs[i] == xs.tail()[i-1] };
249                FSet::cons(self, self.replicate(n - 1))
250            }
251        }
252    }
253
254    /// Returns the set of sequences of length at most `n` whose elements are in `self`.
255    #[logic(open)]
256    #[requires(n >= 0)]
257    #[ensures(forall<xs: Seq<T>> result.contains(xs) == (xs.len() <= n && forall<x: T> xs.contains(x) ==> self.contains(x)))]
258    #[variant(n)]
259    pub fn replicate_up_to(self, n: Int) -> FSet<Seq<T>> {
260        pearlite! {
261            if n == 0 {
262                proof_assert! { forall<xs: Seq<T>> xs.len() == 0 ==> xs == Seq::empty() };
263                FSet::singleton(Seq::empty())
264            } else {
265                self.replicate_up_to(n - 1).union(self.replicate(n))
266            }
267        }
268    }
269
270    /// Distributivity of `unions` over `union`.
271    #[logic]
272    #[ensures(self.union(other).unions(f) == self.unions(f).union(other.unions(f)))]
273    #[ensures(self.unions(|x| f.get(x).union(g.get(x))) == self.unions(f).union(self.unions(g)))]
274    pub fn unions_union<U>(self, other: Self, f: Mapping<T, FSet<U>>, g: Mapping<T, FSet<U>>) {}
275
276    /// Distributivity of `map` over `union`.
277    #[logic]
278    #[ensures(self.union(other).map(f) == self.map(f).union(other.map(f)))]
279    pub fn map_union<U>(self, other: Self, f: Mapping<T, U>) {}
280
281    /// Distributivity of `concat` over `union`.
282    #[logic]
283    #[ensures(FSet::concat(s1.union(s2), t) == FSet::concat(s1, t).union(FSet::concat(s2, t)))]
284    #[ensures(FSet::concat(t, s1.union(s2)) == FSet::concat(t, s1).union(FSet::concat(t, s2)))]
285    pub fn concat_union(s1: FSet<Seq<T>>, s2: FSet<Seq<T>>, t: FSet<Seq<T>>) {}
286
287    /// Distributivity of `cons` over `union`.
288    #[logic]
289    #[ensures(FSet::concat(FSet::cons(self, t), u) == FSet::cons(self, FSet::concat(t, u)))]
290    pub fn cons_concat(self, t: FSet<Seq<T>>, u: FSet<Seq<T>>) {
291        proof_assert!(forall<x: T, xs: Seq<T>, ys: Seq<T>> xs.push_front(x).concat(ys) == xs.concat(ys).push_front(x));
292        proof_assert!(forall<x: T, ys: Seq<T>> ys.push_front(x).tail() == ys);
293        proof_assert!(forall<ys: Seq<T>> 0 < ys.len() ==> ys == ys.tail().push_front(ys[0]));
294    }
295
296    /// Distributivity of `replicate` over `union`.
297    #[logic]
298    #[requires(0 <= n && 0 <= m)]
299    #[ensures(self.replicate(n + m) == FSet::concat(self.replicate(n), self.replicate(m)))]
300    #[variant(n)]
301    pub fn concat_replicate(self, n: Int, m: Int) {
302        pearlite! {
303            if n == 0 {
304                Self::concat_empty(self.replicate(m));
305            } else {
306                self.cons_concat(self.replicate(n - 1), self.replicate(m));
307                self.concat_replicate(n - 1, m);
308            }
309        }
310    }
311
312    /// The neutral element of `FSet::concat` is `FSet::singleton(Seq::empty())`.
313    #[logic]
314    #[ensures(FSet::concat(FSet::singleton(Seq::empty()), s) == s)]
315    #[ensures(FSet::concat(s, FSet::singleton(Seq::empty())) == s)]
316    pub fn concat_empty(s: FSet<Seq<T>>) {
317        proof_assert!(forall<xs: Seq<T>> xs.concat(Seq::empty()) == xs);
318        proof_assert!(forall<xs: Seq<T>> Seq::empty().concat(xs) == xs);
319    }
320
321    /// An equation relating `s.replicate_up_to(m)` and `s.replicate_up_to(n)`.
322    #[logic]
323    #[requires(0 <= n && n < m)]
324    #[ensures(self.replicate_up_to(m) == self.replicate_up_to(n).union(
325        FSet::concat(self.replicate(n + 1), self.replicate_up_to(m - n - 1))))]
326    #[variant(m)]
327    pub fn concat_replicate_up_to(self, n: Int, m: Int) {
328        pearlite! {
329            if n + 1 == m {
330                Self::concat_empty(self.replicate(n + 1));
331            } else {
332                Self::concat_union(self.replicate(n), self.replicate(m - 1), self.replicate(m - n - 1));
333                self.concat_replicate(n, m - n - 1);
334                self.concat_replicate_up_to(n, m - 1);
335            }
336        }
337    }
338}
339
340impl FSet<Int> {
341    /// Return the interval of integers in `[i, j)`.
342    #[logic(open)]
343    #[builtin("set.FsetInt.interval")]
344    pub fn interval(i: Int, j: Int) -> FSet<Int> {
345        let _ = (i, j);
346        dead
347    }
348}
349
350/// Ghost definitions
351impl<T> FSet<T> {
352    /// Create a new, empty set on the ghost heap.
353    #[trusted]
354    #[check(ghost)]
355    #[ensures(result.is_empty())]
356    #[allow(unreachable_code)]
357    pub fn new() -> Ghost<Self> {
358        Ghost::conjure()
359    }
360
361    /// Returns the number of elements in the set.
362    ///
363    /// If you need to get the length in pearlite, consider using [`len`](Self::len).
364    ///
365    /// # Example
366    /// ```rust,creusot
367    /// use creusot_std::{logic::FSet, prelude::*};
368    ///
369    /// let mut set = FSet::new();
370    /// ghost! {
371    ///     let len1 = set.len_ghost();
372    ///     set.insert_ghost(1);
373    ///     set.insert_ghost(2);
374    ///     set.insert_ghost(1);
375    ///     let len2 = set.len_ghost();
376    ///     proof_assert!(len1 == 0);
377    ///     proof_assert!(len2 == 2);
378    /// };
379    /// ```
380    #[trusted]
381    #[check(ghost)]
382    #[ensures(result == self.len())]
383    pub fn len_ghost(&self) -> Int {
384        panic!()
385    }
386
387    /// Returns true if the set contains the specified value.
388    ///
389    /// # Example
390    /// ```rust,creusot
391    /// use creusot_std::{logic::FSet, prelude::*};
392    ///
393    /// let mut set = FSet::new();
394    /// ghost! {
395    ///     set.insert_ghost(1);
396    ///     let (b1, b2) = (set.contains_ghost(&1), set.contains_ghost(&2));
397    ///     proof_assert!(b1);
398    ///     proof_assert!(!b2);
399    /// };
400    /// ```
401    #[trusted]
402    #[check(ghost)]
403    #[ensures(result == self.contains(*value))]
404    pub fn contains_ghost(&self, value: &T) -> bool {
405        let _ = value;
406        panic!()
407    }
408
409    /// Adds a value to the set.
410    ///
411    /// Returns whether the value was newly inserted. That is:
412    /// - If the set did not previously contain this value, `true` is returned.
413    /// - If the set already contained this value, `false` is returned, and the set is
414    ///   not modified: original value is not replaced, and the value passed as argument
415    ///   is dropped.
416    ///
417    /// # Example
418    /// ```rust,creusot
419    /// use creusot_std::{logic::FSet, prelude::*};
420    ///
421    /// let mut set = FSet::new();
422    /// ghost! {
423    ///     let res1 = set.insert_ghost(42);
424    ///     proof_assert!(res1);
425    ///     proof_assert!(set.contains(42i32));
426    ///
427    ///     let res2 = set.insert_ghost(41);
428    ///     let res3 = set.insert_ghost(42);
429    ///     proof_assert!(res2);
430    ///     proof_assert!(!res3);
431    ///     proof_assert!(set.len() == 2);
432    /// };
433    /// ```
434    #[trusted]
435    #[check(ghost)]
436    #[ensures(^self == (*self).insert(value))]
437    #[ensures(result == !(*self).contains(value))]
438    pub fn insert_ghost(&mut self, value: T) -> bool {
439        let _ = value;
440        panic!()
441    }
442
443    /// Removes a value from the set. Returns whether the value was present in the set.
444    ///
445    /// # Example
446    /// ```rust,creusot
447    /// use creusot_std::{logic::FSet, prelude::*};
448    ///
449    /// let mut set = FSet::new();
450    /// let res = ghost! {
451    ///     set.insert_ghost(1);
452    ///     let res1 = set.remove_ghost(&1);
453    ///     let res2 = set.remove_ghost(&1);
454    ///     proof_assert!(res1 && !res2);
455    /// };
456    /// ```
457    #[trusted]
458    #[check(ghost)]
459    #[ensures(^self == (*self).remove(*value))]
460    #[ensures(result == (*self).contains(*value))]
461    pub fn remove_ghost(&mut self, value: &T) -> bool {
462        let _ = value;
463        panic!()
464    }
465
466    /// Clears the set, removing all values.
467    ///
468    /// # Example
469    /// ```rust,creusot
470    /// use creusot_std::{prelude::*, logic::FSet};
471    ///
472    /// let mut s = FSet::new();
473    /// ghost! {
474    ///     s.insert_ghost(1);
475    ///     s.insert_ghost(2);
476    ///     s.insert_ghost(3);
477    ///     s.clear_ghost();
478    ///     proof_assert!(s == FSet::empty());
479    /// };
480    /// ```
481    #[trusted]
482    #[check(ghost)]
483    #[ensures(^self == Self::empty())]
484    pub fn clear_ghost(&mut self) {}
485}
486
487impl<T: Clone + Copy> Clone for FSet<T> {
488    #[trusted]
489    #[check(ghost)]
490    #[ensures(result == *self)]
491    fn clone(&self) -> Self {
492        *self
493    }
494}
495
496// Having `Copy` guarantees that the operation is pure, even if we decide to change the definition of `Clone`.
497impl<T: Clone + Copy> Copy for FSet<T> {}
498
499impl<T> Invariant for FSet<T> {
500    #[logic(open, prophetic, inline)]
501    #[creusot::trusted_trivial_if_param_trivial]
502    fn invariant(self) -> bool {
503        pearlite! { forall<x: T> self.contains(x) ==> inv(x) }
504    }
505}
506impl<T> Resolve for FSet<T> {
507    #[logic(open, prophetic)]
508    #[creusot::trusted_trivial_if_param_trivial]
509    fn resolve(self) -> bool {
510        pearlite! { forall<x: T> self.contains(x) ==> resolve(x) }
511    }
512
513    #[trusted]
514    #[logic(open(self), prophetic)]
515    #[requires(structural_resolve(self))]
516    #[ensures(self.resolve())]
517    fn resolve_coherence(self) {}
518}