1#[allow(unused_imports)]
8use vstd::prelude::*;
9
10verus! {
11
12broadcast use vstd::seq_lib::lemma_seq_contains_after_push;
13
14pub open spec fn edge(g: Seq<Vec<usize>>, u: usize, w: usize) -> bool {
16 u < g.len() && g[u as int]@.contains(w)
17}
18
19pub open spec fn wf(g: Seq<Vec<usize>>) -> bool {
21 forall|u: int, k: int| 0 <= u < g.len() && 0 <= k < g[u]@.len() ==> (#[trigger] g[u]@[k]) < g.len()
22}
23
24pub open spec fn is_path(g: Seq<Vec<usize>>, p: Seq<usize>) -> bool {
26 &&& p.len() >= 1
27 &&& forall|i: int| 0 <= i < p.len() - 1 ==> #[trigger] edge(g, p[i], p[i + 1])
28}
29
30pub open spec fn reachable(g: Seq<Vec<usize>>, roots: Seq<usize>, v: usize) -> bool {
32 exists|p: Seq<usize>| #[trigger] is_path(g, p) && roots.contains(p[0]) && p.last() == v
33}
34
35pub open spec fn is_cycle(g: Seq<Vec<usize>>, c: Seq<usize>) -> bool {
37 is_path(g, c) && edge(g, c.last(), c[0])
38}
39
40pub open spec fn has_reachable_cycle(g: Seq<Vec<usize>>, roots: Seq<usize>) -> bool {
42 exists|c: Seq<usize>| #[trigger] is_cycle(g, c) && reachable(g, roots, c[0])
43}
44
45pub open spec fn topological(g: Seq<Vec<usize>>, order: Seq<usize>) -> bool {
47 &&& order.no_duplicates()
48 &&& forall|i: int, w: usize|
49 0 <= i < order.len() && #[trigger] edge(g, order[i], w) ==> order.subrange(0, i).contains(w)
50}
51
52pub open spec fn unseen(st: Seq<u8>) -> nat
54 decreases st.len(),
55{
56 if st.len() == 0 {
57 0
58 } else {
59 unseen(st.drop_last()) + if st.last() == 0 {
60 1nat
61 } else {
62 0nat
63 }
64 }
65}
66
67proof fn lemma_unseen(a: Seq<u8>, b: Seq<u8>, k: int)
70 requires
71 a.len() == b.len(),
72 forall|u: int| 0 <= u < a.len() && b[u] == 0 ==> a[u] == 0,
73 ensures
74 unseen(b) <= unseen(a),
75 0 <= k < a.len() && a[k] == 0 && b[k] != 0 ==> unseen(b) < unseen(a),
76 decreases a.len(),
77{
78 if a.len() > 0 {
79 let (a0, b0) = (a.drop_last(), b.drop_last());
80 assert forall|u: int| 0 <= u < a0.len() && b0[u] == 0 implies a0[u] == 0 by {
81 assert(a0[u] == a[u] && b0[u] == b[u]);
82 }
83 lemma_unseen(a0, b0, k);
84 }
85}
86
87pub open spec fn inv(
91 g: Seq<Vec<usize>>,
92 roots: Seq<usize>,
93 st: Seq<u8>,
94 order: Seq<usize>,
95 stack: Seq<usize>,
96) -> bool {
97 &&& st.len() == g.len()
98 &&& forall|u: int| 0 <= u < st.len() ==> #[trigger] st[u] <= 2
99 &&& forall|u: usize| u < st.len() ==> (st[u as int] == 2 <==> #[trigger] order.contains(u))
100 &&& forall|u: usize| u < st.len() ==> (st[u as int] == 1 <==> #[trigger] stack.contains(u))
101 &&& forall|k: int| 0 <= k < order.len() ==> #[trigger] order[k] < g.len()
102 &&& forall|k: int| 0 <= k < stack.len() ==> #[trigger] stack[k] < g.len()
103 &&& topological(g, order)
104 &&& forall|k: int| 0 <= k < order.len() ==> reachable(g, roots, #[trigger] order[k])
105 &&& stack.len() > 0 ==> is_path(g, stack) && roots.contains(stack[0])
106}
107
108proof fn lemma_back_edge(g: Seq<Vec<usize>>, roots: Seq<usize>, stk: Seq<usize>, w: usize)
110 requires
111 is_path(g, stk),
112 roots.contains(stk[0]),
113 stk.contains(w),
114 edge(g, stk.last(), w),
115 ensures
116 has_reachable_cycle(g, roots),
117{
118 let k = choose|k: int| 0 <= k < stk.len() && stk[k] == w;
119 let c = stk.subrange(k, stk.len() as int);
120 assert forall|i: int| 0 <= i < c.len() - 1 implies #[trigger] edge(g, c[i], c[i + 1]) by {
121 assert(edge(g, stk[k + i], stk[k + i + 1]));
122 }
123 assert(c.last() == stk.last());
124 assert(is_cycle(g, c));
125 let p = stk.subrange(0, k + 1);
126 assert forall|i: int| 0 <= i < p.len() - 1 implies #[trigger] edge(g, p[i], p[i + 1]) by {
127 assert(edge(g, stk[i], stk[i + 1]));
128 }
129 assert(is_path(g, p) && roots.contains(p[0]) && p.last() == c[0]);
130}
131
132proof fn lemma_stack_reachable(g: Seq<Vec<usize>>, roots: Seq<usize>, stk: Seq<usize>)
134 requires
135 is_path(g, stk),
136 roots.contains(stk[0]),
137 ensures
138 reachable(g, roots, stk.last()),
139{
140}
141
142fn visit(
143 g: &Vec<Vec<usize>>,
144 Ghost(roots): Ghost<Seq<usize>>,
145 v: usize,
146 st: &mut Vec<u8>,
147 order: &mut Vec<usize>,
148 Ghost(stack): Ghost<Seq<usize>>,
149 Ghost(fuel): Ghost<nat>,
150) -> (r: Result<(), usize>)
151 requires
152 wf(g@),
153 v < g.len(),
154 inv(g@, roots, old(st)@, old(order)@, stack),
155 old(st)@[v as int] == 0,
156 stack.len() == 0 ==> roots.contains(v),
157 stack.len() > 0 ==> edge(g@, stack.last(), v),
158 fuel == unseen(old(st)@),
159 ensures
160 r is Ok ==> {
161 &&& inv(g@, roots, final(st)@, final(order)@, stack)
162 &&& final(st)@[v as int] == 2
163 &&& forall|u: int| 0 <= u < final(st)@.len() && final(st)@[u] == 0 ==> old(st)@[u] == 0
164 &&& forall|u: int| 0 <= u < final(st)@.len() && old(st)@[u] == 2 ==> final(st)@[u] == 2
165 },
166 r is Err ==> has_reachable_cycle(g@, roots),
167 decreases fuel,
168{
169 let ghost st0 = st@;
170 let ghost stk = stack.push(v);
171 st.set(v, 1);
172 proof {
173 lemma_unseen(st0, st@, v as int);
174 assert(!stack.contains(v));
175 assert forall|i: int| 0 <= i < stk.len() - 1 implies #[trigger] edge(g@, stk[i], stk[i + 1]) by {
176 if i < stack.len() - 1 {
177 assert(edge(g@, stack[i], stack[i + 1]));
178 }
179 }
180 assert(stk[0] == if stack.len() == 0 { v } else { stack[0] });
181 assert forall|u: usize| u < st.len() implies (st@[u as int] == 2 <==> #[trigger] order@.contains(u)) by {
182 if u == v {
183 assert(!order@.contains(v));
184 }
185 }
186 }
187 let n = g[v].len();
188 let mut i = 0;
189 while i < n
190 invariant
191 wf(g@),
192 v < g.len(),
193 n == g@[v as int]@.len(),
194 0 <= i <= n,
195 stk == stack.push(v),
196 !stack.contains(v),
197 is_path(g@, stk),
198 roots.contains(stk[0]),
199 inv(g@, roots, st@, order@, stk),
200 st@[v as int] == 1,
201 unseen(st@) < fuel,
202 forall|k: int| 0 <= k < i ==> st@[#[trigger] g@[v as int]@[k] as int] == 2,
203 forall|u: int| 0 <= u < st.len() && #[trigger] st@[u] == 0 ==> st0[u] == 0,
204 forall|u: int| 0 <= u < st.len() && #[trigger] st0[u] == 2 ==> st@[u] == 2,
205 decreases n - i,
206 {
207 let w = g[v][i];
208 assert(edge(g@, v, w)) by {
209 assert(g@[v as int]@[i as int] == w);
210 }
211 if st[w] == 1 {
212 proof {
213 assert(stk.contains(w));
214 lemma_back_edge(g@, roots, stk, w);
215 }
216 return Err(w);
217 }
218 if st[w] == 0 {
219 let ghost st1 = st@;
220 let ghost fuel1 = unseen(st@);
221 let r = visit(g, Ghost(roots), w, st, order, Ghost(stk), Ghost(fuel1));
222 r?;
223 proof {
224 lemma_unseen(st1, st@, 0);
225 assert(stk.contains(v));
226 assert forall|k: int| 0 <= k < i implies st@[#[trigger] g@[v as int]@[k] as int] == 2 by {
227 assert(st1[g@[v as int]@[k] as int] == 2);
228 }
229 assert forall|u: int| 0 <= u < st.len() && #[trigger] st@[u] == 0 implies st0[u] == 0 by {
230 assert(st1[u] == 0);
231 }
232 assert forall|u: int| 0 <= u < st.len() && #[trigger] st0[u] == 2 implies st@[u] == 2 by {
233 assert(st1[u] == 2);
234 }
235 }
236 }
237 assert(st@[g@[v as int]@[i as int] as int] == 2);
238 i += 1;
239 }
240 let ghost order0 = order@;
241 let ghost st2 = st@;
242 st.set(v, 2);
243 order.push(v);
244 proof {
245 lemma_stack_reachable(g@, roots, stk);
246 assert(!order0.contains(v));
247 assert forall|k: int| 0 <= k < order@.len() implies reachable(g@, roots, #[trigger] order@[k]) by {
248 if k < order0.len() {
249 assert(order@[k] == order0[k]);
250 }
251 }
252 assert forall|k: int| 0 <= k < order@.len() implies #[trigger] order@[k] < g.len() by {
253 if k < order0.len() {
254 assert(order@[k] == order0[k]);
255 }
256 }
257 assert forall|u: usize| u < st.len() implies (st@[u as int] == 2 <==> #[trigger] order@.contains(u)) by {
258 if u != v {
259 assert(st@[u as int] == st2[u as int]);
260 }
261 }
262 assert forall|u: usize| u < st.len() implies (st@[u as int] == 1 <==> #[trigger] stack.contains(u)) by {
263 if u != v {
264 assert(st@[u as int] == st2[u as int]);
265 assert(stk.contains(u) <==> stack.contains(u));
266 }
267 }
268 assert(order@.no_duplicates()) by {
269 assert forall|a: int, b: int| 0 <= a < b < order@.len() implies order@[a] != order@[b] by {
270 if b == order0.len() {
271 assert(order@[a] == order0[a]);
272 assert(order0.contains(order0[a]));
273 }
274 }
275 }
276 assert forall|j: int, x: usize|
277 0 <= j < order@.len() && #[trigger] edge(g@, order@[j], x) implies order@.subrange(0, j).contains(x) by {
278 if j < order0.len() {
279 assert(order@[j] == order0[j]);
280 assert(order@.subrange(0, j) =~= order0.subrange(0, j));
281 } else {
282 let k = choose|k: int| 0 <= k < g@[v as int]@.len() && g@[v as int]@[k] == x;
283 assert(st2[x as int] == 2);
284 assert(order@.subrange(0, j) =~= order0);
285 }
286 }
287 if stack.len() > 0 {
288 assert forall|i: int| 0 <= i < stack.len() - 1 implies #[trigger] edge(g@, stack[i], stack[i + 1]) by {
289 assert(edge(g@, stk[i], stk[i + 1]));
290 }
291 assert(stack[0] == stk[0]);
292 }
293 assert forall|k: int| 0 <= k < stack.len() implies #[trigger] stack[k] < g.len() by {
294 assert(stack[k] == stk[k]);
295 }
296 }
297 Ok(())
298}
299
300proof fn lemma_closed_path(g: Seq<Vec<usize>>, order: Seq<usize>, p: Seq<usize>, k: int)
302 requires
303 topological(g, order),
304 is_path(g, p),
305 order.contains(p[0]),
306 0 <= k < p.len(),
307 ensures
308 order.contains(p[k]),
309 decreases k,
310{
311 if k > 0 {
312 lemma_closed_path(g, order, p, k - 1);
313 let i = choose|i: int| 0 <= i < order.len() && order[i] == p[k - 1];
314 let h = k - 1;
315 assert(edge(g, p[h], p[h + 1]));
316 assert(edge(g, order[i], p[k]));
317 let j = choose|j: int| 0 <= j < i && order.subrange(0, i)[j] == p[k];
318 assert(order[j] == p[k]);
319 }
320}
321
322spec fn pos(order: Seq<usize>, x: usize) -> int {
324 choose|i: int| 0 <= i < order.len() && order[i] == x
325}
326
327proof fn lemma_edge_pos(g: Seq<Vec<usize>>, order: Seq<usize>, a: usize, b: usize)
329 requires
330 topological(g, order),
331 order.contains(a),
332 edge(g, a, b),
333 ensures
334 order.contains(b),
335 pos(order, b) < pos(order, a),
336{
337 let i = pos(order, a);
338 assert(edge(g, order[i], b));
339 let j = choose|j: int| 0 <= j < i && order.subrange(0, i)[j] == b;
340 assert(order[j] == b);
341 let pb = pos(order, b);
342 assert(order[pb] == b);
343 assert(pb == j);
344}
345
346proof fn lemma_path_pos(g: Seq<Vec<usize>>, order: Seq<usize>, c: Seq<usize>, k: int)
348 requires
349 topological(g, order),
350 is_path(g, c),
351 order.contains(c[0]),
352 0 < k < c.len(),
353 ensures
354 order.contains(c[k]),
355 pos(order, c[k]) < pos(order, c[0]),
356 decreases k,
357{
358 lemma_closed_path(g, order, c, k - 1);
359 let h = k - 1;
360 assert(edge(g, c[h], c[h + 1]));
361 lemma_edge_pos(g, order, c[k - 1], c[k]);
362 if k > 1 {
363 lemma_path_pos(g, order, c, k - 1);
364 }
365}
366
367proof fn lemma_no_cycle(g: Seq<Vec<usize>>, roots: Seq<usize>, order: Seq<usize>)
370 requires
371 topological(g, order),
372 forall|v: usize| #[trigger] reachable(g, roots, v) ==> order.contains(v),
373 ensures
374 !has_reachable_cycle(g, roots),
375{
376 if has_reachable_cycle(g, roots) {
377 let c = choose|c: Seq<usize>| #[trigger] is_cycle(g, c) && reachable(g, roots, c[0]);
378 assert(order.contains(c[0]));
379 if c.len() > 1 {
380 lemma_path_pos(g, order, c, c.len() - 1);
381 }
382 lemma_edge_pos(g, order, c.last(), c[0]);
383 }
384}
385
386pub fn build_order(g: &Vec<Vec<usize>>, roots: &[usize]) -> (r: Result<Vec<usize>, usize>)
391 requires
392 wf(g@),
393 forall|k: int| 0 <= k < roots.len() ==> #[trigger] roots[k] < g.len(),
394 ensures
395 r is Err <==> has_reachable_cycle(g@, roots@),
396 r matches Ok(order) ==> {
397 &&& topological(g@, order@)
398 &&& forall|v: usize| v < g.len() ==> (#[trigger] order@.contains(v) <==> reachable(g@, roots@, v))
399 &&& forall|k: int| 0 <= k < order.len() ==> #[trigger] order@[k] < g.len()
400 },
401{
402 let n = g.len();
403 let mut st: Vec<u8> = Vec::with_capacity(n);
404 while st.len() < n
405 invariant
406 st.len() <= n,
407 forall|u: int| 0 <= u < st.len() ==> st@[u] == 0,
408 decreases n - st.len(),
409 {
410 st.push(0);
411 }
412 let mut order: Vec<usize> = Vec::new();
413 let ghost empty = Seq::<usize>::empty();
414 let mut t = 0;
415 while t < roots.len()
416 invariant
417 wf(g@),
418 n == g.len(),
419 forall|k: int| 0 <= k < roots.len() ==> #[trigger] roots[k] < g.len(),
420 0 <= t <= roots.len(),
421 empty == Seq::<usize>::empty(),
422 inv(g@, roots@, st@, order@, empty),
423 forall|k: int| 0 <= k < t ==> st@[#[trigger] roots@[k] as int] == 2,
424 decreases roots.len() - t,
425 {
426 let v = roots[t];
427 if st[v] == 0 {
428 assert(roots@.contains(v)) by {
429 assert(roots@[t as int] == v);
430 }
431 let ghost fuel = unseen(st@);
432 let r = visit(g, Ghost(roots@), v, &mut st, &mut order, Ghost(empty), Ghost(fuel));
433 r?;
434 } else {
435 assert(!empty.contains(v));
436 }
437 assert(st@[roots@[t as int] as int] == 2);
438 t += 1;
439 }
440 proof {
441 assert forall|v: usize| #[trigger] reachable(g@, roots@, v) implies order@.contains(v) by {
442 let p = choose|p: Seq<usize>| #[trigger] is_path(g@, p) && roots@.contains(p[0]) && p.last() == v;
443 let k = choose|k: int| 0 <= k < roots.len() && roots@[k] == p[0];
444 assert(st@[roots@[k] as int] == 2);
445 lemma_closed_path(g@, order@, p, p.len() - 1);
446 }
447 lemma_no_cycle(g@, roots@, order@);
448 assert forall|v: usize| v < g.len() implies (#[trigger] order@.contains(v) <==> reachable(g@, roots@, v)) by {
449 if order@.contains(v) {
450 let k = choose|k: int| 0 <= k < order.len() && order@[k] == v;
451 assert(reachable(g@, roots@, order@[k]));
452 }
453 }
454 }
455 Ok(order)
456}
457
458} #[cfg(test)]
461#[cfg_attr(coverage_nightly, coverage(off))]
462mod tests {
463 use super::*;
464
465 fn reference(g: &[Vec<usize>], roots: &[usize]) -> Result<Vec<usize>, usize> {
468 fn go(
469 g: &[Vec<usize>],
470 v: usize,
471 order: &mut Vec<usize>,
472 done: &mut [bool],
473 on: &mut [bool],
474 ) -> Result<(), usize> {
475 if done[v] {
476 return Ok(());
477 }
478 if on[v] {
479 return Err(v);
480 }
481 on[v] = true;
482 for &w in &g[v] {
483 go(g, w, order, done, on)?;
484 }
485 on[v] = false;
486 done[v] = true;
487 order.push(v);
488 Ok(())
489 }
490 let (mut order, mut done, mut on) = (vec![], vec![false; g.len()], vec![false; g.len()]);
491 for &r in roots {
492 go(g, r, &mut order, &mut done, &mut on)?;
493 }
494 Ok(order)
495 }
496
497 #[test]
498 fn orders_and_rejects_cycles() {
499 let g = vec![vec![1, 2], vec![2], vec![], vec![0]];
500 assert_eq!(build_order(&g, &[0]), Ok(vec![2, 1, 0]));
501 assert_eq!(build_order(&g, &[3, 0]), Ok(vec![2, 1, 0, 3]));
502 assert_eq!(build_order(&g, &[]), Ok(vec![]));
503 let cyc = vec![vec![1], vec![0], vec![]];
504 assert_eq!(build_order(&cyc, &[2]), Ok(vec![2]));
505 assert_eq!(build_order(&cyc, &[0]), Err(0));
506 }
507
508 #[test]
511 fn matches_reference_dfs() {
512 let mut seed = 0x2545_f491_4f6c_dd1du64;
513 let mut next = |m: usize| {
514 seed ^= seed << 13;
515 seed ^= seed >> 7;
516 seed ^= seed << 17;
517 (seed % m as u64) as usize
518 };
519 for _ in 0..20_000 {
520 let n = 1 + next(8);
521 let mut g: Vec<Vec<usize>> = vec![Vec::new(); n];
522 for (v, deps) in g.iter_mut().enumerate() {
523 for _ in 0..next(4) {
524 let w = next(n);
525 if w != v || next(4) == 0 {
526 deps.push(w);
527 }
528 }
529 }
530 let roots: Vec<usize> = (0..next(4)).map(|_| next(n)).collect();
531 assert_eq!(
532 build_order(&g, &roots),
533 reference(&g, &roots),
534 "{g:?} {roots:?}"
535 );
536 }
537 }
538}