extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
#[cfg(creusot)]
use ::creusot_std::macros::variant;
#[cfg(creusot)]
use ::creusot_std::prelude::{Int, Invariant};
#[cfg_attr(not(creusot), derive(Debug, Clone, PartialEq, Eq))]
pub struct PureRemint {
pub minted: Vec<(u32, u64)>,
pub live: Vec<(u32, u64)>,
}
struct StationTallies {
positions: BTreeMap<u32, usize>,
live: Vec<(u32, u64)>,
}
#[cfg(creusot)]
impl Invariant for StationTallies {
#[::creusot_std::macros::logic(opaque)]
fn invariant(self) -> bool {
false
}
}
impl StationTallies {
#[cfg_attr(creusot, ::creusot_std::macros::trusted)]
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(result.invariant()))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(result.live@.len() == 0))]
const fn new() -> Self {
Self {
positions: BTreeMap::new(),
live: Vec::new(),
}
}
#[cfg_attr(creusot, ::creusot_std::macros::trusted)]
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires((*self).invariant()))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures((^self).invariant()))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<p: Int, q: Int> 0 <= p && p < q && q < (*self).live@.len()
==> (*self).live@[p].0@ != (*self).live@[q].0@
))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<q: Int> 0 <= q && q < (*self).live@.len()
==> 1 <= (*self).live@[q].1@ && (*self).live@[q].1@ < 18446744073709551615
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
((^self).live@.len() == (*self).live@.len()
&& result@ >= 2
&& exists<q: Int> 0 <= q && q < (*self).live@.len()
&& (*self).live@[q].0@ == station@
&& result@ == (*self).live@[q].1@ + 1
&& (^self).live@[q].0@ == station@
&& (^self).live@[q].1@ == result@
&& forall<r: Int> 0 <= r && r < (*self).live@.len() && r != q
==> (^self).live@[r] == (*self).live@[r])
|| ((^self).live@.len() == (*self).live@.len() + 1
&& result@ == 1
&& (^self).live@[(*self).live@.len()].0@ == station@
&& (^self).live@[(*self).live@.len()].1@ == 1
&& (forall<q: Int> 0 <= q && q < (*self).live@.len()
==> (*self).live@[q].0@ != station@)
&& forall<q: Int> 0 <= q && q < (*self).live@.len()
==> (^self).live@[q] == (*self).live@[q])
))]
fn mint(&mut self, station: u32) -> u64 {
if let Some(&position) = self.positions.get(&station) {
debug_assert_eq!(self.live[position].0, station);
let next = self.live[position].1 + 1;
self.live[position].1 = next;
next
} else {
let position = self.live.len();
let old = self.positions.insert(station, position);
debug_assert!(old.is_none());
self.live.push((station, 1));
1
}
}
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<p: Int, q: Int> 0 <= p && p < q && q < (*tallies).live@.len()
==> (*tallies).live@[p].0@ != (*tallies).live@[q].0@
))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<q: Int> 0 <= q && q < (*tallies).live@.len()
==> 1 <= (*tallies).live@[q].1@ && (*tallies).live@[q].1@ < 18446744073709551615
))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<j: Int> 0 <= j && j < minted@.len() && 2 <= minted@[j].1@
==> exists<j2: Int> 0 <= j2 && j2 < j
&& minted@[j2].0@ == minted@[j].0@
&& minted@[j2].1@ == minted@[j].1@ - 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<q: Int> 0 <= q && q < (*tallies).live@.len()
==> exists<j: Int> 0 <= j && j < minted@.len()
&& minted@[j].0@ == (*tallies).live@[q].0@
&& minted@[j].1@ == (*tallies).live@[q].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::requires(
forall<j: Int> 0 <= j && j < minted@.len()
==> exists<q: Int> 0 <= q && q < (*tallies).live@.len()
&& (*tallies).live@[q].0@ == minted@[j].0@
&& minted@[j].1@ <= (*tallies).live@[q].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
(^minted)@.len() == minted@.len() + 1
&& (^minted)@[minted@.len()].0@ == station@
&& (^minted)@[minted@.len()].1@ == result@
&& forall<j: Int> 0 <= j && j < minted@.len()
==> (^minted)@[j] == minted@[j]
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
((^tallies).live@.len() == (*tallies).live@.len()
&& result@ >= 2
&& exists<q: Int> 0 <= q && q < (*tallies).live@.len()
&& (*tallies).live@[q].0@ == station@
&& result@ == (*tallies).live@[q].1@ + 1
&& (^tallies).live@[q].0@ == station@
&& (^tallies).live@[q].1@ == result@
&& forall<r: Int> 0 <= r && r < (*tallies).live@.len() && r != q
==> (^tallies).live@[r] == (*tallies).live@[r])
|| ((^tallies).live@.len() == (*tallies).live@.len() + 1
&& result@ == 1
&& (^tallies).live@[(*tallies).live@.len()].0@ == station@
&& (^tallies).live@[(*tallies).live@.len()].1@ == 1
&& (forall<q: Int> 0 <= q && q < (*tallies).live@.len()
==> (*tallies).live@[q].0@ != station@)
&& forall<q: Int> 0 <= q && q < (*tallies).live@.len()
==> (^tallies).live@[q] == (*tallies).live@[q])
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<p: Int, q: Int> 0 <= p && p < q && q < (^tallies).live@.len()
==> (^tallies).live@[p].0@ != (^tallies).live@[q].0@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<q: Int> 0 <= q && q < (*tallies).live@.len()
==> (^tallies).live@[q].0@ == (*tallies).live@[q].0@
&& (*tallies).live@[q].1@ <= (^tallies).live@[q].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<j: Int> 0 <= j && j < (^minted)@.len() && 2 <= (^minted)@[j].1@
==> exists<j2: Int> 0 <= j2 && j2 < j
&& (^minted)@[j2].0@ == (^minted)@[j].0@
&& (^minted)@[j2].1@ == (^minted)@[j].1@ - 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<q: Int> 0 <= q && q < (^tallies).live@.len()
==> exists<j: Int> 0 <= j && j < (^minted)@.len()
&& (^minted)@[j].0@ == (^tallies).live@[q].0@
&& (^minted)@[j].1@ == (^tallies).live@[q].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<j: Int> 0 <= j && j < (^minted)@.len()
==> exists<q: Int> 0 <= q && q < (^tallies).live@.len()
&& (^tallies).live@[q].0@ == (^minted)@[j].0@
&& (^minted)@[j].1@ <= (^tallies).live@[q].1@
))]
fn mint_one(minted: &mut Vec<(u32, u64)>, tallies: &mut StationTallies, station: u32) -> u64 {
let counter = tallies.mint(station);
#[cfg(creusot)]
let sealed = ::creusot_std::macros::snapshot!(minted@);
minted.push((station, counter));
#[cfg(creusot)]
::creusot_std::macros::proof_assert!(
minted@.len() == (*sealed).len() + 1
&& minted@[(*sealed).len()].0@ == station@
&& minted@[(*sealed).len()].1@ == counter@
&& forall<j: Int> 0 <= j && j < (*sealed).len()
==> minted@[j] == (*sealed)[j]
);
counter
}
#[cfg_attr(creusot, ::creusot_std::macros::check(terminates))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
result.minted@.len() == order@.len()
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int> 0 <= i && i < order@.len()
==> result.minted@[i].0@ == order@[i].0@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int> 0 <= i && i < order@.len()
==> 1 <= result.minted@[i].1@ && result.minted@[i].1@ <= order@.len()
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int, j: Int> 0 <= i && i < j && j < order@.len()
&& order@[i].0@ == order@[j].0@
==> result.minted@[i].1@ < result.minted@[j].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int, j: Int> 0 <= i && i < j && j < order@.len()
==> result.minted@[i].0@ != result.minted@[j].0@
|| result.minted@[i].1@ != result.minted@[j].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int> 1 <= i && i < order@.len()
&& order@[i].0@ == order@[i - 1].0@
==> result.minted@[i].1@ == result.minted@[i - 1].1@ + 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<p: Int, q: Int> 0 <= p && p < q && q < result.live@.len()
==> result.live@[p].0@ != result.live@[q].0@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<p: Int> 0 <= p && p < result.live@.len()
==> 1 <= result.live@[p].1@ && result.live@[p].1@ <= order@.len()
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<i: Int> 0 <= i && i < order@.len()
==> exists<p: Int> 0 <= p && p < result.live@.len()
&& result.live@[p].0@ == order@[i].0@
&& result.minted@[i].1@ <= result.live@[p].1@
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<j: Int> 0 <= j && j < result.minted@.len()
&& 2 <= result.minted@[j].1@
==> exists<j2: Int> 0 <= j2 && j2 < j
&& result.minted@[j2].0@ == result.minted@[j].0@
&& result.minted@[j2].1@ == result.minted@[j].1@ - 1
))]
#[cfg_attr(creusot, ::creusot_std::macros::ensures(
forall<p: Int> 0 <= p && p < result.live@.len()
==> exists<j: Int> 0 <= j && j < result.minted@.len()
&& result.minted@[j].0@ == result.live@[p].0@
&& result.minted@[j].1@ == result.live@[p].1@
))]
#[must_use]
pub fn remint(order: &[(u32, u64)]) -> PureRemint {
let mut minted: Vec<(u32, u64)> = Vec::new();
let mut tallies = StationTallies::new();
let mut i: usize = 0;
#[cfg_attr(creusot, variant(order@.len() - i@))]
#[cfg_attr(creusot, invariant(i@ <= order@.len()))]
#[cfg_attr(creusot, invariant(minted@.len() == i@))]
#[cfg_attr(creusot, invariant(
forall<j: Int> 0 <= j && j < i@ ==> minted@[j].0@ == order@[j].0@
))]
#[cfg_attr(creusot, invariant(
forall<j: Int> 0 <= j && j < i@
==> 1 <= minted@[j].1@ && minted@[j].1@ <= i@
))]
#[cfg_attr(creusot, invariant(
forall<j: Int, k: Int> 0 <= j && j < k && k < i@
&& order@[j].0@ == order@[k].0@
==> minted@[j].1@ < minted@[k].1@
))]
#[cfg_attr(creusot, invariant(
forall<j: Int> 1 <= j && j < i@ && order@[j].0@ == order@[j - 1].0@
==> minted@[j].1@ == minted@[j - 1].1@ + 1
))]
#[cfg_attr(creusot, invariant(
forall<p: Int, q: Int> 0 <= p && p < q && q < tallies.live@.len()
==> tallies.live@[p].0@ != tallies.live@[q].0@
))]
#[cfg_attr(creusot, invariant(
forall<p: Int> 0 <= p && p < tallies.live@.len()
==> 1 <= tallies.live@[p].1@ && tallies.live@[p].1@ <= i@
))]
#[cfg_attr(creusot, invariant(
forall<j: Int> 0 <= j && j < i@
==> exists<p: Int> 0 <= p && p < tallies.live@.len()
&& tallies.live@[p].0@ == order@[j].0@
&& minted@[j].1@ <= tallies.live@[p].1@
))]
#[cfg_attr(creusot, invariant(
forall<j: Int> 0 <= j && j < i@ && 2 <= minted@[j].1@
==> exists<j2: Int> 0 <= j2 && j2 < j
&& minted@[j2].0@ == minted@[j].0@
&& minted@[j2].1@ == minted@[j].1@ - 1
))]
#[cfg_attr(creusot, invariant(
forall<p: Int> 0 <= p && p < tallies.live@.len()
==> exists<j: Int> 0 <= j && j < i@
&& minted@[j].0@ == tallies.live@[p].0@
&& minted@[j].1@ == tallies.live@[p].1@
))]
#[cfg_attr(creusot, invariant(
i@ == 0 || exists<p: Int> 0 <= p && p < tallies.live@.len()
&& tallies.live@[p].0@ == minted@[i@ - 1].0@
&& tallies.live@[p].1@ == minted@[i@ - 1].1@
))]
while i < order.len() {
let station = order[i].0;
let _ = mint_one(&mut minted, &mut tallies, station);
i += 1;
}
PureRemint {
minted,
live: tallies.live,
}
}
#[cfg(test)]
mod tests {
use super::{Vec, remint};
#[test]
fn many_distinct_stations_remain_linearithmic() {
const STATIONS: u32 = 16_384;
let order: Vec<_> = (0..STATIONS).map(|station| (station, 1)).collect();
let assignment = remint(&order);
assert_eq!(assignment.minted, order);
assert_eq!(assignment.live, order);
}
}