use vstd::prelude::*;
verus! {
pub open spec fn cost_sum_to(entries: Seq<(u64, u64)>, n: int) -> int
decreases n,
{
if n <= 0 || n > entries.len() {
0
} else {
entries[n - 1].1 as int + cost_sum_to(entries, n - 1)
}
}
proof fn cost_sum_push_prefix(entries: Seq<(u64, u64)>, entry: (u64, u64), n: int)
requires 0 <= n <= entries.len(),
ensures cost_sum_to(entries.push(entry), n) == cost_sum_to(entries, n),
decreases n,
{
if n > 0 {
cost_sum_push_prefix(entries, entry, n - 1);
assert(entries.push(entry)[n - 1] == entries[n - 1]);
}
}
pub proof fn cost_sum_push(entries: Seq<(u64, u64)>, key: u64, cost: u64)
ensures
cost_sum_to(entries.push((key, cost)), entries.len() as int + 1)
== cost_sum_to(entries, entries.len() as int) + cost as int,
{
cost_sum_push_prefix(entries, (key, cost), entries.len() as int);
assert(entries.push((key, cost))[entries.len() as int].1 == cost);
}
pub struct AllocationSnapshot {
pub num_nodes: u64,
pub registry: crate::primitives::resource_registry::ResourceRegistry<u64, u64>,
pub budget: crate::primitives::budget::Budget,
}
impl AllocationSnapshot {
pub open spec fn accepted_subset_nodes(&self) -> bool {
forall|i: int|
0 <= i < self.registry.entries.len()
==> #[trigger] self.registry.entries@[i].0 < self.num_nodes
}
pub open spec fn accepted_distinct(&self) -> bool {
self.registry.unique_mapping()
}
pub open spec fn costs_valid(&self) -> bool {
forall|i: int| 0 <= i < self.registry.entries.len()
==> #[trigger] self.registry.entries@[i].1 > 0
}
pub open spec fn type_invariant(&self) -> bool {
self.accepted_subset_nodes() && self.accepted_distinct() && self.costs_valid()
}
pub open spec fn budget_consistency(&self) -> bool {
&&& self.budget.safety_invariant()
&&& self.budget.reserved == 0
&&& self.budget.pending_eviction == 0
&&& self.budget.allocated as int
== cost_sum_to(self.registry.entries@, self.registry.entries.len() as int)
}
pub open spec fn contains(&self, n: u64) -> bool {
self.registry.contains_key(n)
}
pub fn new(capacity: u64, num_nodes: u64) -> (s: AllocationSnapshot)
ensures
s.num_nodes == num_nodes,
s.registry.entries@.len() == 0,
s.budget.capacity == capacity,
s.budget.allocated == 0,
s.type_invariant(),
s.budget_consistency(),
{
AllocationSnapshot {
num_nodes,
registry: crate::primitives::resource_registry::ResourceRegistry::new(),
budget: crate::primitives::budget::Budget::new(capacity),
}
}
pub fn contains_exec(&self, n: u64) -> (b: bool)
requires self.registry.unique_mapping(),
ensures
b == self.contains(n),
{
match self.registry.lookup(n) {
Some(_) => true,
None => false,
}
}
pub fn accept_node(&mut self, n: u64, node_cost: u64)
requires
old(self).type_invariant(),
old(self).budget_consistency(),
n < old(self).num_nodes, !old(self).contains(n), 1 <= node_cost, old(self).budget.used() + node_cost as int <= old(self).budget.capacity as int,
ensures
final(self).num_nodes == old(self).num_nodes,
final(self).registry.entries@
== old(self).registry.entries@.push((n, node_cost)),
final(self).budget.capacity == old(self).budget.capacity,
final(self).budget.allocated == old(self).budget.allocated + node_cost,
final(self).contains(n),
final(self).type_invariant(),
final(self).budget_consistency(),
{
let _accepted = self.budget.try_allocate(node_cost);
assert(_accepted);
let ghost prior_entries = self.registry.entries@;
self.registry.register(n, node_cost);
proof { cost_sum_push(prior_entries, n, node_cost); }
assert(self.contains(n)) by {
assert(self.registry.maps_to(n, node_cost));
}
}
}
pub fn capture(capacity: u64, num_nodes: u64, nodes: &[u64], costs: &[u64])
-> (s: AllocationSnapshot)
requires
nodes@.len() == costs@.len(),
ensures
s.budget.capacity == capacity,
s.num_nodes == num_nodes,
s.type_invariant(),
s.budget_consistency(),
{
let mut s = AllocationSnapshot::new(capacity, num_nodes);
let n_reqs = nodes.len();
let mut i: usize = 0;
while i < n_reqs
invariant
i <= n_reqs,
n_reqs == nodes@.len(),
nodes@.len() == costs@.len(),
s.budget.capacity == capacity,
s.num_nodes == num_nodes,
s.type_invariant(),
s.budget_consistency(),
decreases n_reqs - i,
{
let n = nodes[i];
let c = costs[i];
let available = s.budget.available();
if n < num_nodes && 1 <= c && c <= available && !s.contains_exec(n) {
s.accept_node(n, c);
}
i = i + 1;
}
s
}
}