use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
use crate::ext::Q;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::q::*;
#[allow(unused_imports)]
use crate::types::MAX_MAG;
use crate::types::{Dir, Rat};
verus! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct QI {
pub(crate) lo: Rat,
pub(crate) hi: Rat,
}
impl QI {
pub closed spec fn spec_lower(self) -> Rat {
self.lo
}
pub closed spec fn spec_upper(self) -> Rat {
self.hi
}
pub fn lower(&self) -> (r: Rat)
ensures r == self.spec_lower(),
{
proof { reveal(QI::spec_lower); }
self.lo
}
pub fn upper(&self) -> (r: Rat)
ensures r == self.spec_upper(),
{
proof { reveal(QI::spec_upper); }
self.hi
}
pub open spec fn wf(self) -> bool {
&&& self.spec_lower().wf()
&&& self.spec_upper().wf()
&&& q_le(self.spec_lower(), self.spec_upper())
}
pub fn exact(a: Rat) -> (r: QI)
requires
a.wf(),
ensures
r.wf(),
r.spec_lower() == a,
r.spec_upper() == a,
{
QI { lo: a, hi: a }
}
pub fn new(lo: Rat, hi: Rat) -> (r: QI)
requires
lo.wf(),
hi.wf(),
q_le(lo, hi),
ensures
r.wf(),
r.spec_lower() == lo,
r.spec_upper() == hi,
{
crate::q::require_condition(
Rat::le(lo, hi),
"the-q: QI::new requires lo <= hi. Use QI::checked_new for untrusted input.",
);
QI { lo, hi }
}
pub fn checked_new(lo: Rat, hi: Rat) -> (r: Option<QI>)
requires
lo.wf(),
hi.wf(),
ensures
r.is_some() <==> q_le(lo, hi),
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> r.unwrap().spec_lower() == lo,
r.is_some() ==> r.unwrap().spec_upper() == hi,
{
if Rat::le(lo, hi) {
Some(QI { lo, hi })
} else {
None
}
}
pub fn contains(&self, x: Rat) -> (r: bool)
requires
self.wf(),
x.wf(),
ensures
r <==> (q_le(self.spec_lower(), x) && q_le(x, self.spec_upper())),
{
Rat::le(self.lo, x) && Rat::le(x, self.hi)
}
pub fn checked_width(&self) -> (r: Option<Rat>)
requires
self.wf(),
ensures
r.is_none() <==> crate::round::saturated(
sub_n(self.spec_upper(), self.spec_lower()),
prod_d(self.spec_upper(), self.spec_lower()),
),
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> q_ge_frac(
r.unwrap(),
sub_n(self.spec_upper(), self.spec_lower()),
prod_d(self.spec_upper(), self.spec_lower()),
),
r.is_some() ==> r.unwrap().n() >= 0,
{
proof {
reveal(QI::spec_lower);
reveal(QI::spec_upper);
theorem_qi_exact_width_nonnegative(*self);
}
let result = Rat::checked_sub_dir(self.hi, self.lo, Dir::Up);
proof {
reveal(QI::spec_lower);
reveal(QI::spec_upper);
theorem_qi_exact_width_nonnegative(*self);
assert(prod_d(self.hi, self.lo) > 0);
assert(sub_n(self.hi, self.lo) >= 0);
if result.is_some() {
assert(result.unwrap().n() * prod_d(self.hi, self.lo)
>= sub_n(self.hi, self.lo) * result.unwrap().d());
assert(sub_n(self.hi, self.lo) * result.unwrap().d() >= 0) by (nonlinear_arith)
requires
sub_n(self.hi, self.lo) >= 0,
result.unwrap().d() > 0,
;
assert(result.unwrap().n() * prod_d(self.hi, self.lo) >= 0);
if result.unwrap().n() < 0 {
vstd::arithmetic::mul::lemma_mul_strictly_positive(
-result.unwrap().n(),
prod_d(self.hi, self.lo),
);
assert(
result.unwrap().n() * prod_d(self.hi, self.lo)
== -((-result.unwrap().n()) * prod_d(self.hi, self.lo))
) by (nonlinear_arith);
assert(false);
}
}
}
result
}
pub fn width(&self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
!crate::round::saturated(
sub_n(self.spec_upper(), self.spec_lower()),
prod_d(self.spec_upper(), self.spec_lower()),
) ==> r.spec_is_number(),
crate::round::saturated(
sub_n(self.spec_upper(), self.spec_lower()),
prod_d(self.spec_upper(), self.spec_lower()),
) ==> r == Q::PosSat,
r != Q::NegSat,
!r.spec_is_infinite(),
!r.spec_is_nan(),
{
match self.checked_width() {
Some(width) => Q::Number(width),
None => Q::PosSat,
}
}
pub fn add(a: QI, b: QI) -> (r: QI)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
!crate::round::saturated(add_n(a.spec_lower(), b.spec_lower()), prod_d(a.spec_lower(), b.spec_lower())) ==> q_le_frac(
r.spec_lower(),
add_n(a.spec_lower(), b.spec_lower()),
prod_d(a.spec_lower(), b.spec_lower()),
),
!crate::round::saturated(add_n(a.spec_upper(), b.spec_upper()), prod_d(a.spec_upper(), b.spec_upper())) ==> q_ge_frac(
r.spec_upper(),
add_n(a.spec_upper(), b.spec_upper()),
prod_d(a.spec_upper(), b.spec_upper()),
),
{
let lo = Rat::add_dir(a.lo, b.lo, Dir::Down);
let hi = Rat::add_dir(a.hi, b.hi, Dir::Up);
proof {
crate::q::lemma_op_widths(a.lo, b.lo);
crate::q::lemma_op_widths(a.hi, b.hi);
if !crate::round::saturated(add_n(a.lo, b.lo), prod_d(a.lo, b.lo)) {
crate::round::lemma_r2_directed(add_n(a.lo, b.lo), prod_d(a.lo, b.lo));
}
if !crate::round::saturated(add_n(a.hi, b.hi), prod_d(a.hi, b.hi)) {
crate::round::lemma_r2_directed(add_n(a.hi, b.hi), prod_d(a.hi, b.hi));
}
lemma_add_endpoint_order(
a.lo.n(),
a.lo.d(),
b.lo.n(),
b.lo.d(),
a.hi.n(),
a.hi.d(),
b.hi.n(),
b.hi.d(),
);
lemma_directed_round_order(
add_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
add_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
}
QI { lo, hi }
}
pub fn sub(a: QI, b: QI) -> (r: QI)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
!crate::round::saturated(sub_n(a.spec_lower(), b.spec_upper()), prod_d(a.spec_lower(), b.spec_upper())) ==> q_le_frac(
r.spec_lower(),
sub_n(a.spec_lower(), b.spec_upper()),
prod_d(a.spec_lower(), b.spec_upper()),
),
!crate::round::saturated(sub_n(a.spec_upper(), b.spec_lower()), prod_d(a.spec_upper(), b.spec_lower())) ==> q_ge_frac(
r.spec_upper(),
sub_n(a.spec_upper(), b.spec_lower()),
prod_d(a.spec_upper(), b.spec_lower()),
),
{
let lo = Rat::sub_dir(a.lo, b.hi, Dir::Down);
let hi = Rat::sub_dir(a.hi, b.lo, Dir::Up);
proof {
crate::q::lemma_op_widths(a.lo, b.hi);
crate::q::lemma_op_widths(a.hi, b.lo);
if !crate::round::saturated(sub_n(a.lo, b.hi), prod_d(a.lo, b.hi)) {
crate::round::lemma_r2_directed(sub_n(a.lo, b.hi), prod_d(a.lo, b.hi));
}
if !crate::round::saturated(sub_n(a.hi, b.lo), prod_d(a.hi, b.lo)) {
crate::round::lemma_r2_directed(sub_n(a.hi, b.lo), prod_d(a.hi, b.lo));
}
assert((-b.hi.n()) * b.lo.d() <= (-b.lo.n()) * b.hi.d()) by (nonlinear_arith)
requires
b.lo.n() * b.hi.d() <= b.hi.n() * b.lo.d(),
;
lemma_add_endpoint_order(
a.lo.n(),
a.lo.d(),
-b.hi.n(),
b.hi.d(),
a.hi.n(),
a.hi.d(),
-b.lo.n(),
b.lo.d(),
);
assert(
sub_n(a.lo, b.hi) * prod_d(a.hi, b.lo) <= sub_n(a.hi, b.lo) * prod_d(a.lo, b.hi)
) by (nonlinear_arith)
requires
(a.lo.n() * b.hi.d() + (-b.hi.n()) * a.lo.d()) * (a.hi.d() * b.lo.d())
<= (a.hi.n() * b.lo.d() + (-b.lo.n()) * a.hi.d()) * (a.lo.d() * b.hi.d()),
;
lemma_directed_round_order(
sub_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
sub_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
);
}
QI { lo, hi }
}
pub fn neg(a: QI) -> (r: QI)
requires
a.wf(),
ensures
r.wf(),
r.spec_lower().n() == -a.spec_upper().n(),
r.spec_upper().n() == -a.spec_lower().n(),
{
let lo = a.hi.neg();
let hi = a.lo.neg();
proof {
assert(q_le(lo, hi)) by (nonlinear_arith)
requires
a.lo.n() * a.hi.d() <= a.hi.n() * a.lo.d(),
lo.n() == -a.hi.n(),
lo.d() == a.hi.d(),
hi.n() == -a.lo.n(),
hi.d() == a.lo.d(),
;
}
QI { lo, hi }
}
pub fn mul(a: QI, b: QI) -> (r: QI)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
{
let ll_lo = Rat::mul_dir(a.lo, b.lo, Dir::Down);
let lh_lo = Rat::mul_dir(a.lo, b.hi, Dir::Down);
let hl_lo = Rat::mul_dir(a.hi, b.lo, Dir::Down);
let hh_lo = Rat::mul_dir(a.hi, b.hi, Dir::Down);
let ll_hi = Rat::mul_dir(a.lo, b.lo, Dir::Up);
let lh_hi = Rat::mul_dir(a.lo, b.hi, Dir::Up);
let hl_hi = Rat::mul_dir(a.hi, b.lo, Dir::Up);
let hh_hi = Rat::mul_dir(a.hi, b.hi, Dir::Up);
let lo_m1 = Rat::min(ll_lo, lh_lo);
let lo_m2 = Rat::min(hl_lo, hh_lo);
let lo = Rat::min(lo_m1, lo_m2);
let hi_m1 = Rat::max(ll_hi, lh_hi);
let hi_m2 = Rat::max(hl_hi, hh_hi);
let hi = Rat::max(hi_m1, hi_m2);
proof {
crate::q::lemma_op_widths(a.lo, b.lo);
lemma_le_trans(lo, lo_m1, ll_lo);
lemma_directed_round_order(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
);
lemma_le_trans(ll_hi, hi_m1, hi);
lemma_le_trans(ll_lo, ll_hi, hi);
lemma_le_trans(lo, ll_lo, hi);
}
QI { lo, hi }
}
pub fn hull(a: QI, b: QI) -> (r: QI)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r.spec_lower() == a.spec_lower() || r.spec_lower() == b.spec_lower(),
r.spec_upper() == a.spec_upper() || r.spec_upper() == b.spec_upper(),
q_le(r.spec_lower(), a.spec_lower()),
q_le(r.spec_lower(), b.spec_lower()),
q_le(a.spec_upper(), r.spec_upper()),
q_le(b.spec_upper(), r.spec_upper()),
{
let lo = Rat::min(a.lo, b.lo);
let hi = Rat::max(a.hi, b.hi);
proof {
crate::q::lemma_le_trans(lo, a.lo, a.hi);
crate::q::lemma_le_trans(lo, a.hi, hi);
}
QI { lo, hi }
}
}
pub proof fn theorem_qi_exact_width_nonnegative(i: QI)
requires
i.wf(),
ensures
sub_n(i.spec_upper(), i.spec_lower()) >= 0,
prod_d(i.spec_upper(), i.spec_lower()) > 0,
{
reveal(QI::spec_lower);
reveal(QI::spec_upper);
crate::q::lemma_op_widths(i.hi, i.lo);
}
pub proof fn theorem_interval_add_contains(a: QI, b: QI, x: Rat, y: Rat)
requires
a.wf(),
b.wf(),
x.wf(),
y.wf(),
q_le(a.spec_lower(), x),
q_le(x, a.spec_upper()),
q_le(b.spec_lower(), y),
q_le(y, b.spec_upper()),
ensures
add_n(a.spec_lower(), b.spec_lower()) * prod_d(x, y) <= add_n(x, y) * prod_d(a.spec_lower(), b.spec_lower()),
add_n(x, y) * prod_d(a.spec_upper(), b.spec_upper()) <= add_n(a.spec_upper(), b.spec_upper()) * prod_d(x, y),
{
lemma_add_endpoint_order(
a.lo.n(),
a.lo.d(),
b.lo.n(),
b.lo.d(),
x.n(),
x.d(),
y.n(),
y.d(),
);
lemma_add_endpoint_order(x.n(), x.d(), y.n(), y.d(), a.hi.n(), a.hi.d(), b.hi.n(), b.hi.d());
}
pub proof fn theorem_interval_sub_contains(a: QI, b: QI, x: Rat, y: Rat)
requires
a.wf(),
b.wf(),
x.wf(),
y.wf(),
q_le(a.spec_lower(), x),
q_le(x, a.spec_upper()),
q_le(b.spec_lower(), y),
q_le(y, b.spec_upper()),
ensures
sub_n(a.spec_lower(), b.spec_upper()) * prod_d(x, y) <= sub_n(x, y) * prod_d(a.spec_lower(), b.spec_upper()),
sub_n(x, y) * prod_d(a.spec_upper(), b.spec_lower()) <= sub_n(a.spec_upper(), b.spec_lower()) * prod_d(x, y),
{
assert((-b.hi.n()) * y.d() <= (-y.n()) * b.hi.d()) by (nonlinear_arith)
requires
y.n() * b.hi.d() <= b.hi.n() * y.d(),
;
assert((-y.n()) * b.lo.d() <= (-b.lo.n()) * y.d()) by (nonlinear_arith)
requires
b.lo.n() * y.d() <= y.n() * b.lo.d(),
;
lemma_add_endpoint_order(
a.lo.n(),
a.lo.d(),
-b.hi.n(),
b.hi.d(),
x.n(),
x.d(),
-y.n(),
y.d(),
);
lemma_add_endpoint_order(
x.n(),
x.d(),
-y.n(),
y.d(),
a.hi.n(),
a.hi.d(),
-b.lo.n(),
b.lo.d(),
);
assert(sub_n(a.lo, b.hi) * prod_d(x, y) <= sub_n(x, y) * prod_d(a.lo, b.hi)) by (
nonlinear_arith)
requires
(a.lo.n() * b.hi.d() + (-b.hi.n()) * a.lo.d()) * (x.d() * y.d()) <= (
x.n() * y.d() + (-y.n()) * x.d()) * (a.lo.d() * b.hi.d()),
;
assert(sub_n(x, y) * prod_d(a.hi, b.lo) <= sub_n(a.hi, b.lo) * prod_d(x, y)) by (
nonlinear_arith)
requires
(x.n() * y.d() + (-y.n()) * x.d()) * (a.hi.d() * b.lo.d()) <= (
a.hi.n() * b.lo.d() + (-b.lo.n()) * a.hi.d()) * (x.d() * y.d()),
;
}
pub proof fn lemma_add_endpoint_order(
an: int,
ad: int,
bn: int,
bd: int,
xn: int,
xd: int,
yn: int,
yd: int,
)
requires
ad > 0,
bd > 0,
xd > 0,
yd > 0,
an * xd <= xn * ad,
bn * yd <= yn * bd,
ensures
(an * bd + bn * ad) * (xd * yd) <= (xn * yd + yn * xd) * (ad * bd),
{
assert((an * xd) * (bd * yd) <= (xn * ad) * (bd * yd)) by (nonlinear_arith)
requires
an * xd <= xn * ad,
bd > 0,
yd > 0,
;
assert((bn * yd) * (ad * xd) <= (yn * bd) * (ad * xd)) by (nonlinear_arith)
requires
bn * yd <= yn * bd,
ad > 0,
xd > 0,
;
assert((an * bd + bn * ad) * (xd * yd) == (an * bd) * (xd * yd) + (bn * ad) * (xd * yd))
by (nonlinear_arith);
assert((an * bd) * (xd * yd) == (an * xd) * (bd * yd)) by (nonlinear_arith);
assert((bn * ad) * (xd * yd) == (bn * yd) * (ad * xd)) by (nonlinear_arith);
assert((xn * yd + yn * xd) * (ad * bd) == (xn * yd) * (ad * bd) + (yn * xd) * (ad * bd))
by (nonlinear_arith);
assert((xn * yd) * (ad * bd) == (xn * ad) * (bd * yd)) by (nonlinear_arith);
assert((yn * xd) * (ad * bd) == (yn * bd) * (ad * xd)) by (nonlinear_arith);
}
pub proof fn lemma_frac_chain_le(
rln: int,
rld: int,
n1: int,
d1: int,
n2: int,
d2: int,
rhn: int,
rhd: int,
)
requires
rld > 0,
d1 > 0,
d2 > 0,
rhd > 0,
rln * d1 <= n1 * rld,
n1 * d2 <= n2 * d1,
n2 * rhd <= rhn * d2,
ensures
rln * rhd <= rhn * rld,
{
assert((rln * d1) * (d2 * rhd) <= (n1 * rld) * (d2 * rhd)) by (nonlinear_arith)
requires
rln * d1 <= n1 * rld,
d2 > 0,
rhd > 0,
;
assert((n1 * d2) * (rld * rhd) <= (n2 * d1) * (rld * rhd)) by (nonlinear_arith)
requires
n1 * d2 <= n2 * d1,
rld > 0,
rhd > 0,
;
assert((n2 * rhd) * (d1 * rld) <= (rhn * d2) * (d1 * rld)) by (nonlinear_arith)
requires
n2 * rhd <= rhn * d2,
d1 > 0,
rld > 0,
;
assert((rln * d1) * (d2 * rhd) == (rln * rhd) * (d1 * d2)) by (nonlinear_arith);
assert((n1 * rld) * (d2 * rhd) == (n1 * d2) * (rld * rhd)) by (nonlinear_arith);
assert((n2 * d1) * (rld * rhd) == (n2 * rhd) * (d1 * rld)) by (nonlinear_arith);
assert((rhn * d2) * (d1 * rld) == (rhn * rld) * (d1 * d2)) by (nonlinear_arith);
assert((rln * rhd) * (d1 * d2) <= (rhn * rld) * (d1 * d2));
assert(rln * rhd <= rhn * rld) by (nonlinear_arith)
requires
(rln * rhd) * (d1 * d2) <= (rhn * rld) * (d1 * d2),
d1 > 0,
d2 > 0,
;
}
pub proof fn lemma_directed_round_order(n1: int, d1: int, n2: int, d2: int)
requires
d1 > 0,
d2 > 0,
n1 * d2 <= n2 * d1,
ensures
q_le(
crate::round::round_frac(n1, d1, Dir::Down),
crate::round::round_frac(n2, d2, Dir::Up),
),
{
let rlo = crate::round::round_frac(n1, d1, Dir::Down);
let rhi = crate::round::round_frac(n2, d2, Dir::Up);
crate::round::lemma_round_frac_wf(n1, d1, Dir::Down);
crate::round::lemma_round_frac_wf(n2, d2, Dir::Up);
Rat::lemma_from_raw_spec_components((-(MAX_MAG as int)) as i64, 1);
Rat::lemma_from_raw_spec_components(MAX_MAG, 1);
assert(rlo.n() <= max_mag() * rlo.d()) by (nonlinear_arith)
requires
abs_int(rlo.n()) <= max_mag(),
rlo.d() >= 1,
;
assert(rhi.n() >= 0 - max_mag() * rhi.d()) by (nonlinear_arith)
requires
abs_int(rhi.n()) <= max_mag(),
rhi.d() >= 1,
;
if !crate::round::saturated(n1, d1) && !crate::round::saturated(n2, d2) {
crate::round::lemma_r2_directed(n1, d1);
crate::round::lemma_r2_directed(n2, d2);
lemma_frac_chain_le(rlo.n(), rlo.d(), n1, d1, n2, d2, rhi.n(), rhi.d());
assert(q_le(rlo, rhi));
} else if crate::round::saturated(n1, d1) {
assert(n1 != 0 && !magnitude_fits(n1, d1));
if n1 < 0 {
assert(rlo == Rat::from_raw_spec((-(MAX_MAG as int)) as i64, 1));
assert(rlo.n() == 0 - max_mag() && rlo.d() == 1);
assert(q_le(rlo, rhi)) by (nonlinear_arith)
requires
rlo.n() == 0 - max_mag(),
rlo.d() == 1,
rhi.n() >= 0 - max_mag() * rhi.d(),
rhi.d() > 0,
;
} else {
assert(rlo == Rat::from_raw_spec(MAX_MAG, 1));
assert(n2 > max_mag() * d2) by (nonlinear_arith)
requires
n1 > max_mag() * d1,
n1 * d2 <= n2 * d1,
d1 > 0,
d2 > 0,
;
assert(n2 != 0 && !magnitude_fits(n2, d2));
assert(rhi == Rat::from_raw_spec(MAX_MAG, 1));
assert(rlo.n() == max_mag() && rlo.d() == 1);
assert(rhi.n() == max_mag() && rhi.d() == 1);
assert(q_le(rlo, rhi)) by (nonlinear_arith)
requires
rlo.n() == max_mag(),
rlo.d() == 1,
rhi.n() == max_mag(),
rhi.d() == 1,
;
}
} else {
assert(n2 != 0 && !magnitude_fits(n2, d2));
if n2 > 0 {
assert(rhi == Rat::from_raw_spec(MAX_MAG, 1));
assert(rhi.n() == max_mag() && rhi.d() == 1);
assert(q_le(rlo, rhi)) by (nonlinear_arith)
requires
rlo.n() <= max_mag() * rlo.d(),
rlo.d() > 0,
rhi.n() == max_mag(),
rhi.d() == 1,
;
} else {
assert(n1 < 0 - max_mag() * d1) by (nonlinear_arith)
requires
n2 < 0 - max_mag() * d2,
n1 * d2 <= n2 * d1,
d1 > 0,
d2 > 0,
;
assert(false);
}
}
}
pub open spec fn frac_min(n1: int, d1: int, n2: int, d2: int) -> (int, int) {
if n1 * d2 <= n2 * d1 {
(n1, d1)
} else {
(n2, d2)
}
}
pub open spec fn frac_max(n1: int, d1: int, n2: int, d2: int) -> (int, int) {
if n1 * d2 >= n2 * d1 {
(n1, d1)
} else {
(n2, d2)
}
}
pub proof fn lemma_frac_min_le(n1: int, d1: int, n2: int, d2: int)
requires
d1 > 0,
d2 > 0,
ensures
({
let (mn, md) = frac_min(n1, d1, n2, d2);
&&& md > 0
&&& mn * d1 <= n1 * md
&&& mn * d2 <= n2 * md
}),
{
}
pub proof fn lemma_frac_max_ge(n1: int, d1: int, n2: int, d2: int)
requires
d1 > 0,
d2 > 0,
ensures
({
let (mx, mxd) = frac_max(n1, d1, n2, d2);
&&& mxd > 0
&&& n1 * mxd <= mx * d1
&&& n2 * mxd <= mx * d2
}),
{
}
pub proof fn lemma_frac_le_trans(n1: int, d1: int, n2: int, d2: int, n3: int, d3: int)
requires
d1 > 0,
d2 > 0,
d3 > 0,
n1 * d2 <= n2 * d1,
n2 * d3 <= n3 * d2,
ensures
n1 * d3 <= n3 * d1,
{
assert((n1 * d2) * d3 <= (n2 * d1) * d3) by (nonlinear_arith)
requires
n1 * d2 <= n2 * d1,
d3 > 0,
;
assert((n2 * d3) * d1 <= (n3 * d2) * d1) by (nonlinear_arith)
requires
n2 * d3 <= n3 * d2,
d1 > 0,
;
assert((n1 * d2) * d3 == (n1 * d3) * d2) by (nonlinear_arith);
assert((n2 * d1) * d3 == (n2 * d3) * d1) by (nonlinear_arith);
assert((n3 * d2) * d1 == (n3 * d1) * d2) by (nonlinear_arith);
assert((n1 * d3) * d2 <= (n3 * d1) * d2);
assert(n1 * d3 <= n3 * d1) by (nonlinear_arith)
requires
(n1 * d3) * d2 <= (n3 * d1) * d2,
d2 > 0,
;
}
pub proof fn lemma_frac_min4_le(n1: int, d1: int, n2: int, d2: int, n3: int, d3: int, n4: int, d4: int)
requires
d1 > 0,
d2 > 0,
d3 > 0,
d4 > 0,
ensures
({
let m1 = frac_min(n1, d1, n2, d2);
let m2 = frac_min(n3, d3, n4, d4);
let (mn, md) = frac_min(m1.0, m1.1, m2.0, m2.1);
&&& md > 0
&&& mn * d1 <= n1 * md
&&& mn * d2 <= n2 * md
&&& mn * d3 <= n3 * md
&&& mn * d4 <= n4 * md
}),
{
lemma_frac_min_le(n1, d1, n2, d2);
lemma_frac_min_le(n3, d3, n4, d4);
let m1 = frac_min(n1, d1, n2, d2);
let m2 = frac_min(n3, d3, n4, d4);
lemma_frac_min_le(m1.0, m1.1, m2.0, m2.1);
let (mn, md) = frac_min(m1.0, m1.1, m2.0, m2.1);
lemma_frac_le_trans(mn, md, m1.0, m1.1, n1, d1);
lemma_frac_le_trans(mn, md, m1.0, m1.1, n2, d2);
lemma_frac_le_trans(mn, md, m2.0, m2.1, n3, d3);
lemma_frac_le_trans(mn, md, m2.0, m2.1, n4, d4);
}
pub proof fn lemma_frac_max4_ge(n1: int, d1: int, n2: int, d2: int, n3: int, d3: int, n4: int, d4: int)
requires
d1 > 0,
d2 > 0,
d3 > 0,
d4 > 0,
ensures
({
let m1 = frac_max(n1, d1, n2, d2);
let m2 = frac_max(n3, d3, n4, d4);
let (mx, mxd) = frac_max(m1.0, m1.1, m2.0, m2.1);
&&& mxd > 0
&&& n1 * mxd <= mx * d1
&&& n2 * mxd <= mx * d2
&&& n3 * mxd <= mx * d3
&&& n4 * mxd <= mx * d4
}),
{
lemma_frac_max_ge(n1, d1, n2, d2);
lemma_frac_max_ge(n3, d3, n4, d4);
let m1 = frac_max(n1, d1, n2, d2);
let m2 = frac_max(n3, d3, n4, d4);
lemma_frac_max_ge(m1.0, m1.1, m2.0, m2.1);
let (mx, mxd) = frac_max(m1.0, m1.1, m2.0, m2.1);
lemma_frac_le_trans(n1, d1, m1.0, m1.1, mx, mxd);
lemma_frac_le_trans(n2, d2, m1.0, m1.1, mx, mxd);
lemma_frac_le_trans(n3, d3, m2.0, m2.1, mx, mxd);
lemma_frac_le_trans(n4, d4, m2.0, m2.1, mx, mxd);
}
pub proof fn lemma_mul_scale_order(lo: Rat, hi: Rat, x: Rat, c: Rat)
requires
lo.wf(),
hi.wf(),
x.wf(),
c.wf(),
q_le(lo, x),
q_le(x, hi),
ensures
c.n() >= 0 ==> (mul_n(lo, c) * prod_d(x, c) <= mul_n(x, c) * prod_d(lo, c)
&& mul_n(x, c) * prod_d(hi, c) <= mul_n(hi, c) * prod_d(x, c)),
c.n() < 0 ==> (mul_n(hi, c) * prod_d(x, c) <= mul_n(x, c) * prod_d(hi, c)
&& mul_n(x, c) * prod_d(lo, c) <= mul_n(lo, c) * prod_d(x, c)),
{
if c.n() >= 0 {
assert((lo.n() * x.d()) * (c.n() * c.d()) <= (x.n() * lo.d()) * (c.n() * c.d()))
by (nonlinear_arith)
requires
lo.n() * x.d() <= x.n() * lo.d(),
c.n() >= 0,
c.d() > 0,
;
assert((lo.n() * x.d()) * (c.n() * c.d()) == mul_n(lo, c) * prod_d(x, c))
by (nonlinear_arith);
assert((x.n() * lo.d()) * (c.n() * c.d()) == mul_n(x, c) * prod_d(lo, c))
by (nonlinear_arith);
assert((x.n() * hi.d()) * (c.n() * c.d()) <= (hi.n() * x.d()) * (c.n() * c.d()))
by (nonlinear_arith)
requires
x.n() * hi.d() <= hi.n() * x.d(),
c.n() >= 0,
c.d() > 0,
;
assert((x.n() * hi.d()) * (c.n() * c.d()) == mul_n(x, c) * prod_d(hi, c))
by (nonlinear_arith);
assert((hi.n() * x.d()) * (c.n() * c.d()) == mul_n(hi, c) * prod_d(x, c))
by (nonlinear_arith);
} else {
assert((x.n() * hi.d()) * (c.n() * c.d()) >= (hi.n() * x.d()) * (c.n() * c.d()))
by (nonlinear_arith)
requires
x.n() * hi.d() <= hi.n() * x.d(),
c.n() < 0,
c.d() > 0,
;
assert((x.n() * hi.d()) * (c.n() * c.d()) == mul_n(x, c) * prod_d(hi, c))
by (nonlinear_arith);
assert((hi.n() * x.d()) * (c.n() * c.d()) == mul_n(hi, c) * prod_d(x, c))
by (nonlinear_arith);
assert((lo.n() * x.d()) * (c.n() * c.d()) >= (x.n() * lo.d()) * (c.n() * c.d()))
by (nonlinear_arith)
requires
lo.n() * x.d() <= x.n() * lo.d(),
c.n() < 0,
c.d() > 0,
;
assert((lo.n() * x.d()) * (c.n() * c.d()) == mul_n(lo, c) * prod_d(x, c))
by (nonlinear_arith);
assert((x.n() * lo.d()) * (c.n() * c.d()) == mul_n(x, c) * prod_d(lo, c))
by (nonlinear_arith);
}
}
pub proof fn lemma_mul_corner_lower(a: QI, b: QI, x: Rat, y: Rat)
requires
a.wf(),
b.wf(),
x.wf(),
y.wf(),
q_le(a.spec_lower(), x),
q_le(x, a.spec_upper()),
q_le(b.spec_lower(), y),
q_le(y, b.spec_upper()),
ensures
(y.n() >= 0 && a.spec_lower().n() >= 0) ==> mul_n(a.spec_lower(), b.spec_lower()) * prod_d(x, y) <= mul_n(x, y) * prod_d(
a.spec_lower(),
b.spec_lower(),
),
(y.n() >= 0 && a.spec_lower().n() < 0) ==> mul_n(a.spec_lower(), b.spec_upper()) * prod_d(x, y) <= mul_n(x, y) * prod_d(
a.spec_lower(),
b.spec_upper(),
),
(y.n() < 0 && a.spec_upper().n() >= 0) ==> mul_n(a.spec_upper(), b.spec_lower()) * prod_d(x, y) <= mul_n(x, y) * prod_d(
a.spec_upper(),
b.spec_lower(),
),
(y.n() < 0 && a.spec_upper().n() < 0) ==> mul_n(a.spec_upper(), b.spec_upper()) * prod_d(x, y) <= mul_n(x, y) * prod_d(
a.spec_upper(),
b.spec_upper(),
),
{
crate::q::lemma_op_widths(a.lo, b.lo);
crate::q::lemma_op_widths(a.lo, b.hi);
crate::q::lemma_op_widths(a.hi, b.lo);
crate::q::lemma_op_widths(a.hi, b.hi);
crate::q::lemma_op_widths(a.lo, y);
crate::q::lemma_op_widths(a.hi, y);
crate::q::lemma_op_widths(x, y);
lemma_mul_scale_order(a.lo, a.hi, x, y);
if y.n() >= 0 {
if a.lo.n() >= 0 {
lemma_mul_scale_order(b.lo, b.hi, y, a.lo);
assert(mul_n(b.lo, a.lo) * prod_d(y, a.lo) <= mul_n(y, a.lo) * prod_d(b.lo, a.lo));
assert(mul_n(b.lo, a.lo) == mul_n(a.lo, b.lo)) by (nonlinear_arith);
assert(mul_n(y, a.lo) == mul_n(a.lo, y)) by (nonlinear_arith);
assert(prod_d(y, a.lo) == prod_d(a.lo, y));
assert(prod_d(b.lo, a.lo) == prod_d(a.lo, b.lo));
lemma_frac_le_trans(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, y),
prod_d(a.lo, y),
mul_n(x, y),
prod_d(x, y),
);
} else {
lemma_mul_scale_order(b.lo, b.hi, y, a.lo);
assert(mul_n(b.hi, a.lo) * prod_d(y, a.lo) <= mul_n(y, a.lo) * prod_d(b.hi, a.lo));
assert(mul_n(b.hi, a.lo) == mul_n(a.lo, b.hi)) by (nonlinear_arith);
assert(mul_n(y, a.lo) == mul_n(a.lo, y)) by (nonlinear_arith);
assert(prod_d(y, a.lo) == prod_d(a.lo, y));
assert(prod_d(b.hi, a.lo) == prod_d(a.lo, b.hi));
lemma_frac_le_trans(
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
mul_n(a.lo, y),
prod_d(a.lo, y),
mul_n(x, y),
prod_d(x, y),
);
}
} else {
if a.hi.n() >= 0 {
lemma_mul_scale_order(b.lo, b.hi, y, a.hi);
assert(mul_n(b.lo, a.hi) * prod_d(y, a.hi) <= mul_n(y, a.hi) * prod_d(b.lo, a.hi));
assert(mul_n(b.lo, a.hi) == mul_n(a.hi, b.lo)) by (nonlinear_arith);
assert(mul_n(y, a.hi) == mul_n(a.hi, y)) by (nonlinear_arith);
assert(prod_d(y, a.hi) == prod_d(a.hi, y));
assert(prod_d(b.lo, a.hi) == prod_d(a.hi, b.lo));
lemma_frac_le_trans(
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
mul_n(a.hi, y),
prod_d(a.hi, y),
mul_n(x, y),
prod_d(x, y),
);
} else {
lemma_mul_scale_order(b.lo, b.hi, y, a.hi);
assert(mul_n(b.hi, a.hi) * prod_d(y, a.hi) <= mul_n(y, a.hi) * prod_d(b.hi, a.hi));
assert(mul_n(b.hi, a.hi) == mul_n(a.hi, b.hi)) by (nonlinear_arith);
assert(mul_n(y, a.hi) == mul_n(a.hi, y)) by (nonlinear_arith);
assert(prod_d(y, a.hi) == prod_d(a.hi, y));
assert(prod_d(b.hi, a.hi) == prod_d(a.hi, b.hi));
lemma_frac_le_trans(
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
mul_n(a.hi, y),
prod_d(a.hi, y),
mul_n(x, y),
prod_d(x, y),
);
}
}
}
pub proof fn lemma_mul_corner_upper(a: QI, b: QI, x: Rat, y: Rat)
requires
a.wf(),
b.wf(),
x.wf(),
y.wf(),
q_le(a.spec_lower(), x),
q_le(x, a.spec_upper()),
q_le(b.spec_lower(), y),
q_le(y, b.spec_upper()),
ensures
(y.n() >= 0 && a.spec_upper().n() >= 0) ==> mul_n(x, y) * prod_d(a.spec_upper(), b.spec_upper()) <= mul_n(
a.spec_upper(),
b.spec_upper(),
) * prod_d(x, y),
(y.n() >= 0 && a.spec_upper().n() < 0) ==> mul_n(x, y) * prod_d(a.spec_upper(), b.spec_lower()) <= mul_n(
a.spec_upper(),
b.spec_lower(),
) * prod_d(x, y),
(y.n() < 0 && a.spec_lower().n() >= 0) ==> mul_n(x, y) * prod_d(a.spec_lower(), b.spec_upper()) <= mul_n(
a.spec_lower(),
b.spec_upper(),
) * prod_d(x, y),
(y.n() < 0 && a.spec_lower().n() < 0) ==> mul_n(x, y) * prod_d(a.spec_lower(), b.spec_lower()) <= mul_n(
a.spec_lower(),
b.spec_lower(),
) * prod_d(x, y),
{
crate::q::lemma_op_widths(a.lo, b.lo);
crate::q::lemma_op_widths(a.lo, b.hi);
crate::q::lemma_op_widths(a.hi, b.lo);
crate::q::lemma_op_widths(a.hi, b.hi);
crate::q::lemma_op_widths(a.lo, y);
crate::q::lemma_op_widths(a.hi, y);
crate::q::lemma_op_widths(x, y);
lemma_mul_scale_order(a.lo, a.hi, x, y);
if y.n() >= 0 {
if a.hi.n() >= 0 {
lemma_mul_scale_order(b.lo, b.hi, y, a.hi);
assert(mul_n(y, a.hi) * prod_d(b.hi, a.hi) <= mul_n(b.hi, a.hi) * prod_d(y, a.hi));
assert(mul_n(b.hi, a.hi) == mul_n(a.hi, b.hi)) by (nonlinear_arith);
assert(mul_n(y, a.hi) == mul_n(a.hi, y)) by (nonlinear_arith);
assert(prod_d(y, a.hi) == prod_d(a.hi, y));
assert(prod_d(b.hi, a.hi) == prod_d(a.hi, b.hi));
lemma_frac_le_trans(
mul_n(x, y),
prod_d(x, y),
mul_n(a.hi, y),
prod_d(a.hi, y),
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
} else {
lemma_mul_scale_order(b.lo, b.hi, y, a.hi);
assert(mul_n(y, a.hi) * prod_d(b.lo, a.hi) <= mul_n(b.lo, a.hi) * prod_d(y, a.hi));
assert(mul_n(b.lo, a.hi) == mul_n(a.hi, b.lo)) by (nonlinear_arith);
assert(mul_n(y, a.hi) == mul_n(a.hi, y)) by (nonlinear_arith);
assert(prod_d(y, a.hi) == prod_d(a.hi, y));
assert(prod_d(b.lo, a.hi) == prod_d(a.hi, b.lo));
lemma_frac_le_trans(
mul_n(x, y),
prod_d(x, y),
mul_n(a.hi, y),
prod_d(a.hi, y),
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
);
}
} else {
if a.lo.n() >= 0 {
lemma_mul_scale_order(b.lo, b.hi, y, a.lo);
assert(mul_n(y, a.lo) * prod_d(b.hi, a.lo) <= mul_n(b.hi, a.lo) * prod_d(y, a.lo));
assert(mul_n(b.hi, a.lo) == mul_n(a.lo, b.hi)) by (nonlinear_arith);
assert(mul_n(y, a.lo) == mul_n(a.lo, y)) by (nonlinear_arith);
assert(prod_d(y, a.lo) == prod_d(a.lo, y));
assert(prod_d(b.hi, a.lo) == prod_d(a.lo, b.hi));
lemma_frac_le_trans(
mul_n(x, y),
prod_d(x, y),
mul_n(a.lo, y),
prod_d(a.lo, y),
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
);
} else {
lemma_mul_scale_order(b.lo, b.hi, y, a.lo);
assert(mul_n(y, a.lo) * prod_d(b.lo, a.lo) <= mul_n(b.lo, a.lo) * prod_d(y, a.lo));
assert(mul_n(b.lo, a.lo) == mul_n(a.lo, b.lo)) by (nonlinear_arith);
assert(mul_n(y, a.lo) == mul_n(a.lo, y)) by (nonlinear_arith);
assert(prod_d(y, a.lo) == prod_d(a.lo, y));
assert(prod_d(b.lo, a.lo) == prod_d(a.lo, b.lo));
lemma_frac_le_trans(
mul_n(x, y),
prod_d(x, y),
mul_n(a.lo, y),
prod_d(a.lo, y),
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
);
}
}
}
pub proof fn theorem_interval_mul_contains(a: QI, b: QI, x: Rat, y: Rat)
requires
a.wf(),
b.wf(),
x.wf(),
y.wf(),
q_le(a.spec_lower(), x),
q_le(x, a.spec_upper()),
q_le(b.spec_lower(), y),
q_le(y, b.spec_upper()),
ensures
({
let (mn, md) = frac_min(
frac_min(
mul_n(a.spec_lower(), b.spec_lower()),
prod_d(a.spec_lower(), b.spec_lower()),
mul_n(a.spec_lower(), b.spec_upper()),
prod_d(a.spec_lower(), b.spec_upper()),
).0,
frac_min(
mul_n(a.spec_lower(), b.spec_lower()),
prod_d(a.spec_lower(), b.spec_lower()),
mul_n(a.spec_lower(), b.spec_upper()),
prod_d(a.spec_lower(), b.spec_upper()),
).1,
frac_min(
mul_n(a.spec_upper(), b.spec_lower()),
prod_d(a.spec_upper(), b.spec_lower()),
mul_n(a.spec_upper(), b.spec_upper()),
prod_d(a.spec_upper(), b.spec_upper()),
).0,
frac_min(
mul_n(a.spec_upper(), b.spec_lower()),
prod_d(a.spec_upper(), b.spec_lower()),
mul_n(a.spec_upper(), b.spec_upper()),
prod_d(a.spec_upper(), b.spec_upper()),
).1,
);
mn * prod_d(x, y) <= mul_n(x, y) * md
}),
({
let (mx, mxd) = frac_max(
frac_max(
mul_n(a.spec_lower(), b.spec_lower()),
prod_d(a.spec_lower(), b.spec_lower()),
mul_n(a.spec_lower(), b.spec_upper()),
prod_d(a.spec_lower(), b.spec_upper()),
).0,
frac_max(
mul_n(a.spec_lower(), b.spec_lower()),
prod_d(a.spec_lower(), b.spec_lower()),
mul_n(a.spec_lower(), b.spec_upper()),
prod_d(a.spec_lower(), b.spec_upper()),
).1,
frac_max(
mul_n(a.spec_upper(), b.spec_lower()),
prod_d(a.spec_upper(), b.spec_lower()),
mul_n(a.spec_upper(), b.spec_upper()),
prod_d(a.spec_upper(), b.spec_upper()),
).0,
frac_max(
mul_n(a.spec_upper(), b.spec_lower()),
prod_d(a.spec_upper(), b.spec_lower()),
mul_n(a.spec_upper(), b.spec_upper()),
prod_d(a.spec_upper(), b.spec_upper()),
).1,
);
mul_n(x, y) * mxd <= mx * prod_d(x, y)
}),
{
crate::q::lemma_op_widths(a.lo, b.lo);
crate::q::lemma_op_widths(a.lo, b.hi);
crate::q::lemma_op_widths(a.hi, b.lo);
crate::q::lemma_op_widths(a.hi, b.hi);
crate::q::lemma_op_widths(x, y);
lemma_frac_min4_le(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
lemma_frac_max4_ge(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
lemma_mul_corner_lower(a, b, x, y);
lemma_mul_corner_upper(a, b, x, y);
let m1 = frac_min(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
);
let m2 = frac_min(
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
let (mn, md) = frac_min(m1.0, m1.1, m2.0, m2.1);
let mx1 = frac_max(
mul_n(a.lo, b.lo),
prod_d(a.lo, b.lo),
mul_n(a.lo, b.hi),
prod_d(a.lo, b.hi),
);
let mx2 = frac_max(
mul_n(a.hi, b.lo),
prod_d(a.hi, b.lo),
mul_n(a.hi, b.hi),
prod_d(a.hi, b.hi),
);
let (mx, mxd) = frac_max(mx1.0, mx1.1, mx2.0, mx2.1);
if y.n() >= 0 && a.lo.n() >= 0 {
lemma_frac_le_trans(mn, md, mul_n(a.lo, b.lo), prod_d(a.lo, b.lo), mul_n(x, y), prod_d(x, y));
} else if y.n() >= 0 {
lemma_frac_le_trans(mn, md, mul_n(a.lo, b.hi), prod_d(a.lo, b.hi), mul_n(x, y), prod_d(x, y));
} else if a.hi.n() >= 0 {
lemma_frac_le_trans(mn, md, mul_n(a.hi, b.lo), prod_d(a.hi, b.lo), mul_n(x, y), prod_d(x, y));
} else {
lemma_frac_le_trans(mn, md, mul_n(a.hi, b.hi), prod_d(a.hi, b.hi), mul_n(x, y), prod_d(x, y));
}
if y.n() >= 0 && a.hi.n() >= 0 {
lemma_frac_le_trans(mul_n(x, y), prod_d(x, y), mul_n(a.hi, b.hi), prod_d(a.hi, b.hi), mx, mxd);
} else if y.n() >= 0 {
lemma_frac_le_trans(mul_n(x, y), prod_d(x, y), mul_n(a.hi, b.lo), prod_d(a.hi, b.lo), mx, mxd);
} else if a.lo.n() >= 0 {
lemma_frac_le_trans(mul_n(x, y), prod_d(x, y), mul_n(a.lo, b.hi), prod_d(a.lo, b.hi), mx, mxd);
} else {
lemma_frac_le_trans(mul_n(x, y), prod_d(x, y), mul_n(a.lo, b.lo), prod_d(a.lo, b.lo), mx, mxd);
}
}
}