use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::q::*;
#[allow(unused_imports)]
use crate::round::*;
use crate::types::Rat;
verus! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Exact {
pub(crate) value: Rat,
}
impl Exact {
pub closed spec fn spec_value(self) -> Rat {
self.value
}
pub open spec fn wf(self) -> bool {
self.spec_value().wf()
}
pub open spec fn spec_is_sum(r: Exact, a: Exact, b: Exact) -> bool {
q_is(r.spec_value(), add_n(a.spec_value(), b.spec_value()), prod_d(a.spec_value(), b.spec_value()))
}
pub open spec fn spec_is_difference(r: Exact, a: Exact, b: Exact) -> bool {
q_is(r.spec_value(), sub_n(a.spec_value(), b.spec_value()), prod_d(a.spec_value(), b.spec_value()))
}
pub open spec fn spec_is_product(r: Exact, a: Exact, b: Exact) -> bool {
q_is(r.spec_value(), mul_n(a.spec_value(), b.spec_value()), prod_d(a.spec_value(), b.spec_value()))
}
pub open spec fn spec_is_quotient(r: Exact, a: Exact, b: Exact) -> bool {
q_is(r.spec_value(), div_n(a.spec_value(), b.spec_value()), div_d(a.spec_value(), b.spec_value()))
}
pub proof fn lemma_extensional(a: Exact, b: Exact)
requires
a.spec_value() == b.spec_value(),
ensures
a == b,
{
reveal(Exact::spec_value);
}
pub fn new(value: Rat) -> (r: Exact)
requires
value.wf(),
ensures
r.wf(),
r.spec_value() == value,
{
Exact { value }
}
pub fn value(&self) -> (r: Rat)
ensures
r == self.spec_value(),
{
proof {
reveal(Exact::spec_value);
}
self.value
}
pub fn is_nonneg(&self) -> (r: bool)
requires
self.wf(),
ensures
r <==> self.spec_value().n() >= 0,
{
proof {
reveal(Exact::spec_value);
}
self.value.numerator() >= 0
}
pub fn le(a: Exact, b: Exact) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_le(a.spec_value(), b.spec_value()),
{
proof {
reveal(Exact::spec_value);
}
Rat::le(a.value, b.value)
}
pub fn checked_add(a: Exact, b: Exact) -> (r: Option<Exact>)
requires
a.wf(),
b.wf(),
ensures
r.is_some() <==> exact_path(
add_n(a.spec_value(), b.spec_value()),
prod_d(a.spec_value(), b.spec_value()),
),
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> Exact::spec_is_sum(r.unwrap(), a, b),
{
proof {
reveal(Exact::spec_value);
lemma_op_widths(a.value, b.value);
crate::model::lemma_pow2_126();
}
if crate::round::exact_path_exec(add_n_exec(a.value, b.value), prod_d_exec(a.value, b.value)) {
Some(Exact { value: Rat::add(a.value, b.value) })
} else {
None
}
}
pub fn checked_sub(a: Exact, b: Exact) -> (r: Option<Exact>)
requires
a.wf(),
b.wf(),
ensures
r.is_some() <==> exact_path(
sub_n(a.spec_value(), b.spec_value()),
prod_d(a.spec_value(), b.spec_value()),
),
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> Exact::spec_is_difference(r.unwrap(), a, b),
{
proof {
reveal(Exact::spec_value);
lemma_op_widths(a.value, b.value);
crate::model::lemma_pow2_126();
}
if crate::round::exact_path_exec(sub_n_exec(a.value, b.value), prod_d_exec(a.value, b.value)) {
Some(Exact { value: Rat::sub(a.value, b.value) })
} else {
None
}
}
pub fn checked_mul(a: Exact, b: Exact) -> (r: Option<Exact>)
requires
a.wf(),
b.wf(),
ensures
r.is_some() <==> exact_path(
mul_n(a.spec_value(), b.spec_value()),
prod_d(a.spec_value(), b.spec_value()),
),
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> Exact::spec_is_product(r.unwrap(), a, b),
{
proof {
reveal(Exact::spec_value);
lemma_op_widths(a.value, b.value);
crate::model::lemma_pow2_126();
}
if crate::round::exact_path_exec(mul_n_exec(a.value, b.value), prod_d_exec(a.value, b.value)) {
Some(Exact { value: Rat::mul(a.value, b.value) })
} else {
None
}
}
pub fn checked_div(a: Exact, b: Exact) -> (r: Option<Exact>)
requires
a.wf(),
b.wf(),
ensures
b.spec_value().n() == 0 ==> r.is_none(),
b.spec_value().n() != 0 ==> (r.is_some() <==> exact_path(
div_n(a.spec_value(), b.spec_value()),
div_d(a.spec_value(), b.spec_value()),
)),
r.is_some() ==> b.spec_value().n() != 0,
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> Exact::spec_is_quotient(r.unwrap(), a, b),
{
proof {
reveal(Exact::spec_value);
}
if b.value.numerator() == 0 {
return None;
}
proof {
lemma_op_widths(a.value, b.value);
crate::model::lemma_pow2_126();
assert(b.value.n() > 0 ==> a.value.d() * b.value.n() > 0) by (nonlinear_arith)
requires
a.value.d() > 0,
;
assert(b.value.n() < 0 ==> a.value.d() * b.value.n() < 0) by (nonlinear_arith)
requires
a.value.d() > 0,
;
}
if crate::round::exact_path_exec(div_n_exec(a.value, b.value), div_d_exec(a.value, b.value)) {
Some(Exact { value: Rat::div(a.value, b.value) })
} else {
None
}
}
}
pub proof fn theorem_exact_add_associative(
a: Exact,
b: Exact,
c: Exact,
ab: Exact,
bc: Exact,
left: Exact,
right: Exact,
)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
bc.wf(),
left.wf(),
right.wf(),
Exact::spec_is_sum(ab, a, b),
Exact::spec_is_sum(bc, b, c),
Exact::spec_is_sum(left, ab, c),
Exact::spec_is_sum(right, a, bc),
ensures
left == right,
{
crate::laws::lemma_add_assoc_exact_values(
a.spec_value(),
b.spec_value(),
c.spec_value(),
ab.spec_value(),
bc.spec_value(),
left.spec_value(),
right.spec_value(),
);
crate::laws::lemma_canonical_eq(left.spec_value(), right.spec_value());
Exact::lemma_extensional(left, right);
}
pub proof fn theorem_exact_mul_associative(
a: Exact,
b: Exact,
c: Exact,
ab: Exact,
bc: Exact,
left: Exact,
right: Exact,
)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
bc.wf(),
left.wf(),
right.wf(),
Exact::spec_is_product(ab, a, b),
Exact::spec_is_product(bc, b, c),
Exact::spec_is_product(left, ab, c),
Exact::spec_is_product(right, a, bc),
ensures
left == right,
{
crate::laws::theorem_mul_associative_exact(
a.spec_value(),
b.spec_value(),
c.spec_value(),
ab.spec_value(),
bc.spec_value(),
left.spec_value(),
right.spec_value(),
);
crate::laws::lemma_canonical_eq(left.spec_value(), right.spec_value());
Exact::lemma_extensional(left, right);
}
pub proof fn theorem_exact_distributive(
a: Exact,
b: Exact,
c: Exact,
bc: Exact,
lhs: Exact,
ab: Exact,
ac: Exact,
rhs: Exact,
)
requires
a.wf(),
b.wf(),
c.wf(),
bc.wf(),
lhs.wf(),
ab.wf(),
ac.wf(),
rhs.wf(),
Exact::spec_is_sum(bc, b, c),
Exact::spec_is_product(lhs, a, bc),
Exact::spec_is_product(ab, a, b),
Exact::spec_is_product(ac, a, c),
Exact::spec_is_sum(rhs, ab, ac),
ensures
lhs == rhs,
{
crate::laws::theorem_distributive_exact(
a.spec_value(),
b.spec_value(),
c.spec_value(),
bc.spec_value(),
lhs.spec_value(),
ab.spec_value(),
ac.spec_value(),
rhs.spec_value(),
);
crate::laws::lemma_canonical_eq(lhs.spec_value(), rhs.spec_value());
Exact::lemma_extensional(lhs, rhs);
}
pub proof fn theorem_exact_add_monotone(a: Exact, b: Exact, c: Exact, ac: Exact, bc: Exact)
requires
a.wf(),
b.wf(),
c.wf(),
ac.wf(),
bc.wf(),
q_le(a.spec_value(), b.spec_value()),
Exact::spec_is_sum(ac, a, c),
Exact::spec_is_sum(bc, b, c),
ensures
q_le(ac.spec_value(), bc.spec_value()),
{
let av = a.spec_value();
let bv = b.spec_value();
let cv = c.spec_value();
let x = ac.spec_value();
let y = bc.spec_value();
crate::laws::theorem_add_monotone_exact(av, bv, cv);
crate::q::lemma_op_widths(av, cv);
crate::q::lemma_op_widths(bv, cv);
crate::interval::lemma_frac_chain_le(
x.n(),
x.d(),
add_n(av, cv),
prod_d(av, cv),
add_n(bv, cv),
prod_d(bv, cv),
y.n(),
y.d(),
);
}
pub proof fn theorem_exact_mul_monotone_nonneg(a: Exact, b: Exact, c: Exact, ac: Exact, bc: Exact)
requires
a.wf(),
b.wf(),
c.wf(),
ac.wf(),
bc.wf(),
q_le(a.spec_value(), b.spec_value()),
c.spec_value().n() >= 0,
Exact::spec_is_product(ac, a, c),
Exact::spec_is_product(bc, b, c),
ensures
q_le(ac.spec_value(), bc.spec_value()),
{
let av = a.spec_value();
let bv = b.spec_value();
let cv = c.spec_value();
let x = ac.spec_value();
let y = bc.spec_value();
crate::laws::theorem_mul_monotone_nonneg_exact(av, bv, cv);
crate::q::lemma_op_widths(av, cv);
crate::q::lemma_op_widths(bv, cv);
crate::interval::lemma_frac_chain_le(
x.n(),
x.d(),
mul_n(av, cv),
prod_d(av, cv),
mul_n(bv, cv),
prod_d(bv, cv),
y.n(),
y.d(),
);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum ExactError {
Inexact,
DivisionByZero,
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::fmt::Display for ExactError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(match self {
ExactError::Inexact => "the-q: exact result would need rounding",
ExactError::DivisionByZero => "the-q: division by zero",
})
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl std::error::Error for ExactError {}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl Exact {
pub fn add(a: Exact, b: Exact) -> Result<Exact, ExactError> {
Exact::checked_add(a, b).ok_or(ExactError::Inexact)
}
pub fn sub(a: Exact, b: Exact) -> Result<Exact, ExactError> {
Exact::checked_sub(a, b).ok_or(ExactError::Inexact)
}
pub fn mul(a: Exact, b: Exact) -> Result<Exact, ExactError> {
Exact::checked_mul(a, b).ok_or(ExactError::Inexact)
}
pub fn div(a: Exact, b: Exact) -> Result<Exact, ExactError> {
if b.value().numerator() == 0 {
return Err(ExactError::DivisionByZero);
}
Exact::checked_div(a, b).ok_or(ExactError::Inexact)
}
}