use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::ext::*;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::types::*;
verus! {
pub enum XR {
Fin(int, int),
PosInfinity,
NegInfinity,
}
pub open spec fn xr_wf(v: XR) -> bool {
match v {
XR::Fin(_n, d) => d > 0,
_ => true,
}
}
pub open spec fn xr_eq(u: XR, v: XR) -> bool {
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => un * vd == vn * ud,
(XR::PosInfinity, XR::PosInfinity) => true,
(XR::NegInfinity, XR::NegInfinity) => true,
_ => false,
}
}
pub open spec fn xr_is_zero(v: XR) -> bool {
match v {
XR::Fin(n, _d) => n == 0,
_ => false,
}
}
pub open spec fn xr_neg(v: XR) -> XR {
match v {
XR::Fin(n, d) => XR::Fin(-n, d),
XR::PosInfinity => XR::NegInfinity,
XR::NegInfinity => XR::PosInfinity,
}
}
pub open spec fn xr_add(u: XR, v: XR) -> Option<XR> {
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => Some(XR::Fin(un * vd + vn * ud, ud * vd)),
(XR::PosInfinity, XR::NegInfinity) => None,
(XR::NegInfinity, XR::PosInfinity) => None,
(XR::PosInfinity, _) => Some(XR::PosInfinity),
(XR::NegInfinity, _) => Some(XR::NegInfinity),
(_, XR::PosInfinity) => Some(XR::PosInfinity),
(_, XR::NegInfinity) => Some(XR::NegInfinity),
}
}
pub open spec fn xr_sub(u: XR, v: XR) -> Option<XR> {
xr_add(u, xr_neg(v))
}
pub open spec fn xr_mul(u: XR, v: XR) -> Option<XR> {
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => Some(XR::Fin(un * vn, ud * vd)),
(XR::PosInfinity, XR::Fin(vn, _vd)) => if vn == 0 {
None
} else if vn > 0 {
Some(XR::PosInfinity)
} else {
Some(XR::NegInfinity)
},
(XR::NegInfinity, XR::Fin(vn, _vd)) => if vn == 0 {
None
} else if vn > 0 {
Some(XR::NegInfinity)
} else {
Some(XR::PosInfinity)
},
(XR::Fin(un, _ud), XR::PosInfinity) => if un == 0 {
None
} else if un > 0 {
Some(XR::PosInfinity)
} else {
Some(XR::NegInfinity)
},
(XR::Fin(un, _ud), XR::NegInfinity) => if un == 0 {
None
} else if un > 0 {
Some(XR::NegInfinity)
} else {
Some(XR::PosInfinity)
},
(XR::PosInfinity, XR::PosInfinity) => Some(XR::PosInfinity),
(XR::PosInfinity, XR::NegInfinity) => Some(XR::NegInfinity),
(XR::NegInfinity, XR::PosInfinity) => Some(XR::NegInfinity),
(XR::NegInfinity, XR::NegInfinity) => Some(XR::PosInfinity),
}
}
pub open spec fn xr_div(u: XR, v: XR) -> Option<XR> {
match (u, v) {
(XR::Fin(un, ud), XR::Fin(vn, vd)) => if vn == 0 {
if un == 0 {
None
} else if un > 0 {
Some(XR::PosInfinity)
} else {
Some(XR::NegInfinity)
}
} else if vn > 0 {
Some(XR::Fin(un * vd, ud * vn))
} else {
Some(XR::Fin(0 - un * vd, 0 - ud * vn))
},
(XR::PosInfinity, XR::Fin(vn, _vd)) => if vn < 0 {
Some(XR::NegInfinity)
} else {
Some(XR::PosInfinity)
},
(XR::NegInfinity, XR::Fin(vn, _vd)) => if vn < 0 {
Some(XR::PosInfinity)
} else {
Some(XR::NegInfinity)
},
(XR::Fin(_un, _ud), XR::PosInfinity) => Some(XR::Fin(0, 1)),
(XR::Fin(_un, _ud), XR::NegInfinity) => Some(XR::Fin(0, 1)),
(XR::PosInfinity, XR::PosInfinity) => None,
(XR::PosInfinity, XR::NegInfinity) => None,
(XR::NegInfinity, XR::PosInfinity) => None,
(XR::NegInfinity, XR::NegInfinity) => None,
}
}
pub open spec fn denotes(q: Q, v: XR) -> bool {
match q {
Q::Number(x) => xr_eq(v, XR::Fin(x.n(), x.d())),
Q::PosSat => match v {
XR::Fin(n, d) => n > max_mag() * d,
_ => false,
},
Q::NegSat => match v {
XR::Fin(n, d) => n < 0 - max_mag() * d,
_ => false,
},
Q::PosInf => match v {
XR::PosInfinity => true,
_ => false,
},
Q::NegInf => match v {
XR::NegInfinity => true,
_ => false,
},
Q::Nan => true,
}
}
pub proof fn lemma_denotes_inhabited(q: Q)
requires
q.wf(),
ensures
exists|v: XR| xr_wf(v) && #[trigger] denotes(q, v),
{
match q {
Q::Number(x) => {
assert(xr_wf(XR::Fin(x.n(), x.d())));
assert(denotes(q, XR::Fin(x.n(), x.d())));
},
Q::PosSat => {
let v = XR::Fin(max_mag() + 1, 1);
assert(xr_wf(v));
assert(denotes(q, v));
},
Q::NegSat => {
let v = XR::Fin(0 - (max_mag() + 1), 1);
assert(xr_wf(v));
assert(denotes(q, v));
},
Q::PosInf => {
assert(denotes(q, XR::PosInfinity));
},
Q::NegInf => {
assert(denotes(q, XR::NegInfinity));
},
Q::Nan => {
assert(xr_wf(XR::Fin(0, 1)));
assert(denotes(q, XR::Fin(0, 1)));
},
}
}
pub proof fn lemma_denotes_number_unique(x: Rat, v: XR)
requires
x.wf(),
denotes(Q::Number(x), v),
ensures
xr_eq(v, XR::Fin(x.n(), x.d())),
{
}
pub proof fn lemma_denotes_posinf_unique(v: XR)
requires
denotes(Q::PosInf, v),
ensures
v == XR::PosInfinity,
{
}
pub proof fn lemma_denotes_neginf_unique(v: XR)
requires
denotes(Q::NegInf, v),
ensures
v == XR::NegInfinity,
{
}
pub proof fn lemma_number_not_saturated(x: Rat)
requires
x.wf(),
ensures
!(x.n() > max_mag() * x.d()),
!(x.n() < 0 - max_mag() * x.d()),
{
assert(x.n() <= max_mag() * x.d()) by (nonlinear_arith)
requires
x.n() <= max_mag(),
x.d() >= 1,
max_mag() >= 0,
;
assert(x.n() >= 0 - max_mag() * x.d()) by (nonlinear_arith)
requires
x.n() >= 0 - max_mag(),
x.d() >= 1,
max_mag() >= 0,
;
}
pub proof fn lemma_neg_denotes(a: Q, v: XR)
requires
a.wf(),
xr_wf(v),
ensures
xr_wf(xr_neg(v)),
denotes(a, v) == denotes(crate::ext::Q::spec_neg(a), xr_neg(v)),
{
match a {
Q::Number(x) => {
match v {
XR::Fin(n, d) => {
Rat::lemma_from_raw_spec_components((0 - x.n()) as i64, x.d() as i64);
assert((n * x.d() == x.n() * d) == ((0 - n) * x.d() == (0 - x.n()) * d)) by (
nonlinear_arith
);
},
_ => {},
}
},
Q::PosSat | Q::NegSat | Q::PosInf | Q::NegInf | Q::Nan => {},
}
}
}