#[allow(unused_imports)]
use super::super::prelude::*;
#[allow(unused_imports)]
use super::super::view::View;
#[allow(unused_imports)]
#[cfg(verus_keep_ghost)]
use super::mul::{lemma_mul_by_zero_is_zero, lemma_mul_inequality, lemma_mul_is_commutative};
#[allow(unused_imports)]
use super::*;
macro_rules! checked_uint_gen {
($uty: ty, $cty: ty) => {
verus! {
pub struct $cty {
i: Ghost<nat>,
v: Option<$uty>,
}
impl View for $cty
{
type V = nat;
closed spec fn view(&self) -> nat
{
self.i@
}
}
impl Clone for $cty {
exec fn clone(&self) -> (result: Self)
ensures
result@ == self@
{
proof { use_type_invariant(self); }
Self{ i: self.i, v: self.v }
}
}
impl $cty {
#[verifier::type_invariant]
spec fn well_formed(self) -> bool
{
match self.v {
Some(v) => self.i@ == v,
None => self.i@ > $uty::MAX,
}
}
pub closed spec fn spec_new(v: $uty) -> $cty
{
$cty{ i: Ghost(v as nat), v: Some(v) }
}
#[verifier::when_used_as_spec(spec_new)]
pub exec fn new(v: $uty) -> (result: Self)
ensures
result@ == v,
{
Self{ i: Ghost(v as nat), v: Some(v) }
}
pub exec fn new_overflowed(Ghost(i): Ghost<int>) -> (result: Self)
requires
i > $uty::MAX,
ensures
result@ == i,
{
Self{ i: Ghost(i as nat), v: None }
}
pub open spec fn spec_is_overflowed(&self) -> bool
{
self@ > $uty::MAX
}
#[verifier::when_used_as_spec(spec_is_overflowed)]
pub exec fn is_overflowed(&self) -> (result: bool)
ensures
result == self.spec_is_overflowed()
{
proof { use_type_invariant(self) }
self.v.is_none()
}
pub exec fn unwrap(&self) -> (result: $uty)
requires
!self.is_overflowed(),
ensures
result == self@,
{
proof { use_type_invariant(self) }
self.v.unwrap()
}
pub exec fn to_option(&self) -> (result: Option<$uty>)
ensures
match result {
Some(v) => self@ == v && v <= $uty::MAX,
None => self@ > $uty::MAX,
}
{
proof { use_type_invariant(self); }
self.v
}
#[inline]
pub exec fn add_value(&self, v2: $uty) -> (result: Self)
ensures
result@ == self@ + v2,
{
proof {
use_type_invariant(&self);
}
let i: Ghost<nat> = Ghost((&self@ + v2) as nat);
match self.v {
Some(v1) => Self{ i, v: v1.checked_add(v2) },
None => Self{ i, v: None },
}
}
#[inline]
pub exec fn add_checked(&self, v2: &$cty) -> (result: Self)
ensures
result@ == self@ + v2@,
{
proof {
use_type_invariant(self);
use_type_invariant(v2);
}
match v2.v {
Some(n) => self.add_value(n),
None => {
let i: Ghost<nat> = Ghost((self@ + v2@) as nat);
Self{ i, v: None }
}
}
}
#[inline]
pub exec fn mul_value(&self, v2: $uty) -> (result: Self)
ensures
result@ == self@ as int * v2 as int,
{
proof {
use_type_invariant(self);
}
let i: Ghost<nat> = Ghost((self@ * v2) as nat);
match self.v {
Some(n1) => Self{ i, v: n1.checked_mul(v2) },
None => {
if v2 == 0 {
assert(i@ == 0) by {
lemma_mul_by_zero_is_zero(self@ as int);
}
Self{ i, v: Some(0) }
}
else {
assert(self@ * v2 >= self@ * 1 == self@) by {
lemma_mul_inequality(1, v2 as int, self@ as int);
lemma_mul_is_commutative(self@ as int, v2 as int);
}
Self{ i, v: None }
}
},
}
}
#[inline]
pub exec fn mul_checked(&self, v2: &Self) -> (result: Self)
ensures
result@ == self@ as int * v2@ as int,
{
proof {
use_type_invariant(self);
use_type_invariant(v2);
}
let i: Ghost<nat> = Ghost((self@ * v2@) as nat);
match v2.v {
Some(n) => self.mul_value(n),
None => {
match self.v {
Some(n1) => {
if n1 == 0 {
assert(i@ == 0) by {
lemma_mul_by_zero_is_zero(v2@ as int);
}
Self{ i, v: Some(0) }
}
else {
assert(self@ * v2@ >= 1 * v2@ == v2@) by {
lemma_mul_inequality(1, self@ as int, v2@ as int);
}
Self{ i, v: None }
}
},
None => {
assert(self@ * v2@ > $uty::MAX) by {
lemma_mul_inequality(1, self@ as int, v2@ as int);
}
Self{ i, v: None }
},
}
}
}
}
}
}
};
}
checked_uint_gen!(u8, CheckedU8);
checked_uint_gen!(u16, CheckedU16);
checked_uint_gen!(u32, CheckedU32);
checked_uint_gen!(u64, CheckedU64);
checked_uint_gen!(u128, CheckedU128);
checked_uint_gen!(usize, CheckedUsize);