Skip to main content

improv_verified/
keys.rs

1//! Coordinate-key projection and union: the key arithmetic of every
2//! dimensional join.
3//!
4//! A coordinate key is a `(category, item)` list sorted by category with no
5//! category twice (`sorted`), as every engine `CoordKey` is.
6
7#[allow(unused_imports)]
8use vstd::prelude::*;
9
10verus! {
11
12/// Strictly increasing categories: sorted, and no category twice.
13pub open spec fn sorted(k: Seq<(u32, u32)>) -> bool {
14    forall|i: int, j: int| 0 <= i < j < k.len() ==> (#[trigger] k[i]).0 < (#[trigger] k[j]).0
15}
16
17/// The pair's category is one of `keep`.
18pub open spec fn kept(keep: Seq<u32>) -> spec_fn((u32, u32)) -> bool {
19    |p: (u32, u32)| keep.contains(p.0)
20}
21
22/// Some pair of `k` has category `c`.
23pub open spec fn has_cat(k: Seq<(u32, u32)>, c: u32) -> bool {
24    exists|i: int| 0 <= i < k.len() && (#[trigger] k[i]).0 == c
25}
26
27/// `a` and `b` give the same item to every category they share.
28pub open spec fn agree(a: Seq<(u32, u32)>, b: Seq<(u32, u32)>) -> bool {
29    forall|i: int, j: int|
30        0 <= i < a.len() && 0 <= j < b.len() && (#[trigger] a[i]).0 == (#[trigger] b[j]).0 ==> a[i]
31            == b[j]
32}
33
34/// Pushing keeps what was there and adds `x`.
35proof fn lemma_push_contains(r0: Seq<(u32, u32)>, x: (u32, u32))
36    ensures
37        r0.push(x).contains(x),
38        forall|y: (u32, u32)| r0.contains(y) ==> #[trigger] r0.push(x).contains(y),
39{
40    assert(r0.push(x)[r0.len() as int] == x);
41    assert forall|y: (u32, u32)| r0.contains(y) implies #[trigger] r0.push(x).contains(y) by {
42        let t = choose|t: int| 0 <= t < r0.len() && r0[t] == y;
43        assert(r0.push(x)[t] == y);
44    }
45}
46
47/// `c` is one of `keep` (linear: `keep` is a handful of categories).
48#[inline]
49fn has(keep: &[u32], c: u32) -> (b: bool)
50    ensures
51        b == keep@.contains(c),
52{
53    let mut i = 0;
54    while i < keep.len()
55        invariant
56            0 <= i <= keep.len(),
57            forall|m: int| 0 <= m < i ==> keep@[m] != c,
58        decreases keep.len() - i,
59    {
60        if keep[i] == c {
61            assert(keep@[i as int] == c);
62            return true;
63        }
64        i += 1;
65    }
66    false
67}
68
69/// `k` restricted to the categories in `keep`: exactly the pairs whose
70/// category is kept, in their order (a filter), so a sorted key projects to a
71/// sorted key.
72#[inline]
73pub fn project(k: &[(u32, u32)], keep: &[u32]) -> (r: Vec<(u32, u32)>)
74    ensures
75        r@ == k@.filter(kept(keep@)),
76        sorted(k@) ==> sorted(r@),
77{
78    let mut r: Vec<(u32, u32)> = Vec::with_capacity(k.len());
79    let mut i = 0;
80    while i < k.len()
81        invariant
82            0 <= i <= k.len(),
83            r@ == k@.subrange(0, i as int).filter(kept(keep@)),
84            sorted(k@) ==> sorted(r@),
85            sorted(k@) ==> forall|t: int, m: int|
86                0 <= t < r.len() && i <= m < k.len() ==> (#[trigger] r@[t]).0 < (#[trigger] k@[m]).0,
87        decreases k.len() - i,
88    {
89        let x = k[i];
90        let keep_it = has(keep, x.0);
91        proof {
92            assert(k@.subrange(0, i + 1) =~= k@.subrange(0, i as int).push(x));
93            k@.subrange(0, i as int).lemma_filter_push(x, kept(keep@));
94        }
95        if keep_it {
96            r.push(x);
97        }
98        i += 1;
99    }
100    assert(k@.subrange(0, k.len() as int) =~= k@);
101    r
102}
103
104/// The union of two sorted keys, by merge: sorted (so no category twice);
105/// every pair of `a`; every pair of `b` whose category `a` lacks (all of `b`
106/// when the two agree on their shared categories, as join partners do); and
107/// nothing else. On a shared category `a`'s item wins.
108#[inline]
109pub fn union_keys(a: &[(u32, u32)], b: &[(u32, u32)]) -> (r: Vec<(u32, u32)>)
110    requires
111        sorted(a@),
112        sorted(b@),
113    ensures
114        sorted(r@),
115        forall|x: (u32, u32)| a@.contains(x) ==> r@.contains(x),
116        forall|x: (u32, u32)| #[trigger] b@.contains(x) ==> r@.contains(x) || has_cat(a@, x.0),
117        agree(a@, b@) ==> forall|x: (u32, u32)| b@.contains(x) ==> r@.contains(x),
118        forall|x: (u32, u32)| r@.contains(x) ==> a@.contains(x) || b@.contains(x),
119{
120    broadcast use vstd::seq_lib::lemma_seq_contains_after_push;
121
122    let mut r: Vec<(u32, u32)> = Vec::with_capacity(a.len().saturating_add(b.len()));
123    let mut i = 0;
124    let mut j = 0;
125    while i < a.len() || j < b.len()
126        invariant
127            0 <= i <= a.len(),
128            0 <= j <= b.len(),
129            sorted(a@),
130            sorted(b@),
131            sorted(r@),
132            forall|t: int, m: int|
133                0 <= t < r.len() && i <= m < a.len() ==> (#[trigger] r@[t]).0 < (#[trigger] a@[m]).0,
134            forall|t: int, m: int|
135                0 <= t < r.len() && j <= m < b.len() ==> (#[trigger] r@[t]).0 < (#[trigger] b@[m]).0,
136            forall|m: int| 0 <= m < i ==> r@.contains(#[trigger] a@[m]),
137            forall|m: int| 0 <= m < j ==> r@.contains(#[trigger] b@[m]) || has_cat(a@, b@[m].0),
138            forall|x: (u32, u32)| r@.contains(x) ==> a@.contains(x) || b@.contains(x),
139        decreases a.len() - i + b.len() - j,
140    {
141        if j >= b.len() || (i < a.len() && a[i].0 < b[j].0) {
142            let x = a[i];
143            assert(a@.contains(x) && x == a@[i as int]);
144            let ghost r0 = r@;
145            r.push(x);
146            proof {
147                lemma_push_contains(r0, x);
148            }
149            i += 1;
150        } else if i >= a.len() || b[j].0 < a[i].0 {
151            let x = b[j];
152            assert(b@.contains(x) && x == b@[j as int]);
153            let ghost r0 = r@;
154            r.push(x);
155            proof {
156                lemma_push_contains(r0, x);
157            }
158            j += 1;
159        } else {
160            let x = a[i];
161            assert(a@.contains(x) && x == a@[i as int]);
162            assert(has_cat(a@, b@[j as int].0));
163            let ghost r0 = r@;
164            r.push(x);
165            proof {
166                lemma_push_contains(r0, x);
167            }
168            i += 1;
169            j += 1;
170        }
171    }
172    proof {
173        assert forall|x: (u32, u32)| a@.contains(x) implies r@.contains(x) by {
174            let m = choose|m: int| 0 <= m < a.len() && a@[m] == x;
175            assert(r@.contains(a@[m]));
176        }
177        assert forall|x: (u32, u32)| #[trigger] b@.contains(x) implies r@.contains(x) || has_cat(
178            a@,
179            x.0,
180        ) by {
181            let m = choose|m: int| 0 <= m < b.len() && b@[m] == x;
182            assert(r@.contains(b@[m]) || has_cat(a@, b@[m].0));
183        }
184        if agree(a@, b@) {
185            assert forall|x: (u32, u32)| b@.contains(x) implies r@.contains(x) by {
186                let m = choose|m: int| 0 <= m < b.len() && b@[m] == x;
187                if !r@.contains(b@[m]) {
188                    let t = choose|t: int| 0 <= t < a.len() && (#[trigger] a@[t]).0 == b@[m].0;
189                    assert(a@[t] == b@[m]);
190                    assert(r@.contains(a@[t]));
191                }
192            }
193        }
194    }
195    r
196}
197
198} // verus!
199
200#[cfg(test)]
201#[cfg_attr(coverage_nightly, coverage(off))]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn project_filters_in_order() {
207        let k = [(1, 10), (2, 20), (3, 30)];
208        assert_eq!(project(&k, &[3, 1]), vec![(1, 10), (3, 30)]);
209        assert_eq!(project(&k, &[]), vec![]);
210        assert_eq!(project(&k, &[9]), vec![]);
211    }
212
213    #[test]
214    fn union_merges_and_left_wins() {
215        let a = [(1, 10), (3, 30)];
216        let b = [(2, 20), (3, 31), (4, 40)];
217        assert_eq!(union_keys(&a, &b), vec![(1, 10), (2, 20), (3, 30), (4, 40)]);
218        assert_eq!(union_keys(&[], &b), b.to_vec());
219        assert_eq!(union_keys(&a, &[]), a.to_vec());
220    }
221}