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