use vstd::prelude::*;
verus! {
pub open spec fn map_spec(position: int, start: int, old_end: int, new_end: int) -> int {
if position < start {
position
} else if position >= old_end {
new_end + (position - old_end)
} else {
start
}
}
pub fn map_position(position: usize, start: usize, old_end: usize, new_end: usize) -> (mapped: usize)
requires
start <= old_end,
position >= old_end ==> new_end + (position - old_end) <= usize::MAX,
ensures
mapped as int == map_spec(position as int, start as int, old_end as int, new_end as int),
{
if position < start {
position
} else if position >= old_end {
new_end.saturating_add(position - old_end)
} else {
start
}
}
proof fn map_spec_monotone(a: int, b: int, start: int, old_end: int, new_end: int)
requires
start <= old_end,
start <= new_end,
a <= b,
ensures
map_spec(a, start, old_end, new_end) <= map_spec(b, start, old_end, new_end),
{
}
proof fn map_spec_in_bounds(
position: int,
start: int,
old_end: int,
new_end: int,
old_len: int,
new_len: int,
)
requires
0 <= start <= old_end <= old_len,
0 <= start <= new_end <= new_len,
0 <= position <= old_len,
new_len == old_len - (old_end - start) + (new_end - start),
ensures
0 <= map_spec(position, start, old_end, new_end) <= new_len,
{
}
}
verus! {
pub open spec fn to_ints(edits: Seq<(usize, usize)>) -> Seq<(int, int)> {
edits.map_values(|pair: (usize, usize)| (pair.0 as int, pair.1 as int))
}
pub open spec fn batch_ok(edits: Seq<(int, int)>, len: int) -> bool {
forall|i: int| #![trigger edits[i]] 0 <= i < edits.len() ==> {
let (s, e) = edits[i];
&&& 0 <= s <= e <= len
&&& i + 1 < edits.len() ==> {
let (s2, _) = edits[i + 1];
&&& s < s2
&&& e <= s2
}
}
}
proof fn insert_preserves(
edits: Seq<(usize, usize)>,
pre: Seq<(usize, (usize, usize))>,
j: usize,
i: usize,
pair: (usize, usize),
)
requires
j as int <= pre.len(),
pre.len() == i as int,
(i as int) < edits.len(),
pair == edits[i as int],
forall|k: int| #![trigger pre[k]] 0 <= k < pre.len() ==> {
&&& (pre[k].0 as int) < i as int
&&& pre[k].1 == edits[pre[k].0 as int]
},
forall|a: int, b: int| #![trigger pre[a], pre[b]]
0 <= a < b < pre.len() ==> pre[a].0 != pre[b].0,
forall|k: int| #![trigger pre[k]] 0 <= k < pre.len() - 1 ==>
pre[k].1.0 <= pre[k + 1].1.0,
j > 0 ==> pre[j as int - 1].1.0 as int <= pair.0 as int,
forall|p: int| #![trigger pre[p]] j as int <= p < pre.len() ==>
pre[p].1.0 as int > pair.0 as int,
ensures
({
let post = pre.insert(j as int, (i, pair));
&&& post.len() == i as int + 1
&&& forall|k: int| #![trigger post[k]] 0 <= k < post.len() ==> {
&&& (post[k].0 as int) < i as int + 1
&&& post[k].1 == edits[post[k].0 as int]
}
&&& forall|a: int, b: int| #![trigger post[a], post[b]]
0 <= a < b < post.len() ==> post[a].0 != post[b].0
&&& forall|k: int| #![trigger post[k]] 0 <= k < post.len() - 1 ==>
post[k].1.0 <= post[k + 1].1.0
}),
{
let post = pre.insert(j as int, (i, pair));
assert forall|k: int| #![trigger post[k]] 0 <= k < post.len() implies {
&&& (post[k].0 as int) < i as int + 1
&&& post[k].1 == edits[post[k].0 as int]
} by {
if k == j as int {
assert(post[k] == (i, pair));
} else if k < j as int {
assert(post[k] == pre[k]);
} else {
assert(post[k] == pre[k - 1]);
}
}
assert forall|a: int, b: int| #![trigger post[a], post[b]]
0 <= a < b < post.len() implies post[a].0 != post[b].0 by {
if a == j as int {
assert(post[b] == pre[b - 1]);
} else if b == j as int {
assert(post[a] == pre[a]);
} else {
assert(post[a] == if a < j as int { pre[a] } else { pre[a - 1] });
assert(post[b] == if b < j as int { pre[b] } else { pre[b - 1] });
}
}
assert forall|k: int| #![trigger post[k]] 0 <= k < post.len() - 1 implies
post[k].1.0 <= post[k + 1].1.0 by {
if k + 1 < j as int {
assert(post[k] == pre[k]);
assert(post[k + 1] == pre[k + 1]);
} else if k + 1 == j as int {
assert(post[k] == pre[k]);
assert(post[k + 1] == (i, pair));
} else if k == j as int {
assert(post[k] == (i, pair));
assert(post[k + 1] == pre[k]);
} else {
assert(post[k] == pre[k - 1]);
assert(post[k + 1] == pre[k]);
}
}
}
#[allow(clippy::ptr_arg, clippy::needless_range_loop)]
fn sort_by_start(edits: &Vec<(usize, usize)>) -> (out: Vec<(usize, (usize, usize))>)
ensures
out@.len() == edits@.len(),
forall|k: int| #![trigger out@[k]] 0 <= k < out@.len() ==> {
&&& (out@[k].0 as int) < edits@.len()
&&& out@[k].1 == edits@[out@[k].0 as int]
},
forall|a: int, b: int| #![trigger out@[a], out@[b]]
0 <= a < b < out@.len() ==> out@[a].0 != out@[b].0,
forall|k: int| #![trigger out@[k]] 0 <= k < out@.len() - 1 ==>
out@[k].1.0 <= out@[k + 1].1.0,
{
let mut out: Vec<(usize, (usize, usize))> = Vec::new();
for i in 0..edits.len()
invariant
i <= edits.len(),
out@.len() == i as int,
forall|k: int| #![trigger out@[k]] 0 <= k < out@.len() ==> {
&&& (out@[k].0 as int) < i as int
&&& out@[k].1 == edits@[out@[k].0 as int]
},
forall|a: int, b: int| #![trigger out@[a], out@[b]]
0 <= a < b < out@.len() ==> out@[a].0 != out@[b].0,
forall|k: int| #![trigger out@[k]] 0 <= k < out@.len() - 1 ==>
out@[k].1.0 <= out@[k + 1].1.0,
{
let pair = edits[i];
let s = pair.0;
let ghost pre = out@;
let mut j = out.len();
while j > 0 && out[j - 1].1.0 > s
invariant
j <= out.len(),
out@ == pre,
forall|p: int| #![trigger out@[p]] j <= p < out@.len() ==>
out@[p].1.0 > s as int,
decreases j,
{
j -= 1;
}
out.insert(j, (i, pair));
proof {
insert_preserves(edits@, pre, j, i, pair);
}
}
out
}
#[allow(clippy::result_unit_err, clippy::needless_range_loop)]
pub fn check_batch(len: usize, edits: Vec<(usize, usize)>) -> (out: Result<Vec<(usize, usize)>, ()>)
ensures
out.is_ok() ==> batch_ok(to_ints(out.unwrap()@), len as int),
out.is_err() ==> {
||| exists|i: int| #![trigger edits@[i]] 0 <= i < edits@.len() && {
let (s, e) = edits@[i]; !(0 <= s <= e <= len as int)
}
||| exists|i: int, j: int| #![trigger edits@[i], edits@[j]]
0 <= i < edits@.len() && 0 <= j < edits@.len() && i != j && {
let (si, ei) = edits@[i]; let (sj, _ej) = edits@[j];
&&& si == sj || (si <= sj && ei > sj)
}
},
{
let sorted = sort_by_start(&edits);
let n = sorted.len();
let mut i = 0usize;
while i < n
invariant
0 <= i <= n,
n == sorted@.len(),
forall|k: int| #![trigger sorted@[k]] 0 <= k < sorted@.len() ==> {
&&& (sorted@[k].0 as int) < edits@.len()
&&& sorted@[k].1 == edits@[sorted@[k].0 as int]
},
forall|a: int, b: int| #![trigger sorted@[a], sorted@[b]]
0 <= a < b < sorted@.len() ==> sorted@[a].0 != sorted@[b].0,
forall|k: int| #![trigger sorted@[k]] 0 <= k < sorted@.len() - 1 ==>
sorted@[k].1.0 <= sorted@[k + 1].1.0,
forall|k: int| #![trigger sorted@[k]] 0 <= k < i as int ==> {
let (sk, ek) = sorted@[k].1;
&&& 0 <= sk as int
&&& sk <= ek
&&& ek <= len
},
forall|k: int| #![trigger sorted@[k]] 0 <= k < i as int - 1 ==> {
let (sk, ek) = sorted@[k].1;
let (sk1, _ek1) = sorted@[k + 1].1;
&&& sk < sk1
&&& ek <= sk1
},
decreases n - i,
{
let (s, e) = sorted[i].1;
if !(s <= e && e <= len) {
proof {
let w = sorted@[i as int].0 as int;
assert(0 <= w < edits@.len());
assert(edits@[w] == (s, e));
assert(!(0 <= s as int && s <= e && e <= len));
assert(exists|i2: int| #![trigger edits@[i2]] 0 <= i2 < edits@.len() && {
let (s2, e2) = edits@[i2]; !(0 <= s2 <= e2 <= len as int)
});
}
return Err(());
}
if i > 0 {
let (sp, ep) = sorted[i - 1].1;
if sp == s || ep > s {
proof {
let a = sorted@[i as int - 1].0 as int;
let b = sorted@[i as int].0 as int;
assert(0 <= a < edits@.len());
assert(0 <= b < edits@.len());
assert(a != b);
assert(edits@[a] == (sp, ep));
assert(edits@[b] == (s, e));
assert(sp as int <= s as int);
assert(exists|i2: int, j2: int| #![trigger edits@[i2], edits@[j2]]
0 <= i2 < edits@.len() && 0 <= j2 < edits@.len() && i2 != j2 && {
let (si, ei) = edits@[i2]; let (sj, _ej) = edits@[j2];
&&& si == sj || (si <= sj && ei > sj)
});
}
return Err(());
}
}
i += 1;
}
let mut result: Vec<(usize, usize)> = Vec::new();
let mut k = 0usize;
while k < n
invariant
0 <= k <= n,
n == sorted@.len(),
result@.len() == k as int,
forall|p: int| #![trigger result@[p]] 0 <= p < k as int ==>
result@[p] == sorted@[p].1,
decreases n - k,
{
result.push(sorted[k].1);
k += 1;
}
proof {
let ri = to_ints(result@);
assert forall|p: int| #![trigger ri[p]] 0 <= p < ri.len() implies {
let (s, e) = ri[p];
&&& 0 <= s <= e <= len as int
&&& p + 1 < ri.len() ==> {
let (s2, _) = ri[p + 1];
&&& s < s2 &&& e <= s2
}
} by {
assert(result@[p] == sorted@[p].1);
if p + 1 < ri.len() {
assert(result@[p + 1] == sorted@[p + 1].1);
}
}
assert(batch_ok(ri, len as int));
}
Ok(result)
}
}