Skip to main content

automation_structures/compositions/
traversal_engine.rs

1// RelationshipGraph + Budget + connective-owned TraversalEngine composition.
2//
3// This is the Rust realization of TraversalEngineFromGraphBudget.tla:
4// RelationshipGraph owns topology, Budget owns capacity accounting, Marker owns
5// per-node visited state, Accumulator owns accepted output, and Buffer owns the
6// pending frontier. Remaining budget and all public sets are projections.
7
8use vstd::prelude::*;
9
10use crate::compositions::relationship_graph::RelationshipGraph;
11use crate::connectives::accumulator::Accumulator;
12use crate::connectives::buffer::Buffer;
13use crate::connectives::marker::Marker;
14use crate::primitives::budget::Budget;
15
16verus! {
17
18/// The fixed cost used by the retained TraversalEngine model.
19pub const NODE_COST: u64 = 2;
20
21/// Number of set markers in the retained prefix.
22pub open spec fn marked_count(markers: Seq<Marker>, n: int) -> int
23    recommends 0 <= n <= markers.len(),
24    decreases n,
25{
26    if n <= 0 {
27        0
28    } else {
29        marked_count(markers, n - 1) + if markers[n - 1].marked { 1int } else { 0int }
30    }
31}
32
33/// Budgeted traversal assembled from reusable structures and connective owners.
34pub struct TraversalEngine {
35    /// Number of nodes in the fixed universe.
36    pub num_nodes: usize,
37    /// Root node admitted into the initial frontier.
38    pub root: usize,
39    /// Relationship owner.
40    pub graph: RelationshipGraph,
41    /// Traversal-cost owner.
42    pub budget: Budget,
43    /// Per-node visited markers.
44    pub visited: Vec<Marker>,
45    /// Owner of accepted traversal results.
46    pub accepted: Accumulator<usize>,
47    /// Frontier owner.
48    pub queue: Buffer<usize>,
49}
50
51impl TraversalEngine {
52    /// Whether every retained node identifier is below `num_nodes`.
53    pub open spec fn all_valid(values: Seq<usize>, num_nodes: usize) -> bool {
54        forall|index: int| 0 <= index < values.len() ==>
55            #[trigger] values[index] < num_nodes
56    }
57
58    /// Whether `node` occurs in the accepted-result owner.
59    pub open spec fn accepted_contains_spec(&self, node: usize) -> bool {
60        crate::connectives::buffer::contains_value(self.accepted.accumulated@, node)
61    }
62
63    /// Whether `node` occurs in the frontier owner.
64    pub open spec fn queue_contains_spec(&self, node: usize) -> bool {
65        crate::connectives::buffer::contains_value(self.queue.values@, node)
66    }
67
68    /// Whether the marker owner records `node` as visited.
69    pub open spec fn visited_contains_spec(&self, node: usize) -> bool {
70        node < self.visited.len() && self.visited@[node as int].marked
71    }
72
73    /// Graph edges loaded through RelationshipGraph are exactly the target star.
74    pub open spec fn full_topology(&self) -> bool {
75        forall|source: usize, target: usize|
76            source < self.num_nodes && target < self.num_nodes ==>
77                #[trigger] self.graph.edge_proj(source, target)
78                    == (source == self.root && target != self.root)
79    }
80
81    /// Whether the graph contains the required prefix of the configured topology.
82    pub open spec fn partial_topology(
83        graph: &RelationshipGraph,
84        root: usize,
85        loaded_targets: usize,
86    ) -> bool {
87        forall|source: usize, target: usize|
88            source < graph.num_nodes && target < graph.num_nodes ==>
89                #[trigger] graph.edge_proj(source, target)
90                    == (source == root && target < loaded_targets && target != root)
91    }
92
93    /// Before the root is visited, it is the only possible frontier member.
94    pub open spec fn root_frontier_gate(&self) -> bool {
95        !self.visited@[self.root as int].marked ==>
96            forall|node: usize| #[trigger] self.queue_contains_spec(node) ==> node == self.root
97    }
98
99    /// Whether every accepted node is valid and marked as visited.
100    pub open spec fn accepted_subset_visited(&self) -> bool {
101        forall|node: usize| #[trigger] self.accepted_contains_spec(node) ==>
102            node < self.num_nodes && self.visited_contains_spec(node)
103    }
104
105    /// Whether the traversal budget is safe and has no transitional holdings.
106    pub open spec fn budget_invariant(&self) -> bool {
107        &&& self.budget.safety_invariant()
108        &&& self.budget.reserved == 0
109        &&& self.budget.pending_eviction == 0
110    }
111
112    /// Whether every component owner and retained domain value is well formed.
113    pub open spec fn type_invariant(&self) -> bool {
114        &&& self.root < self.num_nodes
115        &&& self.graph.num_nodes == self.num_nodes
116        &&& self.graph.max_weight == 0
117        &&& self.graph.inv()
118        &&& self.full_topology()
119        &&& self.visited@.len() == self.num_nodes
120        &&& self.queue.well_formed()
121        &&& self.queue.capacity == self.num_nodes
122        &&& Self::all_valid(self.queue.values@, self.num_nodes)
123        &&& crate::connectives::buffer::all_distinct(self.queue.values@)
124        &&& self.accepted.well_formed()
125        &&& self.accepted.pending@.len() == 0
126        &&& Self::all_valid(self.accepted.accumulated@, self.num_nodes)
127        &&& crate::connectives::buffer::all_distinct(self.accepted.accumulated@)
128    }
129
130    /// Whether all component and cross-component traversal obligations hold.
131    pub open spec fn inv(&self) -> bool {
132        &&& self.type_invariant()
133        &&& self.budget_invariant()
134        &&& self.accepted_subset_visited()
135        &&& self.root_frontier_gate()
136    }
137
138    /// Expose the composition facts needed by checked facades and actions.
139    pub proof fn expose(&self)
140        requires self.inv(),
141        ensures
142            self.type_invariant(),
143            self.budget_invariant(),
144            self.accepted_subset_visited(),
145            self.root_frontier_gate(),
146            self.root < self.num_nodes,
147            self.visited@.len() == self.num_nodes,
148            self.full_topology(),
149            self.queue.well_formed(),
150            self.accepted.well_formed(),
151            self.accepted.pending@.len() == 0,
152            crate::connectives::buffer::all_distinct(self.queue.values@),
153            Self::all_valid(self.queue.values@, self.num_nodes),
154            crate::connectives::buffer::all_distinct(self.accepted.accumulated@),
155            Self::all_valid(self.accepted.accumulated@, self.num_nodes),
156            forall|candidate: usize|
157                #[trigger] self.accepted_contains_spec(candidate) ==>
158                    candidate < self.num_nodes && self.visited_contains_spec(candidate),
159            !self.visited@[self.root as int].marked ==>
160                forall|candidate: usize| #[trigger] self.queue_contains_spec(candidate) ==>
161                    candidate == self.root,
162    {
163        reveal(TraversalEngine::inv);
164        reveal(TraversalEngine::type_invariant);
165        reveal(TraversalEngine::accepted_subset_visited);
166        reveal(TraversalEngine::root_frontier_gate);
167    }
168
169    /// Load the target graph through RelationshipGraph, then initialize the connective owners.
170    pub fn new(num_nodes: usize, root: usize, max_budget: u64) -> (engine: TraversalEngine)
171        requires root < num_nodes,
172        ensures
173            engine.inv(),
174            engine.num_nodes == num_nodes,
175            engine.root == root,
176            engine.budget.capacity == max_budget,
177            engine.budget.allocated == 0,
178            engine.queue.values@ == seq![root],
179            engine.accepted.original@.len() == 0,
180            engine.accepted.accumulated@.len() == 0,
181            engine.accepted.pending@.len() == 0,
182            forall|node: int| 0 <= node < engine.visited@.len() ==>
183                !#[trigger] engine.visited@[node].marked,
184    {
185        let mut graph = RelationshipGraph::new(num_nodes, 0);
186        assert(Self::partial_topology(&graph, root, 0)) by {
187            assert forall|source: usize, target: usize|
188                source < graph.num_nodes && target < graph.num_nodes implies
189                    #[trigger] graph.edge_proj(source, target)
190                        == (source == root && target < 0 && target != root) by {
191                if graph.edge_proj(source, target) {
192                    let entry = choose|entry: int|
193                        0 <= entry < graph.registry.entries@.len()
194                            && graph.registry.entries@[entry].0.0 == source
195                            && graph.registry.entries@[entry].0.1 == target;
196                    assert(false);
197                }
198            }
199        }
200        let mut target: usize = 0;
201        while target < num_nodes
202            invariant
203                root < num_nodes,
204                graph.num_nodes == num_nodes,
205                graph.max_weight == 0,
206                graph.inv(),
207                target <= num_nodes,
208                Self::partial_topology(&graph, root, target),
209            decreases num_nodes - target,
210        {
211            if target != root {
212                proof {
213                    graph.exact_edge_implies_pair(root, target, 0);
214                    assert(!graph.edge_proj(root, target));
215                    assert(!graph.exact_edge(root, target, 0));
216                }
217                let added = graph.add_edge(root, target, 0);
218                assert(added);
219                let _ = added;
220            }
221            let next_target = target + 1;
222            assert(Self::partial_topology(&graph, root, next_target)) by {
223                assert forall|source: usize, destination: usize|
224                    source < graph.num_nodes && destination < graph.num_nodes implies
225                        #[trigger] graph.edge_proj(source, destination)
226                            == (source == root
227                                && destination < next_target
228                                && destination != root) by {
229                    if target == root {
230                        if destination != root {
231                            if destination < next_target {
232                                assert(destination <= target);
233                                assert(destination < target);
234                            }
235                            if destination < target {
236                                assert(destination < next_target);
237                            }
238                        }
239                    }
240                }
241            }
242            target = next_target;
243        }
244
245        let mut visited: Vec<Marker> = Vec::new();
246        let mut node: usize = 0;
247        while node < num_nodes
248            invariant
249                node <= num_nodes,
250                visited@.len() == node,
251                forall|index: int| 0 <= index < visited@.len() ==>
252                    !#[trigger] visited@[index].marked,
253            decreases num_nodes - node,
254        {
255            visited.push(Marker::new(false));
256            node = node + 1;
257        }
258
259        let budget = Budget::new(max_budget);
260        let accepted = Accumulator::from_accumulated(Vec::new());
261        let mut queue = Buffer::new(num_nodes);
262        let queued = queue.push(root);
263        let _ = queued;
264        assert(queue.values@ == seq![root]);
265        assert(crate::connectives::buffer::all_distinct(queue.values@));
266
267        let engine = TraversalEngine {
268            num_nodes,
269            root,
270            graph,
271            budget,
272            visited,
273            accepted,
274            queue,
275        };
276        assert(engine.full_topology()) by {
277            assert forall|source: usize, destination: usize|
278                source < engine.num_nodes && destination < engine.num_nodes implies
279                    #[trigger] engine.graph.edge_proj(source, destination)
280                        == (source == engine.root && destination != engine.root) by {
281            }
282        }
283        assert(engine.accepted_subset_visited());
284        assert(engine.root_frontier_gate());
285        engine
286    }
287
288    /// Remaining capacity projected from the Budget owner.
289    pub fn budget_remaining(&self) -> (remaining: u64)
290        requires self.budget_invariant(),
291        ensures remaining as int == self.budget.capacity as int - self.budget.allocated as int,
292    {
293        self.budget.available()
294    }
295
296    /// Whether the frontier currently retains `node`.
297    pub fn queue_contains(&self, node: usize) -> (present: bool)
298        ensures present == self.queue_contains_spec(node),
299    {
300        self.queue.contains(node)
301    }
302
303    /// Whether `node` has been visited.
304    pub fn visited_contains(&self, node: usize) -> (present: bool)
305        ensures present == self.visited_contains_spec(node),
306    {
307        if node >= self.visited.len() { false } else { self.visited[node].is_marked() }
308    }
309
310    /// Whether `node` was accepted into the result.
311    pub fn accepted_contains(&self, node: usize) -> (present: bool)
312        ensures present == self.accepted_contains_spec(node),
313    {
314        crate::connectives::buffer::retained_contains(&self.accepted.accumulated, node)
315    }
316
317    /// Whether visiting `node` is currently enabled.
318    pub fn can_visit(&self, node: usize) -> (enabled: bool)
319        ensures enabled == (node < self.num_nodes
320            && self.queue_contains_spec(node)
321            && !self.visited_contains_spec(node)),
322    {
323        node < self.num_nodes && self.queue_contains(node) && !self.visited_contains(node)
324    }
325
326    /// Whether skipping `node` is currently enabled.
327    pub fn can_skip(&self, node: usize) -> (enabled: bool)
328        ensures enabled == (node < self.num_nodes && self.queue_contains_spec(node)),
329    {
330        node < self.num_nodes && self.queue_contains(node)
331    }
332
333    /// Whether terminal stuttering is enabled.
334    pub fn can_terminate(&self) -> (enabled: bool)
335        ensures enabled == (self.queue.values@.len() == 0),
336    {
337        self.queue.is_empty()
338    }
339
340    /// Number of set visited markers, derived without a duplicate counter.
341    pub fn visited_count(&self) -> (count: usize)
342        ensures count as int == marked_count(self.visited@, self.visited@.len() as int),
343    {
344        let mut count: usize = 0;
345        let mut index: usize = 0;
346        while index < self.visited.len()
347            invariant
348                index <= self.visited.len(),
349                count <= index,
350                count as int == marked_count(self.visited@, index as int),
351            decreases self.visited.len() - index,
352        {
353            if self.visited[index].is_marked() {
354                count = count + 1;
355            }
356            index = index + 1;
357        }
358        count
359    }
360
361    fn enqueue_star_children(
362        graph: &RelationshipGraph,
363        queue: &mut Buffer<usize>,
364        root: usize,
365        num_nodes: usize,
366    )
367        requires
368            root < num_nodes,
369            graph.num_nodes == num_nodes,
370            graph.inv(),
371            forall|source: usize, target: usize|
372                source < num_nodes && target < num_nodes ==>
373                    #[trigger] graph.edge_proj(source, target)
374                        == (source == root && target != root),
375            old(queue).well_formed(),
376            old(queue).capacity == num_nodes,
377            old(queue).values@.len() == 0,
378        ensures
379            final(queue).well_formed(),
380            final(queue).capacity == old(queue).capacity,
381            final(queue).values@.len() == num_nodes - 1,
382            forall|index: int| 0 <= index < final(queue).values@.len() ==>
383                #[trigger] final(queue).values@[index]
384                    == if index < root as int {
385                        index as usize
386                    } else {
387                        (index + 1) as usize
388                    },
389            crate::connectives::buffer::all_distinct(final(queue).values@),
390            Self::all_valid(final(queue).values@, num_nodes),
391            forall|candidate: usize|
392                #[trigger] crate::connectives::buffer::contains_value(
393                    final(queue).values@,
394                    candidate,
395                ) == (candidate < num_nodes && candidate != root),
396    {
397        let mut target: usize = 0;
398        while target < num_nodes
399            invariant
400                root < num_nodes,
401                graph.num_nodes == num_nodes,
402                graph.inv(),
403                forall|source: usize, destination: usize|
404                    source < num_nodes && destination < num_nodes ==>
405                        #[trigger] graph.edge_proj(source, destination)
406                            == (source == root && destination != root),
407                target <= num_nodes,
408                queue.well_formed(),
409                queue.capacity == num_nodes,
410                crate::connectives::buffer::all_distinct(queue.values@),
411                Self::all_valid(queue.values@, num_nodes),
412                queue.values@.len()
413                    == target - if root < target { 1usize } else { 0usize },
414                forall|index: int| 0 <= index < queue.values@.len() ==>
415                    #[trigger] queue.values@[index]
416                        == if index < root as int {
417                            index as usize
418                        } else {
419                            (index + 1) as usize
420                        },
421                forall|candidate: usize|
422                    #[trigger] crate::connectives::buffer::contains_value(
423                        queue.values@,
424                        candidate,
425                    ) == (candidate < target && candidate != root),
426            decreases num_nodes - target,
427        {
428            let ghost before_queue = queue.values@;
429            let edge = graph.contains_pair(root, target);
430            assert(edge == (target != root));
431            if edge {
432                assert(!crate::connectives::buffer::contains_value(before_queue, target));
433                assert(queue.values@.len() < queue.capacity);
434                let queued = queue.push_unique(target);
435                assert(queued);
436                let _ = queued;
437                assert(Self::all_valid(queue.values@, num_nodes)) by {
438                    assert forall|index: int| 0 <= index < queue.values@.len()
439                        implies #[trigger] queue.values@[index] < num_nodes by {
440                        if index == before_queue.len() {
441                            assert(queue.values@[index] == target);
442                        } else {
443                            assert(index < before_queue.len());
444                            assert(queue.values@[index] == before_queue[index]);
445                        }
446                    }
447                }
448            }
449            assert(queue.values@.len()
450                == (target + 1) - if root < target + 1 { 1usize } else { 0usize });
451            assert forall|index: int| 0 <= index < queue.values@.len() implies
452                #[trigger] queue.values@[index]
453                    == if index < root as int {
454                        index as usize
455                    } else {
456                        (index + 1) as usize
457                    } by {
458                if edge && index == before_queue.len() {
459                    assert(queue.values@[index] == target);
460                    if target < root {
461                        assert(index == target as int);
462                    } else {
463                        assert(target > root);
464                        assert(index + 1 == target as int);
465                    }
466                }
467            }
468            let next_target = target + 1;
469            assert forall|candidate: usize|
470                #[trigger] crate::connectives::buffer::contains_value(
471                    queue.values@,
472                    candidate,
473                ) == (candidate < next_target && candidate != root) by {
474                if edge {
475                    crate::connectives::buffer::lemma_push_contains(
476                        before_queue,
477                        target,
478                        candidate,
479                    );
480                } else {
481                    assert(queue.values@ == before_queue);
482                }
483                if target == root && candidate != root {
484                    if candidate < next_target {
485                        assert(candidate <= target);
486                        assert(candidate < target);
487                    }
488                }
489            }
490            target = next_target;
491        }
492    }
493
494    /// Visit one queued node and atomically couple acceptance to Budget allocation.
495    pub fn visit_node(&mut self, node: usize)
496        requires
497            old(self).inv(),
498            node < old(self).num_nodes,
499            old(self).queue_contains_spec(node),
500            !old(self).visited_contains_spec(node),
501        ensures
502            final(self).inv(),
503            final(self).num_nodes == old(self).num_nodes,
504            final(self).root == old(self).root,
505            final(self).graph == old(self).graph,
506            final(self).budget.capacity == old(self).budget.capacity,
507            final(self).budget.reserved == old(self).budget.reserved,
508            final(self).budget.pending_eviction == old(self).budget.pending_eviction,
509            final(self).budget.allocated as int
510                == if old(self).budget.allocated as int + NODE_COST as int
511                        <= old(self).budget.capacity as int {
512                    old(self).budget.allocated as int + NODE_COST as int
513                } else {
514                    old(self).budget.allocated as int
515                },
516            final(self).accepted.original@ == if old(self).budget.allocated as int
517                    + NODE_COST as int <= old(self).budget.capacity as int {
518                old(self).accepted.original@.push(node)
519            } else {
520                old(self).accepted.original@
521            },
522            final(self).accepted.accumulated@ == if old(self).budget.allocated as int
523                    + NODE_COST as int <= old(self).budget.capacity as int {
524                old(self).accepted.accumulated@.push(node)
525            } else {
526                old(self).accepted.accumulated@
527            },
528            final(self).accepted.pending@ == old(self).accepted.pending@,
529            forall|candidate: usize|
530                candidate < old(self).num_nodes ==>
531                    #[trigger] final(self).visited_contains_spec(candidate)
532                        == (old(self).visited_contains_spec(candidate) || candidate == node),
533            forall|candidate: usize|
534                #[trigger] final(self).accepted_contains_spec(candidate)
535                    == (old(self).accepted_contains_spec(candidate)
536                        || (old(self).budget.allocated as int + NODE_COST as int
537                                <= old(self).budget.capacity as int
538                            && candidate == node)),
539            forall|candidate: usize|
540                #[trigger] final(self).queue_contains_spec(candidate)
541                    == if old(self).budget.allocated as int + NODE_COST as int
542                            <= old(self).budget.capacity as int
543                        && node == old(self).root {
544                        (old(self).queue_contains_spec(candidate) && candidate != node)
545                            || (candidate < old(self).num_nodes
546                                && candidate != old(self).root)
547                    } else {
548                        old(self).queue_contains_spec(candidate) && candidate != node
549                    },
550            (old(self).budget.allocated as int + NODE_COST as int
551                    <= old(self).budget.capacity as int
552                && node == old(self).root) ==> {
553                &&& final(self).queue.values@.len() == old(self).num_nodes - 1
554                &&& forall|index: int| 0 <= index < final(self).queue.values@.len() ==>
555                    #[trigger] final(self).queue.values@[index]
556                        == if index < old(self).root as int {
557                            index as usize
558                        } else {
559                            (index + 1) as usize
560                        }
561            },
562            (!(old(self).budget.allocated as int + NODE_COST as int
563                    <= old(self).budget.capacity as int
564                && node == old(self).root)) ==> exists|index: int|
565                    0 <= index < old(self).queue.values@.len()
566                        && old(self).queue.values@[index] == node
567                        && final(self).queue.values@
568                            == old(self).queue.values@.remove(index),
569            final(self).queue.capacity == old(self).queue.capacity,
570    {
571        proof { self.expose(); }
572        let num_nodes = self.num_nodes;
573        let root = self.root;
574        let initial_allocated = self.budget.allocated;
575        let budget_capacity = self.budget.capacity;
576        let _ = (initial_allocated, budget_capacity);
577        let ghost old_accepted = self.accepted.accumulated@;
578        let ghost old_visited = self.visited@;
579        let ghost old_queue = self.queue.values@;
580        proof {
581            reveal(TraversalEngine::accepted_contains_spec);
582            reveal(TraversalEngine::queue_contains_spec);
583            reveal(TraversalEngine::visited_contains_spec);
584            assert forall|candidate: usize|
585                crate::connectives::buffer::contains_value(old_accepted, candidate) implies
586                    candidate < self.num_nodes && old_visited[candidate as int].marked by {
587                assert(self.accepted_contains_spec(candidate));
588                assert(self.visited_contains_spec(candidate));
589            }
590            assert(!old_visited[node as int].marked);
591            assert(crate::connectives::buffer::contains_value(old_queue, node));
592            assert(!old_visited[root as int].marked ==> forall|candidate: usize|
593                #[trigger] crate::connectives::buffer::contains_value(old_queue, candidate)
594                    ==> candidate == root) by {
595                if !old_visited[root as int].marked {
596                    assert(!self.visited@[root as int].marked);
597                    assert forall|candidate: usize|
598                        #[trigger] crate::connectives::buffer::contains_value(
599                            old_queue,
600                            candidate,
601                        ) implies candidate == root by {
602                        assert(self.queue_contains_spec(candidate));
603                    }
604                }
605            }
606            assert(self.full_topology());
607            assert(Self::all_valid(old_queue, num_nodes));
608            assert(crate::connectives::buffer::all_distinct(old_queue));
609            assert(Self::all_valid(old_accepted, num_nodes));
610            assert(crate::connectives::buffer::all_distinct(old_accepted));
611        }
612
613        let removed = self.queue.remove_value(node);
614        assert(removed);
615        let _ = removed;
616        let ghost queue_after_removal = self.queue.values@;
617
618        let mut marker = self.visited[node];
619        let changed = marker.set();
620        assert(changed);
621        let _ = changed;
622        self.visited.set(node, marker);
623        assert(self.visited@ == old_visited.update(node as int, marker));
624        assert forall|candidate: usize| candidate < self.num_nodes implies
625            #[trigger] self.visited_contains_spec(candidate)
626                == (candidate == node || old_visited[candidate as int].marked) by {
627        }
628
629        let accepted = self.budget.try_allocate(NODE_COST);
630        assert(accepted == (initial_allocated as int + NODE_COST as int
631            <= budget_capacity as int));
632        if accepted {
633            assert(!crate::connectives::buffer::contains_value(old_accepted, node)) by {
634            if crate::connectives::buffer::contains_value(old_accepted, node) {
635                    assert(old_visited[node as int].marked);
636                }
637            }
638            self.accepted.append(node);
639            assert(self.accepted.accumulated@ == old_accepted.push(node));
640            proof {
641                crate::connectives::buffer::lemma_push_contains(old_accepted, node, node);
642                assert forall|candidate: usize|
643                    #[trigger] crate::connectives::buffer::contains_value(
644                        self.accepted.accumulated@,
645                        candidate,
646                    ) == (crate::connectives::buffer::contains_value(
647                        old_accepted,
648                        candidate,
649                    ) || candidate == node) by {
650                    crate::connectives::buffer::lemma_push_contains(
651                        old_accepted,
652                        node,
653                        candidate,
654                    );
655                }
656            }
657            assert(crate::connectives::buffer::all_distinct(self.accepted.accumulated@)) by {
658                assert forall|left: int, right: int|
659                    0 <= left < self.accepted.accumulated@.len()
660                        && 0 <= right < self.accepted.accumulated@.len()
661                        && left != right
662                    implies #[trigger] self.accepted.accumulated@[left]
663                        != #[trigger] self.accepted.accumulated@[right] by {
664                    if left < old_accepted.len() && right < old_accepted.len() {
665                    } else if left == old_accepted.len() && right < old_accepted.len() {
666                        crate::connectives::buffer::indexed_value_contained(
667                            old_accepted,
668                            right,
669                        );
670                        assert(crate::connectives::buffer::contains_value(
671                            old_accepted,
672                            old_accepted[right],
673                        ));
674                    } else if right == old_accepted.len() && left < old_accepted.len() {
675                        crate::connectives::buffer::indexed_value_contained(
676                            old_accepted,
677                            left,
678                        );
679                        assert(crate::connectives::buffer::contains_value(
680                            old_accepted,
681                            old_accepted[left],
682                        ));
683                    }
684                }
685            }
686
687            if node == root {
688                assert(self.queue.values@.len() == 0) by {
689                    if self.queue.values@.len() > 0 {
690                        let queued = self.queue.values@[0];
691                        crate::connectives::buffer::indexed_value_contained(
692                            self.queue.values@,
693                            0,
694                        );
695                        assert(self.queue_contains_spec(queued));
696                        assert(crate::connectives::buffer::contains_value(old_queue, queued));
697                        assert(queued == root);
698                        assert(!self.queue_contains_spec(root));
699                    }
700                }
701                Self::enqueue_star_children(
702                    &self.graph,
703                    &mut self.queue,
704                    root,
705                    num_nodes,
706                );
707            }
708        } else {
709            assert(self.accepted.accumulated@ == old_accepted);
710        }
711
712        assert(Self::all_valid(self.accepted.accumulated@, self.num_nodes)) by {
713            assert forall|index: int| 0 <= index < self.accepted.accumulated@.len()
714                implies #[trigger] self.accepted.accumulated@[index] < self.num_nodes by {
715                if accepted {
716                    if index == old_accepted.len() {
717                        assert(self.accepted.accumulated@[index] == node);
718                    } else {
719                        assert(index < old_accepted.len());
720                        assert(self.accepted.accumulated@[index] == old_accepted[index]);
721                    }
722                }
723            }
724        }
725        assert forall|candidate: usize|
726            #[trigger] self.accepted_contains_spec(candidate)
727                == (crate::connectives::buffer::contains_value(old_accepted, candidate)
728                    || (accepted && candidate == node)) by {
729            reveal(TraversalEngine::accepted_contains_spec);
730            if accepted {
731                crate::connectives::buffer::lemma_push_contains(
732                    old_accepted,
733                    node,
734                    candidate,
735                );
736            }
737        }
738        assert(self.accepted_subset_visited()) by {
739            assert forall|candidate: usize| #[trigger] self.accepted_contains_spec(candidate)
740                implies candidate < self.num_nodes && self.visited_contains_spec(candidate) by {
741                if accepted && candidate == node {
742                } else {
743                    assert(crate::connectives::buffer::contains_value(old_accepted, candidate));
744                    assert(old_visited[candidate as int].marked);
745                }
746            }
747        }
748        assert forall|candidate: usize|
749            #[trigger] self.queue_contains_spec(candidate)
750                == if accepted && node == root {
751                    (crate::connectives::buffer::contains_value(old_queue, candidate)
752                        && candidate != node)
753                        || (candidate < num_nodes && candidate != root)
754                } else {
755                    crate::connectives::buffer::contains_value(old_queue, candidate)
756                        && candidate != node
757                } by {
758            reveal(TraversalEngine::queue_contains_spec);
759            if accepted && node == root {
760                assert(self.queue_contains_spec(candidate)
761                    == (candidate < num_nodes && candidate != root));
762            } else {
763                assert(self.queue.values@ == queue_after_removal);
764            }
765        }
766        assert(self.visited@[root as int].marked) by {
767            if old_visited[root as int].marked {
768                if node != root {
769                    assert(self.visited@[root as int] == old_visited[root as int]);
770                }
771            } else {
772                assert(node == root) by {
773                    assert(crate::connectives::buffer::contains_value(old_queue, node));
774                }
775            }
776        }
777        assert(self.root_frontier_gate());
778        assert(self.num_nodes == num_nodes);
779        assert(self.root == root);
780        assert(Self::all_valid(self.queue.values@, self.num_nodes)) by {
781            assert forall|index: int| 0 <= index < self.queue.values@.len()
782                implies #[trigger] self.queue.values@[index] < self.num_nodes by {
783                crate::connectives::buffer::indexed_value_contained(
784                    self.queue.values@,
785                    index,
786                );
787                let candidate = self.queue.values@[index];
788                if !accepted || node != root {
789                    assert(crate::connectives::buffer::contains_value(old_queue, candidate));
790                    let old_index = choose|old_index: int|
791                        0 <= old_index < old_queue.len() && old_queue[old_index] == candidate;
792                    assert(old_queue[old_index] < num_nodes);
793                }
794            }
795        }
796        assert(self.type_invariant()) by {
797            reveal(TraversalEngine::type_invariant);
798        }
799        assert(self.budget_invariant()) by {
800            reveal(TraversalEngine::budget_invariant);
801        }
802        assert(self.inv()) by {
803            reveal(TraversalEngine::inv);
804        }
805    }
806
807    /// Remove one queued node without visiting or charging it.
808    pub fn skip(&mut self, node: usize)
809        requires
810            old(self).inv(),
811            node < old(self).num_nodes,
812            old(self).queue_contains_spec(node),
813        ensures
814            final(self).inv(),
815            final(self).num_nodes == old(self).num_nodes,
816            final(self).root == old(self).root,
817            final(self).graph == old(self).graph,
818            final(self).budget == old(self).budget,
819            final(self).visited@ == old(self).visited@,
820            final(self).accepted == old(self).accepted,
821            final(self).queue.capacity == old(self).queue.capacity,
822            exists|index: int|
823                0 <= index < old(self).queue.values@.len()
824                    && old(self).queue.values@[index] == node
825                    && final(self).queue.values@
826                        == old(self).queue.values@.remove(index),
827            forall|candidate: usize| #[trigger] final(self).queue_contains_spec(candidate)
828                == (old(self).queue_contains_spec(candidate) && candidate != node),
829    {
830        proof { self.expose(); }
831        let root = self.root;
832        let num_nodes = self.num_nodes;
833        let _ = (root, num_nodes);
834        let ghost old_queue = self.queue.values@;
835        let ghost old_visited = self.visited@;
836        let ghost old_accepted = self.accepted.accumulated@;
837        proof {
838            reveal(TraversalEngine::queue_contains_spec);
839            reveal(TraversalEngine::accepted_contains_spec);
840            reveal(TraversalEngine::visited_contains_spec);
841            assert(!old_visited[root as int].marked ==> forall|candidate: usize|
842                #[trigger] crate::connectives::buffer::contains_value(old_queue, candidate)
843                    ==> candidate == root) by {
844                if !old_visited[root as int].marked {
845                    assert(!self.visited@[root as int].marked);
846                    assert forall|candidate: usize|
847                        #[trigger] crate::connectives::buffer::contains_value(
848                            old_queue,
849                            candidate,
850                        ) implies candidate == root by {
851                        assert(self.queue_contains_spec(candidate));
852                    }
853                }
854            }
855            assert(Self::all_valid(old_queue, num_nodes));
856            assert forall|candidate: usize|
857                crate::connectives::buffer::contains_value(old_accepted, candidate) implies
858                    candidate < num_nodes && old_visited[candidate as int].marked by {
859                assert(self.accepted_contains_spec(candidate));
860                assert(self.visited_contains_spec(candidate));
861            }
862        }
863        let removed = self.queue.remove_value(node);
864        assert(removed);
865        let _ = removed;
866        assert(self.root_frontier_gate()) by {
867            if !self.visited@[root as int].marked {
868                assert forall|candidate: usize| #[trigger] self.queue_contains_spec(candidate)
869                    implies candidate == root by {
870                    assert(crate::connectives::buffer::contains_value(old_queue, candidate));
871                }
872            }
873        }
874        assert(Self::all_valid(self.queue.values@, self.num_nodes)) by {
875            assert forall|index: int| 0 <= index < self.queue.values@.len()
876                implies #[trigger] self.queue.values@[index] < self.num_nodes by {
877                crate::connectives::buffer::indexed_value_contained(
878                    self.queue.values@,
879                    index,
880                );
881                let candidate = self.queue.values@[index];
882                assert(crate::connectives::buffer::contains_value(old_queue, candidate));
883                let old_index = choose|old_index: int|
884                    0 <= old_index < old_queue.len() && old_queue[old_index] == candidate;
885                assert(old_queue[old_index] < num_nodes);
886            }
887        }
888        assert(self.type_invariant()) by {
889            reveal(TraversalEngine::type_invariant);
890        }
891        assert(self.budget_invariant());
892        assert(self.accepted.accumulated@ == old_accepted);
893        assert(self.visited@ == old_visited);
894        assert(self.accepted_subset_visited()) by {
895            assert forall|candidate: usize| #[trigger] self.accepted_contains_spec(candidate)
896                implies candidate < self.num_nodes && self.visited_contains_spec(candidate) by {
897                assert(crate::connectives::buffer::contains_value(old_accepted, candidate));
898                assert(old_visited[candidate as int].marked);
899            }
900        }
901        assert(self.inv()) by {
902            reveal(TraversalEngine::inv);
903        }
904    }
905
906    /// Enabled-at-empty traversal termination is an exact stutter.
907    pub fn terminate(&mut self)
908        requires
909            old(self).inv(),
910            old(self).queue.values@.len() == 0,
911        ensures final(self).inv(), *final(self) == *old(self),
912    {
913    }
914}
915
916}