1use vstd::prelude::*;
9
10use crate::primitives::resource_registry::ResourceRegistry;
11
12verus! {
13
14pub type EdgeKey = (usize, usize, u64);
16pub type EdgeBinding = (EdgeKey, ());
18
19pub open spec fn edge_admitted(
21 num_nodes: usize,
22 max_weight: u64,
23 source: usize,
24 target: usize,
25 weight: u64,
26) -> bool {
27 source < num_nodes && target < num_nodes && weight <= max_weight
28}
29
30pub open spec fn adjacency_admitted(
32 num_nodes: usize,
33 source: usize,
34 target: usize,
35) -> bool {
36 source < num_nodes && target < num_nodes
37}
38
39pub open spec fn adjacency_consistent(
41 adjacency_present: bool,
42 edge_present: bool,
43) -> bool {
44 crate::connectives::projection::membership_consistent(
45 adjacency_present,
46 edge_present,
47 )
48}
49
50pub open spec fn edge_irreflexive(present: bool, source: usize, target: usize) -> bool {
52 present ==> source != target
53}
54
55pub open spec fn has_edge(
57 entries: Seq<EdgeBinding>,
58 n: int,
59 source: usize,
60 target: usize,
61) -> bool {
62 exists|index: int|
63 0 <= index < n
64 && entries[index].0.0 == source
65 && entries[index].0.1 == target
66}
67
68pub open spec fn has_exact_edge(
70 entries: Seq<EdgeBinding>,
71 n: int,
72 source: usize,
73 target: usize,
74 weight: u64,
75) -> bool {
76 crate::primitives::resource_registry::has_pair(
77 entries,
78 n,
79 (source, target, weight),
80 (),
81 )
82}
83
84pub proof fn lemma_has_edge_extend(
86 entries: Seq<EdgeBinding>,
87 n: int,
88 source: usize,
89 target: usize,
90)
91 requires 0 <= n < entries.len(),
92 ensures
93 has_edge(entries, n + 1, source, target)
94 == (has_edge(entries, n, source, target)
95 || (entries[n].0.0 == source && entries[n].0.1 == target)),
96{
97 if has_edge(entries, n + 1, source, target) {
98 let index = choose|index: int|
99 0 <= index < n + 1
100 && entries[index].0.0 == source
101 && entries[index].0.1 == target;
102 assert(index < n || index == n);
103 }
104 if has_edge(entries, n, source, target) {
105 let index = choose|index: int|
106 0 <= index < n
107 && entries[index].0.0 == source
108 && entries[index].0.1 == target;
109 assert(0 <= index < n + 1);
110 }
111 if entries[n].0.0 == source && entries[n].0.1 == target {
112 assert(0 <= n < n + 1);
113 }
114}
115
116pub proof fn lemma_push_has_edge(
118 entries: Seq<(EdgeKey, ())>,
119 added: EdgeKey,
120 source: usize,
121 target: usize,
122)
123 ensures has_edge(entries.push((added, ())), entries.len() as int + 1, source, target)
124 == (has_edge(entries, entries.len() as int, source, target)
125 || (added.0 == source && added.1 == target)),
126{
127 let pushed = entries.push((added, ()));
128 if has_edge(pushed, pushed.len() as int, source, target) {
129 let index = choose|index: int|
130 0 <= index < pushed.len()
131 && pushed[index].0.0 == source
132 && pushed[index].0.1 == target;
133 if index < entries.len() {
134 assert(pushed[index] == entries[index]);
135 } else {
136 assert(index == entries.len());
137 }
138 }
139 if has_edge(entries, entries.len() as int, source, target) {
140 let index = choose|index: int|
141 0 <= index < entries.len()
142 && entries[index].0.0 == source
143 && entries[index].0.1 == target;
144 assert(pushed[index] == entries[index]);
145 }
146 if added.0 == source && added.1 == target {
147 assert(pushed[entries.len() as int].0 == added);
148 }
149}
150
151pub struct RelationshipGraph {
153 pub num_nodes: usize,
155 pub max_weight: u64,
157 pub registry: ResourceRegistry<EdgeKey, ()>,
159}
160
161impl RelationshipGraph {
162 pub open spec fn edge_proj(&self, source: usize, target: usize) -> bool {
164 has_edge(
165 self.registry.entries@,
166 self.registry.entries@.len() as int,
167 source,
168 target,
169 )
170 }
171
172 pub open spec fn exact_edge(&self, source: usize, target: usize, weight: u64) -> bool {
174 self.registry.maps_to((source, target, weight), ())
175 }
176
177 pub proof fn exact_edge_implies_pair(&self, source: usize, target: usize, weight: u64)
179 ensures self.exact_edge(source, target, weight) ==> self.edge_proj(source, target),
180 {
181 if self.exact_edge(source, target, weight) {
182 let index = choose|index: int|
183 0 <= index < self.registry.entries@.len()
184 && self.registry.entries@[index].0 == (source, target, weight)
185 && self.registry.entries@[index].1 == ();
186 assert(self.registry.entries@[index].0.0 == source);
187 assert(self.registry.entries@[index].0.1 == target);
188 }
189 }
190
191 pub open spec fn adj_proj(&self, source: usize, target: usize) -> bool {
193 self.edge_proj(source, target)
194 }
195
196 pub open spec fn type_invariant(&self) -> bool {
198 &&& self.registry.unique_mapping()
199 &&& forall|index: int|
200 #![trigger self.registry.entries@[index]]
201 0 <= index < self.registry.entries@.len() ==> edge_admitted(
202 self.num_nodes,
203 self.max_weight,
204 self.registry.entries@[index].0.0,
205 self.registry.entries@[index].0.1,
206 self.registry.entries@[index].0.2,
207 )
208 }
209
210 pub open spec fn adjacency_consistency(&self) -> bool {
212 forall|source: usize, target: usize|
213 source < self.num_nodes && target < self.num_nodes ==> adjacency_consistent(
214 #[trigger] self.adj_proj(source, target),
215 self.edge_proj(source, target),
216 )
217 }
218
219 pub open spec fn no_self_loops(&self) -> bool {
221 forall|index: int|
222 #![trigger self.registry.entries@[index]]
223 0 <= index < self.registry.entries@.len() ==> edge_irreflexive(
224 true,
225 self.registry.entries@[index].0.0,
226 self.registry.entries@[index].0.1,
227 )
228 }
229
230 pub open spec fn inv(&self) -> bool {
232 self.type_invariant() && self.adjacency_consistency() && self.no_self_loops()
233 }
234
235 pub proof fn expose_storage_facts(&self)
237 requires self.inv(),
238 ensures
239 self.registry.unique_mapping(),
240 forall|index: int| #![trigger self.registry.entries@[index]]
241 0 <= index < self.registry.entries@.len() ==>
242 self.registry.entries@[index].0.0 < self.num_nodes
243 && self.registry.entries@[index].0.1 < self.num_nodes
244 && self.registry.entries@[index].0.2 <= self.max_weight
245 && self.registry.entries@[index].0.0
246 != self.registry.entries@[index].0.1,
247 {
248 reveal(RelationshipGraph::inv);
249 reveal(RelationshipGraph::type_invariant);
250 reveal(RelationshipGraph::no_self_loops);
251 reveal(edge_admitted);
252 reveal(edge_irreflexive);
253 }
254
255 pub fn new(num_nodes: usize, max_weight: u64) -> (graph: RelationshipGraph)
257 ensures
258 graph.num_nodes == num_nodes,
259 graph.max_weight == max_weight,
260 graph.registry.entries@.len() == 0,
261 graph.inv(),
262 {
263 let registry = ResourceRegistry::new();
264 RelationshipGraph { num_nodes, max_weight, registry }
265 }
266
267 pub fn can_add_edge(&self, source: usize, target: usize, weight: u64) -> (enabled: bool)
269 ensures enabled == (source < self.num_nodes
270 && target < self.num_nodes
271 && weight <= self.max_weight
272 && source != target),
273 {
274 source < self.num_nodes
275 && target < self.num_nodes
276 && weight <= self.max_weight
277 && source != target
278 }
279
280 pub fn contains_exact_edge(
282 &self,
283 source: usize,
284 target: usize,
285 weight: u64,
286 ) -> (present: bool)
287 requires self.registry.unique_mapping(),
288 ensures present == self.exact_edge(source, target, weight),
289 {
290 match self.registry.lookup((source, target, weight)) {
291 Some(_) => true,
292 None => false,
293 }
294 }
295
296 pub fn contains_pair(&self, source: usize, target: usize) -> (present: bool)
298 ensures present == self.edge_proj(source, target),
299 {
300 let length = self.registry.entries.len();
301 let mut index: usize = 0;
302 while index < length
303 invariant
304 index <= length,
305 length == self.registry.entries.len(),
306 !has_edge(self.registry.entries@, index as int, source, target),
307 decreases length - index,
308 {
309 let key = self.registry.entries[index].0;
310 if key.0 == source && key.1 == target {
311 return true;
312 }
313 proof {
314 lemma_has_edge_extend(
315 self.registry.entries@,
316 index as int,
317 source,
318 target,
319 );
320 }
321 index = index + 1;
322 }
323 false
324 }
325
326 pub fn add_edge(
328 &mut self,
329 source: usize,
330 target: usize,
331 weight: u64,
332 ) -> (added: bool)
333 requires
334 old(self).inv(),
335 source < old(self).num_nodes,
336 target < old(self).num_nodes,
337 weight <= old(self).max_weight,
338 source != target,
339 ensures
340 final(self).inv(),
341 final(self).num_nodes == old(self).num_nodes,
342 final(self).max_weight == old(self).max_weight,
343 added == !old(self).exact_edge(source, target, weight),
344 !added ==> final(self).registry.entries@ == old(self).registry.entries@,
345 added ==> final(self).registry.entries@
346 == old(self).registry.entries@.push(((source, target, weight), ())),
347 forall|other_source: usize, other_target: usize|
348 #[trigger] final(self).edge_proj(other_source, other_target)
349 == (old(self).edge_proj(other_source, other_target)
350 || (other_source == source && other_target == target)),
351 {
352 proof { self.expose_storage_facts(); }
353 if self.contains_exact_edge(source, target, weight) {
354 return false;
355 }
356 let ghost before = self.registry.entries@;
357 self.registry.register((source, target, weight), ());
358 assert(self.registry.entries@ == before.push(((source, target, weight), ())));
359 assert forall|other_source: usize, other_target: usize|
360 #[trigger] self.edge_proj(other_source, other_target)
361 == (has_edge(before, before.len() as int, other_source, other_target)
362 || (other_source == source && other_target == target)) by {
363 lemma_push_has_edge(
364 before,
365 (source, target, weight),
366 other_source,
367 other_target,
368 );
369 }
370 assert(self.type_invariant()) by {
371 assert forall|index: int| #![trigger self.registry.entries@[index]]
372 0 <= index < self.registry.entries@.len() implies edge_admitted(
373 self.num_nodes,
374 self.max_weight,
375 self.registry.entries@[index].0.0,
376 self.registry.entries@[index].0.1,
377 self.registry.entries@[index].0.2,
378 ) by {
379 if index < before.len() {
380 assert(self.registry.entries@[index] == before[index]);
381 } else {
382 assert(index == before.len());
383 assert(self.registry.entries@[index] == ((source, target, weight), ()));
384 }
385 }
386 }
387 assert(self.no_self_loops()) by {
388 assert forall|index: int| #![trigger self.registry.entries@[index]]
389 0 <= index < self.registry.entries@.len() implies edge_irreflexive(
390 true,
391 self.registry.entries@[index].0.0,
392 self.registry.entries@[index].0.1,
393 ) by {
394 if index < before.len() {
395 assert(self.registry.entries@[index] == before[index]);
396 } else {
397 assert(index == before.len());
398 assert(self.registry.entries@[index] == ((source, target, weight), ()));
399 }
400 }
401 }
402 assert(self.adjacency_consistency()) by {
403 reveal(RelationshipGraph::adjacency_consistency);
404 reveal(RelationshipGraph::adj_proj);
405 reveal(adjacency_consistent);
406 reveal(crate::connectives::projection::membership_consistent);
407 }
408 true
409 }
410
411 pub fn remove_edge(&mut self, source: usize, target: usize)
413 requires old(self).inv(),
414 ensures
415 final(self).inv(),
416 final(self).num_nodes == old(self).num_nodes,
417 final(self).max_weight == old(self).max_weight,
418 forall|s: usize, d: usize, weight: u64|
419 #[trigger] final(self).exact_edge(s, d, weight)
420 == (!(s == source && d == target)
421 && old(self).exact_edge(s, d, weight)),
422 !final(self).edge_proj(source, target),
423 {
424 proof { self.expose_storage_facts(); }
425 let ghost original = self.registry.entries@;
426 let ghost original_num_nodes = self.num_nodes;
427 let ghost original_max_weight = self.max_weight;
428 let mut index: usize = 0;
429 while index < self.registry.entries.len()
430 invariant
431 index <= self.registry.entries.len(),
432 self.num_nodes == original_num_nodes,
433 self.max_weight == original_max_weight,
434 self.registry.unique_mapping(),
435 forall|entry: int| #![trigger self.registry.entries@[entry]]
436 0 <= entry < self.registry.entries@.len() ==>
437 self.registry.entries@[entry].0.0 < self.num_nodes
438 && self.registry.entries@[entry].0.1 < self.num_nodes
439 && self.registry.entries@[entry].0.2 <= self.max_weight
440 && self.registry.entries@[entry].0.0
441 != self.registry.entries@[entry].0.1,
442 forall|entry: int| #![trigger self.registry.entries@[entry]]
443 0 <= entry < index ==>
444 !(self.registry.entries@[entry].0.0 == source
445 && self.registry.entries@[entry].0.1 == target),
446 forall|s: usize, d: usize, weight: u64|
447 !(s == source && d == target) ==>
448 (#[trigger] has_exact_edge(
449 self.registry.entries@,
450 self.registry.entries@.len() as int,
451 s,
452 d,
453 weight,
454 ) == has_exact_edge(
455 original,
456 original.len() as int,
457 s,
458 d,
459 weight,
460 )),
461 decreases self.registry.entries.len() - index,
462 {
463 let key = self.registry.entries[index].0;
464 if key.0 == source && key.1 == target {
465 let ghost before = self.registry.entries@;
466 let _removed = self.registry.deregister_at(index);
467 assert(_removed.0 == key);
468 assert forall|entry: int| #![trigger self.registry.entries@[entry]]
469 0 <= entry < self.registry.entries@.len() implies
470 self.registry.entries@[entry].0.0 < self.num_nodes
471 && self.registry.entries@[entry].0.1 < self.num_nodes
472 && self.registry.entries@[entry].0.2 <= self.max_weight
473 && self.registry.entries@[entry].0.0
474 != self.registry.entries@[entry].0.1 by {
475 before.remove_ensures(index as int);
476 let old_entry = if entry < index { entry } else { entry + 1 };
477 assert(0 <= old_entry < before.len());
478 assert(self.registry.entries@[entry] == before[old_entry]);
479 }
480 assert forall|entry: int| #![trigger self.registry.entries@[entry]]
481 0 <= entry < index implies
482 !(self.registry.entries@[entry].0.0 == source
483 && self.registry.entries@[entry].0.1 == target) by {
484 before.remove_ensures(index as int);
485 assert(self.registry.entries@[entry] == before[entry]);
486 }
487 assert forall|s: usize, d: usize, weight: u64|
488 !(s == source && d == target) implies
489 (#[trigger] has_exact_edge(
490 self.registry.entries@,
491 self.registry.entries@.len() as int,
492 s,
493 d,
494 weight,
495 ) == has_exact_edge(
496 original,
497 original.len() as int,
498 s,
499 d,
500 weight,
501 )) by {
502 assert((s, d, weight) != key);
503 assert(self.registry.maps_to((s, d, weight), ()) ==
504 (has_exact_edge(
505 before,
506 before.len() as int,
507 s,
508 d,
509 weight,
510 ) && (s, d, weight) != key));
511 assert(has_exact_edge(
512 before,
513 before.len() as int,
514 s,
515 d,
516 weight,
517 ) == has_exact_edge(
518 original,
519 original.len() as int,
520 s,
521 d,
522 weight,
523 ));
524 }
525 } else {
526 index = index + 1;
527 }
528 }
529 assert(!self.edge_proj(source, target)) by {
530 if self.edge_proj(source, target) {
531 let entry = choose|entry: int|
532 0 <= entry < self.registry.entries@.len()
533 && self.registry.entries@[entry].0.0 == source
534 && self.registry.entries@[entry].0.1 == target;
535 assert(false);
536 }
537 }
538 assert(self.type_invariant());
539 assert(self.no_self_loops());
540 assert(self.adjacency_consistency()) by {
541 reveal(RelationshipGraph::adjacency_consistency);
542 reveal(RelationshipGraph::adj_proj);
543 reveal(adjacency_consistent);
544 reveal(crate::connectives::projection::membership_consistent);
545 }
546 assert forall|s: usize, d: usize, weight: u64|
547 #[trigger] self.exact_edge(s, d, weight)
548 == (!(s == source && d == target)
549 && old(self).exact_edge(s, d, weight)) by {
550 if s == source && d == target {
551 if self.exact_edge(s, d, weight) {
552 let entry = choose|entry: int|
553 0 <= entry < self.registry.entries@.len()
554 && self.registry.entries@[entry].0 == (s, d, weight)
555 && self.registry.entries@[entry].1 == ();
556 assert(has_edge(
557 self.registry.entries@,
558 self.registry.entries@.len() as int,
559 source,
560 target,
561 ));
562 assert(self.edge_proj(source, target));
563 }
564 } else {
565 assert(self.exact_edge(s, d, weight) == has_exact_edge(
566 self.registry.entries@,
567 self.registry.entries@.len() as int,
568 s,
569 d,
570 weight,
571 ));
572 assert(old(self).exact_edge(s, d, weight) == has_exact_edge(
573 original,
574 original.len() as int,
575 s,
576 d,
577 weight,
578 ));
579 }
580 }
581 }
582}
583
584}