use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::denote::*;
#[allow(unused_imports)]
use crate::ext::*;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::types::*;
verus! {
pub open spec fn add_sound(a: Q, b: Q, r: Q) -> bool {
forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_add(u, v).is_some()
==> #[trigger] denotes(r, xr_add(u, v).unwrap())
}
pub open spec fn add_honest(a: Q, b: Q, r: Q) -> bool {
(exists|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_add(u, v).is_none())
==> r == Q::Nan
}
pub proof fn lemma_xr_add_comm(u: XR, v: XR)
requires
xr_wf(u),
xr_wf(v),
ensures
xr_add(u, v) == xr_add(v, u),
{
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => {
assert(ud * vd == vd * ud) by (nonlinear_arith);
},
_ => {},
}
}
pub proof fn lemma_add_sound_symm(a: Q, b: Q, r: Q)
requires
a.wf(),
b.wf(),
add_sound(a, b, r),
ensures
add_sound(b, a, r),
{
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(b, u) && denotes(a, v) && xr_add(u, v).is_some()
implies #[trigger] denotes(r, xr_add(u, v).unwrap()) by {
lemma_xr_add_comm(u, v);
}
}
pub proof fn lemma_not_neginf_excludes_neginfinity(b: Q, v: XR)
requires
b.wf(),
b != Q::NegInf,
b != Q::Nan,
denotes(b, v),
ensures
v != XR::NegInfinity,
{
if v == XR::NegInfinity {
match b {
Q::Number(x) => {
assert(denotes(b, v) == xr_eq(XR::NegInfinity, XR::Fin(x.n(), x.d())));
assert(!xr_eq(XR::NegInfinity, XR::Fin(x.n(), x.d())));
},
Q::PosSat => {
assert(!denotes(Q::PosSat, XR::NegInfinity));
},
Q::NegSat => {
assert(!denotes(Q::NegSat, XR::NegInfinity));
},
Q::PosInf => {
lemma_denotes_posinf_unique(v);
},
Q::NegInf | Q::Nan => {},
}
assert(false);
}
}
pub proof fn lemma_not_posinf_excludes_posinfinity(b: Q, v: XR)
requires
b.wf(),
b != Q::PosInf,
b != Q::Nan,
denotes(b, v),
ensures
v != XR::PosInfinity,
{
if v == XR::PosInfinity {
match b {
Q::Number(x) => {
assert(denotes(b, v) == xr_eq(XR::PosInfinity, XR::Fin(x.n(), x.d())));
assert(!xr_eq(XR::PosInfinity, XR::Fin(x.n(), x.d())));
},
Q::PosSat => {
assert(!denotes(Q::PosSat, XR::PosInfinity));
},
Q::NegSat => {
assert(!denotes(Q::NegSat, XR::PosInfinity));
},
Q::NegInf => {
lemma_denotes_neginf_unique(v);
},
Q::PosInf | Q::Nan => {},
}
assert(false);
}
}
pub proof fn lemma_add_sound_posinf_dominates(b: Q)
requires
b.wf(),
ensures
add_sound(Q::PosInf, b, Q::PosInf),
{
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(Q::PosInf, u) && denotes(b, v) && xr_add(u, v).is_some()
implies #[trigger] denotes(Q::PosInf, xr_add(u, v).unwrap()) by {
lemma_denotes_posinf_unique(u);
assert(xr_add(u, v) == Some(XR::PosInfinity));
}
}
pub proof fn lemma_add_sound_neginf_dominates(b: Q)
requires
b.wf(),
ensures
add_sound(Q::NegInf, b, Q::NegInf),
{
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(Q::NegInf, u) && denotes(b, v) && xr_add(u, v).is_some()
implies #[trigger] denotes(Q::NegInf, xr_add(u, v).unwrap()) by {
lemma_denotes_neginf_unique(u);
assert(xr_add(u, v) == Some(XR::NegInfinity));
}
}
pub proof fn lemma_add_sound_number_sat(x: Rat, sat_pos: bool)
requires
x.wf(),
ensures
add_sound(Q::Number(x), if sat_pos { Q::PosSat } else { Q::NegSat }, Q::spec_number_plus_sat(x, sat_pos)),
{
let b = if sat_pos { Q::PosSat } else { Q::NegSat };
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(Q::Number(x), u) && denotes(b, v) && xr_add(u, v).is_some()
implies #[trigger] denotes(Q::spec_number_plus_sat(x, sat_pos), xr_add(u, v).unwrap()) by {
lemma_denotes_number_unique(x, u);
let un = if let XR::Fin(n, _d) = u { n } else { 0 };
let ud = if let XR::Fin(_n, d) = u { d } else { 1 };
if sat_pos {
if x.n() >= 0 {
assert(un >= 0) by (nonlinear_arith)
requires
un * x.d() == x.n() * ud,
x.n() >= 0,
x.d() > 0,
ud > 0,
;
} else {
}
} else {
if x.n() <= 0 {
assert(un <= 0) by (nonlinear_arith)
requires
un * x.d() == x.n() * ud,
x.n() <= 0,
x.d() > 0,
ud > 0,
;
} else {
}
}
match v {
XR::Fin(vn, vd) => {
if sat_pos {
if x.n() >= 0 {
assert(un * vd + vn * ud > max_mag() * (ud * vd)) by (nonlinear_arith)
requires
un >= 0,
vn > max_mag() * vd,
ud > 0,
vd > 0,
;
}
} else {
if x.n() <= 0 {
assert(un * vd + vn * ud < 0 - max_mag() * (ud * vd)) by (nonlinear_arith)
requires
un <= 0,
vn < 0 - max_mag() * vd,
ud > 0,
vd > 0,
;
}
}
},
_ => {},
}
}
}
pub proof fn lemma_add_sound_sat_sat_same_sign(pos: bool)
requires
true,
ensures
add_sound(
if pos { Q::PosSat } else { Q::NegSat },
if pos { Q::PosSat } else { Q::NegSat },
if pos { Q::PosSat } else { Q::NegSat },
),
{
let s = if pos { Q::PosSat } else { Q::NegSat };
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(s, u) && denotes(s, v) && xr_add(u, v).is_some()
implies #[trigger] denotes(s, xr_add(u, v).unwrap()) by {
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => {
if pos {
assert(un * vd + vn * ud > max_mag() * (ud * vd)) by (nonlinear_arith)
requires
un > max_mag() * ud,
vn > max_mag() * vd,
ud > 0,
vd > 0,
max_mag() >= 0,
;
} else {
assert(un * vd + vn * ud < 0 - max_mag() * (ud * vd)) by (nonlinear_arith)
requires
un < 0 - max_mag() * ud,
vn < 0 - max_mag() * vd,
ud > 0,
vd > 0,
max_mag() >= 0,
;
}
},
_ => {},
}
}
}
pub proof fn theorem_add_sound(a: Q, b: Q)
requires
a.wf(),
b.wf(),
!(a.spec_is_number() && b.spec_is_number()),
ensures
add_sound(a, b, Q::spec_add(a, b)),
{
match (a, b) {
(Q::Nan, _) | (_, Q::Nan) => {
},
(Q::Number(_), Q::Number(_)) => {
assert(false);
},
(Q::PosInf, Q::NegInf) | (Q::NegInf, Q::PosInf) => {
},
(Q::PosInf, _) => {
lemma_add_sound_posinf_dominates(b);
},
(Q::NegInf, _) => {
lemma_add_sound_neginf_dominates(b);
},
(_, Q::PosInf) => {
lemma_add_sound_posinf_dominates(a);
lemma_add_sound_symm(Q::PosInf, a, Q::PosInf);
},
(_, Q::NegInf) => {
lemma_add_sound_neginf_dominates(a);
lemma_add_sound_symm(Q::NegInf, a, Q::NegInf);
},
(Q::Number(x), Q::PosSat) => {
lemma_add_sound_number_sat(x, true);
},
(Q::Number(x), Q::NegSat) => {
lemma_add_sound_number_sat(x, false);
},
(Q::PosSat, Q::Number(y)) => {
lemma_add_sound_number_sat(y, true);
lemma_add_sound_symm(Q::Number(y), Q::PosSat, Q::spec_number_plus_sat(y, true));
},
(Q::NegSat, Q::Number(y)) => {
lemma_add_sound_number_sat(y, false);
lemma_add_sound_symm(Q::Number(y), Q::NegSat, Q::spec_number_plus_sat(y, false));
},
(Q::PosSat, Q::PosSat) => {
lemma_add_sound_sat_sat_same_sign(true);
},
(Q::NegSat, Q::NegSat) => {
lemma_add_sound_sat_sat_same_sign(false);
},
(Q::PosSat, Q::NegSat) | (Q::NegSat, Q::PosSat) => {
},
}
}
pub proof fn theorem_add_honest(a: Q, b: Q)
requires
a.wf(),
b.wf(),
ensures
add_honest(a, b, Q::spec_add(a, b)),
{
if Q::spec_add(a, b) != Q::Nan {
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v)
implies #[trigger] xr_add(u, v).is_some() by {
match (a, b) {
(Q::PosInf, Q::NegInf) | (Q::NegInf, Q::PosInf) => {
assert(false);
},
(Q::PosInf, _) => {
assert(b != Q::Nan) by {
if b == Q::Nan {
assert(Q::spec_add(a, b) == Q::Nan);
}
}
lemma_not_neginf_excludes_neginfinity(b, v);
lemma_denotes_posinf_unique(u);
},
(Q::NegInf, _) => {
assert(b != Q::Nan) by {
if b == Q::Nan {
assert(Q::spec_add(a, b) == Q::Nan);
}
}
lemma_not_posinf_excludes_posinfinity(b, v);
lemma_denotes_neginf_unique(u);
},
(_, Q::PosInf) => {
assert(a != Q::Nan) by {
if a == Q::Nan {
assert(Q::spec_add(a, b) == Q::Nan);
}
}
lemma_not_neginf_excludes_neginfinity(a, u);
lemma_denotes_posinf_unique(v);
},
(_, Q::NegInf) => {
assert(a != Q::Nan) by {
if a == Q::Nan {
assert(Q::spec_add(a, b) == Q::Nan);
}
}
lemma_not_posinf_excludes_posinfinity(a, u);
lemma_denotes_neginf_unique(v);
},
_ => {},
}
}
}
}
pub proof fn theorem_add_nan_necessary_sat_sat_opposite(r: Q)
requires
r.wf(),
add_sound(Q::PosSat, Q::NegSat, r),
ensures
r == Q::Nan,
{
let u1 = XR::Fin(max_mag() + 1, 1);
let v1 = XR::Fin(0 - (max_mag() + 2), 1);
let u2 = XR::Fin(max_mag() + 5, 1);
let v2 = XR::Fin(0 - (max_mag() + 2), 1);
assert(xr_wf(u1) && xr_wf(v1));
assert(xr_wf(u2) && xr_wf(v2));
assert(denotes(Q::PosSat, u1));
assert(denotes(Q::NegSat, v1));
assert(denotes(Q::PosSat, u2));
assert(denotes(Q::NegSat, v2));
assert(xr_add(u1, v1) == Some(XR::Fin(0 - 1, 1)));
assert(xr_add(u2, v2) == Some(XR::Fin(3, 1)));
assert(denotes(r, xr_add(u1, v1).unwrap()));
assert(denotes(r, xr_add(u2, v2).unwrap()));
assert(denotes(r, XR::Fin(0 - 1, 1)));
assert(denotes(r, XR::Fin(3, 1)));
if r != Q::Nan {
match r {
Q::Number(x) => {
assert(0 - 1 * x.d() == x.n() * 1);
assert(3 * x.d() == x.n() * 1);
assert(x.d() == 0) by (nonlinear_arith)
requires
0 - x.d() == x.n(),
3 * x.d() == x.n(),
;
assert(false);
},
Q::PosSat => {
assert(false);
},
Q::NegSat => {
assert(false);
},
Q::PosInf | Q::NegInf => {
assert(false);
},
Q::Nan => {},
}
}
}
pub open spec fn sub_sound(a: Q, b: Q, r: Q) -> bool {
forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_sub(u, v).is_some()
==> #[trigger] denotes(r, xr_sub(u, v).unwrap())
}
pub open spec fn sub_honest(a: Q, b: Q, r: Q) -> bool {
(exists|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_sub(u, v).is_none())
==> r == Q::Nan
}
proof fn lemma_spec_neg_classifies(b: Q)
requires
b.wf(),
ensures
Q::spec_neg(b).spec_is_number() == b.spec_is_number(),
{
match b {
Q::Number(_) | Q::PosSat | Q::NegSat | Q::PosInf | Q::NegInf | Q::Nan => {},
}
}
pub proof fn theorem_sub_sound(a: Q, b: Q)
requires
a.wf(),
b.wf(),
!(a.spec_is_number() && b.spec_is_number()),
ensures
sub_sound(a, b, Q::spec_add(a, Q::spec_neg(b))),
{
Q::lemma_spec_neg_wf(b);
lemma_spec_neg_classifies(b);
let nb = Q::spec_neg(b);
theorem_add_sound(a, nb);
assert forall|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_sub(u, v).is_some()
implies #[trigger] denotes(Q::spec_add(a, nb), xr_sub(u, v).unwrap()) by {
lemma_neg_denotes(b, v);
}
}
pub proof fn theorem_sub_honest(a: Q, b: Q)
requires
a.wf(),
b.wf(),
ensures
sub_honest(a, b, Q::spec_add(a, Q::spec_neg(b))),
{
Q::lemma_spec_neg_wf(b);
let nb = Q::spec_neg(b);
theorem_add_honest(a, nb);
if exists|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_sub(u, v).is_none() {
let (u, v): (XR, XR) = choose|u: XR, v: XR|
xr_wf(u) && xr_wf(v) && denotes(a, u) && denotes(b, v) && xr_sub(u, v).is_none();
lemma_neg_denotes(b, v);
assert(xr_wf(xr_neg(v)) && denotes(a, u) && denotes(nb, xr_neg(v)) && xr_add(u, xr_neg(v)).is_none());
}
}
}