use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::gcd::*;
#[allow(unused_imports)]
use crate::model::*;
#[allow(unused_imports)]
use crate::q::*;
#[allow(unused_imports)]
use crate::round::*;
#[allow(unused_imports)]
use crate::types::{Dir, Rat};
verus! {
pub proof fn lemma_euclid(a: nat, b: nat, c: nat)
requires
a > 0,
gcd_nat(a, b) == 1,
divides(a as int, (b * c) as int),
ensures
divides(a as int, c as int),
{
if c == 0 {
crate::model::lemma_divides_basic(a as int);
} else {
lemma_gcd_scale(c, a, b);
assert(gcd_nat((c * a) as nat, (c * b) as nat) == c);
crate::model::lemma_divides_basic(a as int);
assert(divides(a as int, (c * a) as int)) by {
assert((c * a) as int == (a as int) * (c as int)) by (nonlinear_arith);
}
assert(divides(a as int, (c * b) as int)) by {
assert((c * b) as int == (b * c) as int) by (nonlinear_arith);
}
lemma_gcd_greatest((c * a) as nat, (c * b) as nat, a as int);
}
}
pub proof fn lemma_canonical_eq(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
ensures
q_eq(a, b) <==> a == b,
{
if q_eq(a, b) {
crate::q::lemma_gcd_sym(abs_int(a.n()) as nat, a.d() as nat);
crate::q::lemma_gcd_sym(abs_int(b.n()) as nat, b.d() as nat);
assert(divides(a.d(), a.n() * b.d())) by {
assert(a.n() * b.d() == b.n() * a.d());
assert(b.n() * a.d() == a.d() * b.n()) by (nonlinear_arith);
assert(b.n() * a.d() == a.d() * b.n());
}
assert(divides(a.d(), abs_int(a.n()) * b.d())) by {
let ka = choose|k: int| a.n() * b.d() == #[trigger] (a.d() * k);
if a.n() < 0 {
assert(abs_int(a.n()) * b.d() == a.d() * (-ka)) by (nonlinear_arith)
requires
a.n() * b.d() == a.d() * ka,
abs_int(a.n()) == -a.n(),
;
}
}
lemma_euclid(a.d() as nat, abs_int(a.n()) as nat, b.d() as nat);
assert(divides(b.d(), b.n() * a.d())) by {
assert(b.n() * a.d() == a.n() * b.d());
assert(a.n() * b.d() == b.d() * a.n()) by (nonlinear_arith);
assert(a.n() * b.d() == b.d() * a.n());
}
assert(divides(b.d(), abs_int(b.n()) * a.d())) by {
let kb = choose|k: int| b.n() * a.d() == #[trigger] (b.d() * k);
if b.n() < 0 {
assert(abs_int(b.n()) * a.d() == b.d() * (-kb)) by (nonlinear_arith)
requires
b.n() * a.d() == b.d() * kb,
abs_int(b.n()) == -b.n(),
;
}
}
lemma_euclid(b.d() as nat, abs_int(b.n()) as nat, a.d() as nat);
lemma_divides_le(a.d(), b.d());
lemma_divides_le(b.d(), a.d());
assert(a.d() == b.d());
assert(a.n() == b.n()) by (nonlinear_arith)
requires
a.d() == b.d(),
a.d() > 0,
a.n() * b.d() == b.n() * a.d(),
;
Rat::lemma_extensional(a, b);
}
}
pub proof fn theorem_add_commutative(a: Rat, b: Rat, dir: Dir)
requires
a.wf(),
b.wf(),
ensures
round_frac(add_n(a, b), prod_d(a, b), dir) == round_frac(add_n(b, a), prod_d(b, a), dir),
{
assert(add_n(a, b) == add_n(b, a));
assert(prod_d(a, b) == prod_d(b, a)) by (nonlinear_arith);
}
pub proof fn theorem_mul_commutative(a: Rat, b: Rat, dir: Dir)
requires
a.wf(),
b.wf(),
ensures
round_frac(mul_n(a, b), prod_d(a, b), dir) == round_frac(mul_n(b, a), prod_d(b, a), dir),
{
assert(mul_n(a, b) == mul_n(b, a)) by (nonlinear_arith);
assert(prod_d(a, b) == prod_d(b, a)) by (nonlinear_arith);
}
pub proof fn theorem_exact_path_is_exact(n: int, d: int, dir: Dir)
requires
d > 0,
exact_path(n, d),
ensures
q_is(round_frac(n, d, dir), n, d),
round_frac(n, d, dir).wf(),
{
crate::round::lemma_r1_identity(n, d, dir);
}
pub proof fn theorem_add_associative_exact(a: Rat, b: Rat, c: Rat, dir: Dir)
requires
a.wf(),
b.wf(),
c.wf(),
exact_path(add_n(a, b), prod_d(a, b)),
exact_path(add_n(b, c), prod_d(b, c)),
({
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
&&& exact_path(add_n(ab, c), prod_d(ab, c))
&&& exact_path(add_n(a, bc), prod_d(a, bc))
}),
ensures
({
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
let left = round_frac(add_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(add_n(a, bc), prod_d(a, bc), dir);
q_eq(left, right)
}),
{
crate::q::lemma_op_widths(a, b);
crate::q::lemma_op_widths(b, c);
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
crate::round::lemma_round_frac_wf(add_n(a, b), prod_d(a, b), dir);
crate::round::lemma_round_frac_wf(add_n(b, c), prod_d(b, c), dir);
theorem_exact_path_is_exact(add_n(a, b), prod_d(a, b), dir);
theorem_exact_path_is_exact(add_n(b, c), prod_d(b, c), dir);
crate::q::lemma_op_widths(ab, c);
crate::q::lemma_op_widths(a, bc);
let left = round_frac(add_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(add_n(a, bc), prod_d(a, bc), dir);
crate::round::lemma_round_frac_wf(add_n(ab, c), prod_d(ab, c), dir);
crate::round::lemma_round_frac_wf(add_n(a, bc), prod_d(a, bc), dir);
theorem_exact_path_is_exact(add_n(ab, c), prod_d(ab, c), dir);
theorem_exact_path_is_exact(add_n(a, bc), prod_d(a, bc), dir);
lemma_add_assoc_exact_values(a, b, c, ab, bc, left, right);
}
pub proof fn lemma_cancel_pos(x: int, y: int, c: int)
requires
c > 0,
x * c == y * c,
ensures
x == y,
{
assert(x == y) by (nonlinear_arith)
requires
c > 0,
x * c == y * c,
;
}
pub proof fn lemma_same_value_eq(x: Rat, y: Rat, n: int, d: int)
requires
d > 0,
x.d() > 0,
y.d() > 0,
q_is(x, n, d),
q_is(y, n, d),
ensures
q_eq(x, y),
{
assert((x.n() * y.d()) * d == (x.n() * d) * y.d()) by (nonlinear_arith);
assert((y.n() * x.d()) * d == (y.n() * d) * x.d()) by (nonlinear_arith);
assert((x.n() * d) * y.d() == (n * x.d()) * y.d());
assert((y.n() * d) * x.d() == (n * y.d()) * x.d());
assert((n * x.d()) * y.d() == (n * y.d()) * x.d()) by (nonlinear_arith);
lemma_cancel_pos(x.n() * y.d(), y.n() * x.d(), d);
}
pub proof fn lemma_left_assoc_value(a: Rat, b: Rat, c: Rat, ab: Rat, left: Rat, sn: int, sd: int)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
left.wf(),
q_is(ab, add_n(a, b), prod_d(a, b)),
q_is(left, add_n(ab, c), prod_d(ab, c)),
sn == (a.n() * b.d() + b.n() * a.d()) * c.d() + c.n() * (a.d() * b.d()),
sd == (a.d() * b.d()) * c.d(),
ensures
q_is(left, sn, sd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let ld = left.d();
let nab = a.n() * bd + b.n() * ad;
let x = nab * cd;
let y = c.n() * (ad * bd);
assert((left.n() * (ab.d() * cd)) * (ad * bd) == ((ab.n() * cd + c.n() * ab.d()) * ld) * (ad
* bd));
assert((left.n() * (ab.d() * cd)) * (ad * bd) == ab.d() * (left.n() * ((ad * bd) * cd)))
by (nonlinear_arith);
assert(left.n() * ((ad * bd) * cd) == left.n() * sd);
assert(((ab.n() * cd + c.n() * ab.d()) * ld) * (ad * bd) == ((ab.n() * cd) * ld) * (ad * bd)
+ ((c.n() * ab.d()) * ld) * (ad * bd)) by (nonlinear_arith);
assert(((ab.n() * cd) * ld) * (ad * bd) == (ab.n() * (ad * bd)) * (cd * ld))
by (nonlinear_arith);
assert(((c.n() * ab.d()) * ld) * (ad * bd) == ab.d() * (y * ld)) by (nonlinear_arith)
requires
y == c.n() * (ad * bd),
;
assert(ab.n() * (ad * bd) == nab * ab.d());
assert((ab.n() * (ad * bd)) * (cd * ld) == (nab * ab.d()) * (cd * ld));
assert((nab * ab.d()) * (cd * ld) == ab.d() * (x * ld)) by (nonlinear_arith)
requires
x == nab * cd,
;
assert(ab.d() * (x * ld) + ab.d() * (y * ld) == ab.d() * ((x + y) * ld)) by (nonlinear_arith);
assert(x + y == sn);
assert(ab.d() * (left.n() * sd) == ab.d() * (sn * ld));
assert((left.n() * sd) * ab.d() == (sn * ld) * ab.d()) by (nonlinear_arith)
requires
ab.d() * (left.n() * sd) == ab.d() * (sn * ld),
;
lemma_cancel_pos(left.n() * sd, sn * ld, ab.d());
}
#[verifier::rlimit(20)]
pub proof fn lemma_right_assoc_value(a: Rat, b: Rat, c: Rat, bc: Rat, right: Rat, sn: int, sd: int)
requires
a.wf(),
b.wf(),
c.wf(),
bc.wf(),
right.wf(),
q_is(bc, add_n(b, c), prod_d(b, c)),
q_is(right, add_n(a, bc), prod_d(a, bc)),
sn == (a.n() * b.d() + b.n() * a.d()) * c.d() + c.n() * (a.d() * b.d()),
sd == (a.d() * b.d()) * c.d(),
ensures
q_is(right, sn, sd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let rd = right.d();
let nbc = b.n() * cd + c.n() * bd;
let x = a.n() * (bd * cd);
let y = nbc * ad;
assert((right.n() * (ad * bc.d())) * (bd * cd) == ((a.n() * bc.d() + bc.n() * ad) * rd) * (bd
* cd));
assert((right.n() * (ad * bc.d())) * (bd * cd) == bc.d() * (right.n() * ((ad * bd) * cd)))
by (nonlinear_arith);
assert(right.n() * ((ad * bd) * cd) == right.n() * sd);
assert(((a.n() * bc.d() + bc.n() * ad) * rd) * (bd * cd) == ((a.n() * bc.d()) * rd) * (bd * cd)
+ ((bc.n() * ad) * rd) * (bd * cd)) by (nonlinear_arith);
assert(((a.n() * bc.d()) * rd) * (bd * cd) == bc.d() * (x * rd)) by (nonlinear_arith)
requires
x == a.n() * (bd * cd),
;
assert(((bc.n() * ad) * rd) * (bd * cd) == (bc.n() * (bd * cd)) * (ad * rd))
by (nonlinear_arith);
assert(bc.n() * (bd * cd) == nbc * bc.d());
assert((bc.n() * (bd * cd)) * (ad * rd) == (nbc * bc.d()) * (ad * rd));
assert((nbc * bc.d()) * (ad * rd) == bc.d() * (y * rd)) by (nonlinear_arith)
requires
y == nbc * ad,
;
assert(bc.d() * (x * rd) + bc.d() * (y * rd) == bc.d() * ((x + y) * rd)) by (nonlinear_arith);
assert(x + y == sn) by (nonlinear_arith)
requires
x == a.n() * (bd * cd),
y == (b.n() * cd + c.n() * bd) * ad,
sn == (a.n() * bd + b.n() * ad) * cd + c.n() * (ad * bd),
;
assert(bc.d() * (right.n() * sd) == bc.d() * (sn * rd));
assert((right.n() * sd) * bc.d() == (sn * rd) * bc.d()) by (nonlinear_arith)
requires
bc.d() * (right.n() * sd) == bc.d() * (sn * rd),
;
lemma_cancel_pos(right.n() * sd, sn * rd, bc.d());
}
pub proof fn lemma_add_assoc_exact_values(a: Rat, b: Rat, c: Rat, ab: Rat, bc: Rat, left: Rat, right: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
bc.wf(),
left.wf(),
right.wf(),
q_is(ab, add_n(a, b), prod_d(a, b)),
q_is(bc, add_n(b, c), prod_d(b, c)),
q_is(left, add_n(ab, c), prod_d(ab, c)),
q_is(right, add_n(a, bc), prod_d(a, bc)),
ensures
q_eq(left, right),
{
let sn = (a.n() * b.d() + b.n() * a.d()) * c.d() + c.n() * (a.d() * b.d());
let sd = (a.d() * b.d()) * c.d();
assert(sd > 0) by (nonlinear_arith)
requires
sd == (a.d() * b.d()) * c.d(),
a.d() > 0,
b.d() > 0,
c.d() > 0,
;
lemma_left_assoc_value(a, b, c, ab, left, sn, sd);
lemma_right_assoc_value(a, b, c, bc, right, sn, sd);
lemma_same_value_eq(left, right, sn, sd);
}
pub proof fn theorem_mul_associative_exact(a: Rat, b: Rat, c: Rat, ab: Rat, bc: Rat, left: Rat, right: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
bc.wf(),
left.wf(),
right.wf(),
q_is(ab, mul_n(a, b), prod_d(a, b)),
q_is(bc, mul_n(b, c), prod_d(b, c)),
q_is(left, mul_n(ab, c), prod_d(ab, c)),
q_is(right, mul_n(a, bc), prod_d(a, bc)),
ensures
q_eq(left, right),
{
let pn = (a.n() * b.n()) * c.n();
let pd = (a.d() * b.d()) * c.d();
assert(pd > 0) by (nonlinear_arith)
requires
pd == (a.d() * b.d()) * c.d(),
a.d() > 0,
b.d() > 0,
c.d() > 0,
;
lemma_mul_chain_value(a, b, c, ab, left, pn, pd);
lemma_mul_chain_value_right(a, b, c, bc, right, pn, pd);
lemma_same_value_eq(left, right, pn, pd);
}
pub proof fn lemma_mul_chain_value(a: Rat, b: Rat, c: Rat, ab: Rat, left: Rat, pn: int, pd: int)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
left.wf(),
q_is(ab, mul_n(a, b), prod_d(a, b)),
q_is(left, mul_n(ab, c), prod_d(ab, c)),
pn == (a.n() * b.n()) * c.n(),
pd == (a.d() * b.d()) * c.d(),
ensures
q_is(left, pn, pd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let ld = left.d();
assert(left.n() * (ab.d() * cd) == (ab.n() * c.n()) * ld);
assert((left.n() * (ab.d() * cd)) * (ad * bd) == ((ab.n() * c.n()) * ld) * (ad * bd));
assert((left.n() * (ab.d() * cd)) * (ad * bd) == ab.d() * (left.n() * ((ad * bd) * cd)))
by (nonlinear_arith);
assert(left.n() * ((ad * bd) * cd) == left.n() * pd);
assert(((ab.n() * c.n()) * ld) * (ad * bd) == (ab.n() * (ad * bd)) * (c.n() * ld))
by (nonlinear_arith);
assert(ab.n() * (ad * bd) == (a.n() * b.n()) * ab.d());
assert((ab.n() * (ad * bd)) * (c.n() * ld) == ((a.n() * b.n()) * ab.d()) * (c.n() * ld));
assert(((a.n() * b.n()) * ab.d()) * (c.n() * ld) == ab.d() * (((a.n() * b.n()) * c.n()) * ld))
by (nonlinear_arith);
assert(((a.n() * b.n()) * c.n()) * ld == pn * ld);
assert(ab.d() * (left.n() * pd) == ab.d() * (pn * ld));
assert((left.n() * pd) * ab.d() == (pn * ld) * ab.d()) by (nonlinear_arith)
requires
ab.d() * (left.n() * pd) == ab.d() * (pn * ld),
;
lemma_cancel_pos(left.n() * pd, pn * ld, ab.d());
}
pub proof fn lemma_mul_chain_value_right(a: Rat, b: Rat, c: Rat, bc: Rat, right: Rat, pn: int, pd: int)
requires
a.wf(),
b.wf(),
c.wf(),
bc.wf(),
right.wf(),
q_is(bc, mul_n(b, c), prod_d(b, c)),
q_is(right, mul_n(a, bc), prod_d(a, bc)),
pn == (a.n() * b.n()) * c.n(),
pd == (a.d() * b.d()) * c.d(),
ensures
q_is(right, pn, pd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let rd = right.d();
assert(right.n() * (ad * bc.d()) == (a.n() * bc.n()) * rd);
assert((right.n() * (ad * bc.d())) * (bd * cd) == ((a.n() * bc.n()) * rd) * (bd * cd));
assert((right.n() * (ad * bc.d())) * (bd * cd) == bc.d() * (right.n() * ((ad * bd) * cd)))
by (nonlinear_arith);
assert(right.n() * ((ad * bd) * cd) == right.n() * pd);
assert(((a.n() * bc.n()) * rd) * (bd * cd) == (bc.n() * (bd * cd)) * (a.n() * rd))
by (nonlinear_arith);
assert(bc.n() * (bd * cd) == (b.n() * c.n()) * bc.d());
assert((bc.n() * (bd * cd)) * (a.n() * rd) == ((b.n() * c.n()) * bc.d()) * (a.n() * rd));
assert(((b.n() * c.n()) * bc.d()) * (a.n() * rd) == bc.d() * (((a.n() * b.n()) * c.n()) * rd))
by (nonlinear_arith);
assert(((a.n() * b.n()) * c.n()) * rd == pn * rd);
assert(bc.d() * (right.n() * pd) == bc.d() * (pn * rd));
assert((right.n() * pd) * bc.d() == (pn * rd) * bc.d()) by (nonlinear_arith)
requires
bc.d() * (right.n() * pd) == bc.d() * (pn * rd),
;
lemma_cancel_pos(right.n() * pd, pn * rd, bc.d());
}
pub proof fn theorem_distributive_exact(
a: Rat,
b: Rat,
c: Rat,
bc: Rat,
lhs: Rat,
ab: Rat,
ac: Rat,
rhs: Rat,
)
requires
a.wf(),
b.wf(),
c.wf(),
bc.wf(),
lhs.wf(),
ab.wf(),
ac.wf(),
rhs.wf(),
q_is(bc, add_n(b, c), prod_d(b, c)),
q_is(lhs, mul_n(a, bc), prod_d(a, bc)),
q_is(ab, mul_n(a, b), prod_d(a, b)),
q_is(ac, mul_n(a, c), prod_d(a, c)),
q_is(rhs, add_n(ab, ac), prod_d(ab, ac)),
ensures
q_eq(lhs, rhs),
{
let dn = a.n() * (b.n() * c.d() + c.n() * b.d());
let dd = a.d() * (b.d() * c.d());
assert(dd > 0) by (nonlinear_arith)
requires
dd == a.d() * (b.d() * c.d()),
a.d() > 0,
b.d() > 0,
c.d() > 0,
;
lemma_distrib_lhs_value(a, b, c, bc, lhs, dn, dd);
lemma_distrib_rhs_value(a, b, c, ab, ac, rhs, dn, dd);
lemma_same_value_eq(lhs, rhs, dn, dd);
}
pub proof fn lemma_distrib_lhs_value(a: Rat, b: Rat, c: Rat, bc: Rat, lhs: Rat, dn: int, dd: int)
requires
a.wf(),
b.wf(),
c.wf(),
bc.wf(),
lhs.wf(),
q_is(bc, add_n(b, c), prod_d(b, c)),
q_is(lhs, mul_n(a, bc), prod_d(a, bc)),
dn == a.n() * (b.n() * c.d() + c.n() * b.d()),
dd == a.d() * (b.d() * c.d()),
ensures
q_is(lhs, dn, dd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let nbc = b.n() * cd + c.n() * bd;
let ld = lhs.d();
assert(lhs.n() * (ad * bc.d()) == (a.n() * bc.n()) * ld);
assert((lhs.n() * (ad * bc.d())) * (bd * cd) == ((a.n() * bc.n()) * ld) * (bd * cd));
assert((lhs.n() * (ad * bc.d())) * (bd * cd) == bc.d() * (lhs.n() * (ad * (bd * cd))))
by (nonlinear_arith);
assert(lhs.n() * (ad * (bd * cd)) == lhs.n() * dd);
assert(((a.n() * bc.n()) * ld) * (bd * cd) == (bc.n() * (bd * cd)) * (a.n() * ld))
by (nonlinear_arith);
assert(bc.n() * (bd * cd) == nbc * bc.d());
assert((bc.n() * (bd * cd)) * (a.n() * ld) == (nbc * bc.d()) * (a.n() * ld));
assert((nbc * bc.d()) * (a.n() * ld) == bc.d() * ((a.n() * nbc) * ld)) by (nonlinear_arith);
assert((a.n() * nbc) * ld == dn * ld);
assert(bc.d() * (lhs.n() * dd) == bc.d() * (dn * ld));
assert((lhs.n() * dd) * bc.d() == (dn * ld) * bc.d()) by (nonlinear_arith)
requires
bc.d() * (lhs.n() * dd) == bc.d() * (dn * ld),
;
lemma_cancel_pos(lhs.n() * dd, dn * ld, bc.d());
}
pub proof fn lemma_distrib_rhs_value(a: Rat, b: Rat, c: Rat, ab: Rat, ac: Rat, rhs: Rat, dn: int, dd: int)
requires
a.wf(),
b.wf(),
c.wf(),
ab.wf(),
ac.wf(),
rhs.wf(),
q_is(ab, mul_n(a, b), prod_d(a, b)),
q_is(ac, mul_n(a, c), prod_d(a, c)),
q_is(rhs, add_n(ab, ac), prod_d(ab, ac)),
dn == a.n() * (b.n() * c.d() + c.n() * b.d()),
dd == a.d() * (b.d() * c.d()),
ensures
q_is(rhs, dn, dd),
{
let ad = a.d();
let bd = b.d();
let cd = c.d();
let rd = rhs.d();
let g = ab.d() * ac.d();
let k = (ad * bd) * (ad * cd);
assert(g > 0) by (nonlinear_arith)
requires
g == ab.d() * ac.d(),
ab.d() > 0,
ac.d() > 0,
;
assert(k > 0) by (nonlinear_arith)
requires
k == (ad * bd) * (ad * cd),
ad > 0,
bd > 0,
cd > 0,
;
assert(rhs.n() * g == (ab.n() * ac.d() + ac.n() * ab.d()) * rd);
assert((rhs.n() * g) * k == ((ab.n() * ac.d() + ac.n() * ab.d()) * rd) * k);
assert(((ab.n() * ac.d() + ac.n() * ab.d()) * rd) * k == ((ab.n() * ac.d()) * rd) * k + ((
ac.n() * ab.d()) * rd) * k) by (nonlinear_arith);
assert(((ab.n() * ac.d()) * rd) * k == ((ab.n() * (ad * bd)) * (ac.d() * (ad * cd))) * rd)
by (nonlinear_arith)
requires
k == (ad * bd) * (ad * cd),
;
assert(((ac.n() * ab.d()) * rd) * k == ((ac.n() * (ad * cd)) * (ab.d() * (ad * bd))) * rd)
by (nonlinear_arith)
requires
k == (ad * bd) * (ad * cd),
;
assert(ab.n() * (ad * bd) == (a.n() * b.n()) * ab.d());
assert(ac.n() * (ad * cd) == (a.n() * c.n()) * ac.d());
assert(((ab.n() * (ad * bd)) * (ac.d() * (ad * cd))) * rd == (((a.n() * b.n()) * ab.d()) * (
ac.d() * (ad * cd))) * rd);
assert(((ac.n() * (ad * cd)) * (ab.d() * (ad * bd))) * rd == (((a.n() * c.n()) * ac.d()) * (
ab.d() * (ad * bd))) * rd);
let u = (a.n() * b.n()) * (ad * cd);
let v = (a.n() * c.n()) * (ad * bd);
assert((((a.n() * b.n()) * ab.d()) * (ac.d() * (ad * cd))) * rd == g * (u * rd))
by (nonlinear_arith)
requires
g == ab.d() * ac.d(),
u == (a.n() * b.n()) * (ad * cd),
;
assert((((a.n() * c.n()) * ac.d()) * (ab.d() * (ad * bd))) * rd == g * (v * rd))
by (nonlinear_arith)
requires
g == ab.d() * ac.d(),
v == (a.n() * c.n()) * (ad * bd),
;
assert(g * (u * rd) + g * (v * rd) == g * ((u + v) * rd)) by (nonlinear_arith);
assert((rhs.n() * g) * k == g * (rhs.n() * k)) by (nonlinear_arith);
assert(g * (rhs.n() * k) == g * ((u + v) * rd));
assert((rhs.n() * k) * g == ((u + v) * rd) * g) by (nonlinear_arith)
requires
g * (rhs.n() * k) == g * ((u + v) * rd),
;
lemma_cancel_pos(rhs.n() * k, (u + v) * rd, g);
assert(k == ad * dd) by (nonlinear_arith)
requires
k == (ad * bd) * (ad * cd),
dd == ad * (bd * cd),
;
assert(ad * dn == ad * (a.n() * (b.n() * cd)) + ad * (a.n() * (c.n() * bd)))
by (nonlinear_arith)
requires
dn == a.n() * (b.n() * cd + c.n() * bd),
;
assert(u == ad * (a.n() * (b.n() * cd))) by (nonlinear_arith)
requires
u == (a.n() * b.n()) * (ad * cd),
;
assert(v == ad * (a.n() * (c.n() * bd))) by (nonlinear_arith)
requires
v == (a.n() * c.n()) * (ad * bd),
;
assert(rhs.n() * (ad * dd) == (ad * dn) * rd);
assert((rhs.n() * dd) * ad == (dn * rd) * ad) by (nonlinear_arith)
requires
rhs.n() * (ad * dd) == (ad * dn) * rd,
;
lemma_cancel_pos(rhs.n() * dd, dn * rd, ad);
}
pub proof fn theorem_identities(a: Rat, dir: Dir)
requires
a.wf(),
ensures
({
let z = Rat::from_raw_spec(0, 1);
let o = Rat::from_raw_spec(1, 1);
&&& exact_path(add_n(a, z), prod_d(a, z))
&&& exact_path(mul_n(a, o), prod_d(a, o))
&&& round_frac(add_n(a, z), prod_d(a, z), dir) == a
&&& round_frac(mul_n(a, o), prod_d(a, o), dir) == a
}),
{
let z = Rat::from_raw_spec(0, 1);
let o = Rat::from_raw_spec(1, 1);
Rat::lemma_from_raw_spec_components(0, 1);
Rat::lemma_from_raw_spec_components(1, 1);
assert(add_n(a, z) == a.n() && prod_d(a, z) == a.d());
assert(mul_n(a, o) == a.n() && prod_d(a, o) == a.d());
crate::round::lemma_r1_identity(a.n(), a.d(), dir);
lemma_round_of_wf_is_self(a, dir);
}
pub proof fn lemma_round_of_wf_is_self(a: Rat, dir: Dir)
requires
a.wf(),
ensures
round_frac(a.n(), a.d(), dir) == a,
{
if a.n() == 0 {
assert(a.d() == 1);
Rat::lemma_from_raw_spec_components(0, 1);
Rat::lemma_extensional(Rat::from_raw_spec(0, 1), a);
} else {
assert(gcd_int(a.n(), a.d()) == 1);
assert(crate::round::red_num(a.n(), a.d()) == a.n());
assert(crate::round::red_den(a.n(), a.d()) == a.d());
assert(crate::model::magnitude_fits(a.n(), a.d())) by (nonlinear_arith)
requires
abs_int(a.n()) <= crate::model::max_mag(),
a.d() >= 1,
crate::model::max_mag() > 0,
;
Rat::lemma_from_raw_spec_components(a.n() as i64, a.d() as i64);
Rat::lemma_extensional(Rat::from_raw_spec(a.n() as i64, a.d() as i64), a);
}
}
pub proof fn theorem_order_total(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ensures
q_le(a, a),
q_le(a, b) || q_le(b, a),
(q_le(a, b) && q_le(b, a)) ==> a == b,
(q_le(a, b) && q_le(b, c)) ==> q_le(a, c),
q_lt(a, b) <==> (q_le(a, b) && !q_eq(a, b)),
{
crate::q::lemma_le_trans(a, b, c);
if q_le(a, b) && q_le(b, a) {
assert(q_eq(a, b));
lemma_canonical_eq(a, b);
}
}
pub proof fn theorem_neg_abs_involution(a: Rat)
requires
a.wf(),
ensures
({
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let aa = Rat::from_raw_spec(abs_int(a.n()) as i64, a.d() as i64);
&&& Rat::from_raw_spec((-(na.n())) as i64, na.d() as i64) == a
&&& Rat::from_raw_spec(abs_int(aa.n()) as i64, aa.d() as i64) == aa
}),
{
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let aa = Rat::from_raw_spec(abs_int(a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components((-a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components(abs_int(a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components((-(na.n())) as i64, na.d() as i64);
Rat::lemma_from_raw_spec_components(abs_int(aa.n()) as i64, aa.d() as i64);
Rat::lemma_extensional(
Rat::from_raw_spec((-(na.n())) as i64, na.d() as i64),
a,
);
Rat::lemma_extensional(
Rat::from_raw_spec(abs_int(aa.n()) as i64, aa.d() as i64),
aa,
);
}
pub proof fn theorem_recip_involution(a: Rat, r: Rat, rr: Rat)
requires
a.wf(),
r.wf(),
rr.wf(),
a.n() != 0,
crate::q::q_is_recip(r, a),
crate::q::q_is_recip(rr, r),
ensures
q_eq(rr, a),
{
assert(r.n() != 0) by (nonlinear_arith)
requires
r.n() * a.n() == a.d() * r.d(),
a.d() > 0,
r.d() > 0,
;
assert(rr.n() * a.d() == a.n() * rr.d()) by (nonlinear_arith)
requires
r.n() * a.n() == a.d() * r.d(),
rr.n() * r.n() == r.d() * rr.d(),
a.d() > 0,
r.d() > 0,
rr.d() > 0,
a.n() != 0,
r.n() != 0,
;
}
pub proof fn theorem_sub_is_add_neg(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
ensures
({
let nb = Rat::from_raw_spec((-b.n()) as i64, b.d() as i64);
sub_n(a, b) == add_n(a, nb) && prod_d(a, b) == prod_d(a, nb)
}),
{
Rat::lemma_from_raw_spec_components((-b.n()) as i64, b.d() as i64);
crate::model::lemma_max_mag_pow2();
assert((-b.n()) * a.d() == -(b.n() * a.d())) by (nonlinear_arith);
}
pub proof fn theorem_div_is_mul_recip(a: Rat, b: Rat, rb: Rat)
requires
a.wf(),
b.wf(),
rb.wf(),
b.n() != 0,
crate::q::q_is_recip(rb, b),
ensures
div_n(a, b) * prod_d(a, rb) == mul_n(a, rb) * div_d(a, b),
{
assert(div_n(a, b) * prod_d(a, rb) == mul_n(a, rb) * div_d(a, b)) by (nonlinear_arith)
requires
rb.n() * b.n() == b.d() * rb.d(),
a.d() > 0,
b.d() > 0,
rb.d() > 0,
b.n() != 0,
;
}
pub proof fn lemma_self_zero_error(x: Rat, m: int)
requires
x.wf(),
ensures
within_abs_error(x, x.n(), x.d(), 0, m),
{
assert(x.n() * x.d() - x.n() * x.d() == 0);
assert(abs_int(0) == 0);
}
pub proof fn lemma_abs_int_neg(x: int)
ensures
abs_int(-x) == abs_int(x),
{
}
pub proof fn lemma_sum3_ring(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ensures
add_n(a, b) * c.d() + c.n() * prod_d(a, b) == add_n(b, c) * a.d() + a.n() * prod_d(b, c),
prod_d(a, b) * c.d() == prod_d(b, c) * a.d(),
{
let ad = a.d();
let an = a.n();
let bd = b.d();
let bn = b.n();
let cd = c.d();
let cn = c.n();
broadcast use vstd::arithmetic::mul::group_mul_properties;
assert(add_n(a, b) == an * bd + bn * ad);
assert(prod_d(a, b) == ad * bd);
assert(add_n(b, c) == bn * cd + cn * bd);
assert(prod_d(b, c) == bd * cd);
assert(
(an * bd + bn * ad) * cd + cn * (ad * bd)
== (bn * cd + cn * bd) * ad + an * (bd * cd)
);
assert(ad * bd * cd == bd * cd * ad);
}
pub proof fn theorem_add_associativity_bound(a: Rat, b: Rat, c: Rat, dir: Dir, m: int)
requires
a.wf(),
b.wf(),
c.wf(),
m >= 1,
!saturated(add_n(a, b), prod_d(a, b)),
max_int(prod_d(a, b), abs_int(add_n(a, b))) <= m * prod_d(a, b),
!saturated(add_n(b, c), prod_d(b, c)),
max_int(prod_d(b, c), abs_int(add_n(b, c))) <= m * prod_d(b, c),
({
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
&&& !saturated(add_n(ab, c), prod_d(ab, c))
&&& max_int(prod_d(ab, c), abs_int(add_n(ab, c))) <= m * prod_d(ab, c)
}),
({
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
&&& !saturated(add_n(a, bc), prod_d(a, bc))
&&& max_int(prod_d(a, bc), abs_int(add_n(a, bc))) <= m * prod_d(a, bc)
}),
ensures
({
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
let left = round_frac(add_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(add_n(a, bc), prod_d(a, bc), dir);
within_abs_error(left, right.n(), right.d(), 4, m)
}),
{
crate::q::lemma_op_widths(a, b);
crate::q::lemma_op_widths(b, c);
let ab = round_frac(add_n(a, b), prod_d(a, b), dir);
let bc = round_frac(add_n(b, c), prod_d(b, c), dir);
crate::round::lemma_round_frac_wf(add_n(a, b), prod_d(a, b), dir);
crate::round::lemma_round_frac_wf(add_n(b, c), prod_d(b, c), dir);
crate::q::lemma_op_widths(ab, c);
crate::q::lemma_op_widths(a, bc);
crate::q::lemma_op_widths(bc, a);
let left = round_frac(add_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(add_n(a, bc), prod_d(a, bc), dir);
crate::round::lemma_round_frac_wf(add_n(ab, c), prod_d(ab, c), dir);
crate::round::lemma_round_frac_wf(add_n(a, bc), prod_d(a, bc), dir);
lemma_self_zero_error(a, m);
crate::round::lemma_r3_error(add_n(a, b), prod_d(a, b), dir);
crate::lipschitz::lemma_abs_error_step(a, a.n(), a.d(), b, ab, 0, m);
crate::round::lemma_r3_error(add_n(ab, c), prod_d(ab, c), dir);
crate::lipschitz::lemma_abs_error_step(ab, add_n(a, b), prod_d(a, b), c, left, 1, m);
let sn = add_n(a, b) * c.d() + c.n() * prod_d(a, b);
let sd = prod_d(a, b) * c.d();
assert(within_abs_error(left, sn, sd, 2, m));
lemma_self_zero_error(b, m);
crate::round::lemma_r3_error(add_n(b, c), prod_d(b, c), dir);
crate::lipschitz::lemma_abs_error_step(b, b.n(), b.d(), c, bc, 0, m);
assert(add_n(bc, a) == add_n(a, bc));
assert(prod_d(bc, a) == bc.d() * a.d());
assert(prod_d(a, bc) == a.d() * bc.d());
assert(bc.d() * a.d() == a.d() * bc.d()) by (nonlinear_arith);
assert(prod_d(bc, a) == prod_d(a, bc));
crate::round::lemma_r3_error(add_n(a, bc), prod_d(a, bc), dir);
assert(within_error_bound(right, add_n(bc, a), prod_d(bc, a)));
assert(max_int(prod_d(bc, a), abs_int(add_n(bc, a))) <= m * prod_d(bc, a));
crate::lipschitz::lemma_abs_error_step(bc, add_n(b, c), prod_d(b, c), a, right, 1, m);
lemma_sum3_ring(a, b, c);
assert(within_abs_error(right, sn, sd, 2, m));
crate::model::lemma_pow2_pos(precision_b());
assert(left.d() > 0);
assert(right.d() > 0);
assert(sd > 0) by (nonlinear_arith)
requires
sd == prod_d(a, b) * c.d(),
prod_d(a, b) > 0,
c.d() > 0,
;
lemma_abs_int_neg(right.n() * sd - sn * right.d());
assert(sn * right.d() - right.n() * sd == -(right.n() * sd - sn * right.d()));
assert(abs_int(sn * right.d() - right.n() * sd) == abs_int(right.n() * sd - sn * right.d()));
assert(abs_int(right.n() * sd - sn * right.d()) * pow2(precision_b()) <= 2 * m * (right.d()
* sd));
assert(right.d() * sd == sd * right.d()) by (nonlinear_arith);
assert(abs_int(sn * right.d() - right.n() * sd) * pow2(precision_b()) <= 2 * m * (sd
* right.d()));
crate::lipschitz::lemma_frac_triangle(
left.n(),
left.d(),
sn,
sd,
right.n(),
right.d(),
2 * m,
2 * m,
pow2(precision_b()),
);
}
pub proof fn lemma_cancel_pos_le(x: int, y: int, c: int)
requires
c > 0,
x * c <= y * c,
ensures
x <= y,
{
assert(x <= y) by (nonlinear_arith)
requires
c > 0,
x * c <= y * c,
;
}
pub proof fn lemma_abs_mul_nonneg(x: int, y: int)
requires
y >= 0,
ensures
abs_int(x * y) == abs_int(x) * y,
{
if y == 0 {
assert(x * y == 0) by (nonlinear_arith)
requires
y == 0,
;
assert(abs_int(x) * y == 0) by (nonlinear_arith)
requires
y == 0,
;
} else {
crate::model::lemma_abs_mul_pos(x, y);
}
}
pub proof fn lemma_unit_interval_mul(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
0 <= a.n(),
a.n() <= a.d(),
0 <= b.n(),
b.n() <= b.d(),
ensures
0 <= mul_n(a, b),
mul_n(a, b) <= prod_d(a, b),
{
assert(mul_n(a, b) == a.n() * b.n());
assert(prod_d(a, b) == a.d() * b.d());
assert(a.n() * b.n() >= 0) by (nonlinear_arith)
requires
a.n() >= 0,
b.n() >= 0,
;
assert(a.n() * b.n() <= a.d() * b.n()) by (nonlinear_arith)
requires
a.n() <= a.d(),
b.n() >= 0,
;
assert(a.d() * b.n() <= a.d() * b.d()) by (nonlinear_arith)
requires
b.n() <= b.d(),
a.d() > 0,
;
assert(mul_n(a, b) <= prod_d(a, b));
}
pub proof fn lemma_rounded_product_bound(a: Rat, b: Rat, dir: Dir)
requires
a.wf(),
b.wf(),
0 <= a.n(),
a.n() <= a.d(),
0 <= b.n(),
b.n() <= b.d(),
!saturated(mul_n(a, b), prod_d(a, b)),
ensures
({
let ab = round_frac(mul_n(a, b), prod_d(a, b), dir);
&&& ab.wf()
&&& within_abs_error(ab, mul_n(a, b), prod_d(a, b), 1, 1)
&&& -ab.d() <= ab.n()
&&& ab.n() <= 2 * ab.d()
&&& abs_int(ab.n()) <= 2 * ab.d()
}),
{
lemma_unit_interval_mul(a, b);
crate::q::lemma_op_widths(a, b);
crate::round::lemma_round_frac_wf(mul_n(a, b), prod_d(a, b), dir);
crate::round::lemma_r3_error(mul_n(a, b), prod_d(a, b), dir);
crate::model::lemma_pow2_pos(precision_b());
let ab = round_frac(mul_n(a, b), prod_d(a, b), dir);
let pn = mul_n(a, b);
let pd = prod_d(a, b);
assert(pd > 0);
assert(abs_int(pn) == pn);
assert(max_int(pd, abs_int(pn)) == pd);
assert(abs_int(ab.n() * pd - pn * ab.d()) * pow2(precision_b()) <= ab.d() * pd);
assert((1nat as int) * 1 * (ab.d() * pd) == ab.d() * pd) by (nonlinear_arith);
assert(within_abs_error(ab, pn, pd, 1, 1));
assert(pow2(precision_b()) >= 1);
let diff = ab.n() * pd - pn * ab.d();
assert(abs_int(diff) >= 0);
assert(abs_int(diff) <= abs_int(diff) * pow2(precision_b())) by (nonlinear_arith)
requires
abs_int(diff) >= 0,
pow2(precision_b()) >= 1,
;
assert(abs_int(diff) <= ab.d() * pd);
assert(ab.n() * pd <= pn * ab.d() + ab.d() * pd) by (nonlinear_arith)
requires
diff == ab.n() * pd - pn * ab.d(),
abs_int(diff) <= ab.d() * pd,
;
assert(pn * ab.d() <= pd * ab.d()) by (nonlinear_arith)
requires
pn <= pd,
ab.d() > 0,
;
assert(ab.n() * pd <= pd * ab.d() + ab.d() * pd) by (nonlinear_arith)
requires
ab.n() * pd <= pn * ab.d() + ab.d() * pd,
pn * ab.d() <= pd * ab.d(),
;
assert(pd * ab.d() + ab.d() * pd == (2 * ab.d()) * pd) by (nonlinear_arith);
assert(ab.n() * pd <= (2 * ab.d()) * pd);
lemma_cancel_pos_le(ab.n(), 2 * ab.d(), pd);
assert(ab.n() * pd >= pn * ab.d() - ab.d() * pd) by (nonlinear_arith)
requires
diff == ab.n() * pd - pn * ab.d(),
abs_int(diff) <= ab.d() * pd,
;
assert(pn * ab.d() >= 0) by (nonlinear_arith)
requires
pn >= 0,
ab.d() > 0,
;
assert(ab.n() * pd >= 0 - ab.d() * pd) by (nonlinear_arith)
requires
ab.n() * pd >= pn * ab.d() - ab.d() * pd,
pn * ab.d() >= 0,
;
assert((-ab.d()) * pd == 0 - ab.d() * pd) by (nonlinear_arith);
assert((-ab.d()) * pd <= ab.n() * pd);
lemma_cancel_pos_le(-ab.d(), ab.n(), pd);
assert(abs_int(ab.n()) <= 2 * ab.d());
}
pub proof fn lemma_frac_scale_nonneg(xn: int, xd: int, yn: int, yd: int, cn: int, cd: int, e: int, ed: int)
requires
xd > 0,
yd > 0,
cd > 0,
ed > 0,
e >= 0,
0 <= cn <= cd,
abs_int(xn * yd - yn * xd) * ed <= e * (xd * yd),
ensures
abs_int((xn * cn) * (yd * cd) - (yn * cn) * (xd * cd)) * ed <= e * ((xd * cd) * (yd * cd)),
{
let diffx = xn * yd - yn * xd;
let k = cn * cd;
assert((xn * cn) * (yd * cd) == k * (xn * yd)) by (nonlinear_arith)
requires
k == cn * cd,
;
assert((yn * cn) * (xd * cd) == k * (yn * xd)) by (nonlinear_arith)
requires
k == cn * cd,
;
assert(k * (xn * yd) - k * (yn * xd) == k * diffx) by (nonlinear_arith)
requires
diffx == xn * yd - yn * xd,
;
assert((xn * cn) * (yd * cd) - (yn * cn) * (xd * cd) == k * diffx);
assert(k >= 0) by (nonlinear_arith)
requires
cn >= 0,
cd > 0,
k == cn * cd,
;
lemma_abs_mul_nonneg(diffx, k);
assert(k * diffx == diffx * k) by (nonlinear_arith);
assert(abs_int(k * diffx) == abs_int(diffx) * k);
assert(abs_int((xn * cn) * (yd * cd) - (yn * cn) * (xd * cd)) == abs_int(diffx) * k);
assert((abs_int(diffx) * k) * ed == k * (abs_int(diffx) * ed)) by (nonlinear_arith);
assert(k * (abs_int(diffx) * ed) <= k * (e * (xd * yd))) by (nonlinear_arith)
requires
k >= 0,
abs_int(diffx) * ed <= e * (xd * yd),
;
assert(k * (e * (xd * yd)) == (cn * cd) * (e * (xd * yd))) by (nonlinear_arith)
requires
k == cn * cd,
;
assert(cn * cd <= cd * cd) by (nonlinear_arith)
requires
cn <= cd,
cd > 0,
;
assert(e * (xd * yd) >= 0) by (nonlinear_arith)
requires
e >= 0,
xd > 0,
yd > 0,
;
assert((cn * cd) * (e * (xd * yd)) <= (cd * cd) * (e * (xd * yd))) by (nonlinear_arith)
requires
cn * cd <= cd * cd,
e * (xd * yd) >= 0,
;
assert((cd * cd) * (e * (xd * yd)) == e * ((xd * cd) * (yd * cd))) by (nonlinear_arith);
assert(abs_int((xn * cn) * (yd * cd) - (yn * cn) * (xd * cd)) * ed <= e * ((xd * cd) * (yd
* cd)));
}
pub proof fn lemma_prod_magnitude_bound(x: Rat, y: Rat, k: int)
requires
x.wf(),
y.wf(),
k >= 1,
abs_int(x.n()) <= k * x.d(),
0 <= y.n(),
y.n() <= y.d(),
ensures
max_int(prod_d(x, y), abs_int(mul_n(x, y))) <= k * prod_d(x, y),
{
assert(mul_n(x, y) == x.n() * y.n());
assert(prod_d(x, y) == x.d() * y.d());
lemma_abs_mul_nonneg(x.n(), y.n());
assert(abs_int(x.n() * y.n()) == abs_int(x.n()) * y.n());
assert(k * x.d() >= 0) by (nonlinear_arith)
requires
k >= 1,
x.d() > 0,
;
assert(abs_int(x.n()) * y.n() <= (k * x.d()) * y.n()) by (nonlinear_arith)
requires
abs_int(x.n()) <= k * x.d(),
y.n() >= 0,
;
assert((k * x.d()) * y.n() <= (k * x.d()) * y.d()) by (nonlinear_arith)
requires
y.n() <= y.d(),
k * x.d() >= 0,
;
assert((k * x.d()) * y.d() == k * (x.d() * y.d())) by (nonlinear_arith);
assert(abs_int(mul_n(x, y)) <= k * prod_d(x, y));
assert(prod_d(x, y) >= 0) by (nonlinear_arith)
requires
prod_d(x, y) == x.d() * y.d(),
x.d() > 0,
y.d() > 0,
;
assert(prod_d(x, y) <= k * prod_d(x, y)) by (nonlinear_arith)
requires
prod_d(x, y) >= 0,
k >= 1,
;
assert(max_int(prod_d(x, y), abs_int(mul_n(x, y))) <= k * prod_d(x, y));
}
pub proof fn lemma_mul3_ring(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ensures
mul_n(a, b) * c.n() == mul_n(b, c) * a.n(),
prod_d(a, b) * c.d() == prod_d(b, c) * a.d(),
{
let an = a.n();
let ad = a.d();
let bn = b.n();
let bd = b.d();
let cn = c.n();
let cd = c.d();
assert(mul_n(a, b) == an * bn);
assert(mul_n(b, c) == bn * cn);
assert(prod_d(a, b) == ad * bd);
assert(prod_d(b, c) == bd * cd);
assert((an * bn) * cn == (bn * cn) * an) by (nonlinear_arith);
assert((ad * bd) * cd == (bd * cd) * ad) by (nonlinear_arith);
}
pub proof fn theorem_mul_associativity_bound_unit_interval(a: Rat, b: Rat, c: Rat, dir: Dir)
requires
a.wf(),
b.wf(),
c.wf(),
0 <= a.n(),
a.n() <= a.d(),
0 <= b.n(),
b.n() <= b.d(),
0 <= c.n(),
c.n() <= c.d(),
!saturated(mul_n(a, b), prod_d(a, b)),
!saturated(mul_n(b, c), prod_d(b, c)),
({
let ab = round_frac(mul_n(a, b), prod_d(a, b), dir);
!saturated(mul_n(ab, c), prod_d(ab, c))
}),
({
let bc = round_frac(mul_n(b, c), prod_d(b, c), dir);
!saturated(mul_n(a, bc), prod_d(a, bc))
}),
ensures
({
let ab = round_frac(mul_n(a, b), prod_d(a, b), dir);
let bc = round_frac(mul_n(b, c), prod_d(b, c), dir);
let left = round_frac(mul_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(mul_n(a, bc), prod_d(a, bc), dir);
within_abs_error(left, right.n(), right.d(), 6, 1)
}),
{
lemma_rounded_product_bound(a, b, dir);
lemma_rounded_product_bound(b, c, dir);
let ab = round_frac(mul_n(a, b), prod_d(a, b), dir);
let bc = round_frac(mul_n(b, c), prod_d(b, c), dir);
crate::q::lemma_op_widths(a, b);
crate::q::lemma_op_widths(b, c);
crate::q::lemma_op_widths(ab, c);
crate::q::lemma_op_widths(a, bc);
crate::q::lemma_op_widths(bc, a);
crate::round::lemma_round_frac_wf(mul_n(ab, c), prod_d(ab, c), dir);
crate::round::lemma_round_frac_wf(mul_n(a, bc), prod_d(a, bc), dir);
crate::model::lemma_pow2_pos(precision_b());
let left = round_frac(mul_n(ab, c), prod_d(ab, c), dir);
let right = round_frac(mul_n(a, bc), prod_d(a, bc), dir);
let pn = a.n() * b.n() * c.n();
let pd = a.d() * b.d() * c.d();
assert(pd > 0) by (nonlinear_arith)
requires
pd == a.d() * b.d() * c.d(),
a.d() > 0,
b.d() > 0,
c.d() > 0,
;
lemma_frac_scale_nonneg(ab.n(), ab.d(), mul_n(a, b), prod_d(a, b), c.n(), c.d(), 1, pow2(
precision_b(),
));
assert(mul_n(ab, c) == ab.n() * c.n());
assert(prod_d(ab, c) == ab.d() * c.d());
assert(mul_n(a, b) * c.n() == pn);
assert(prod_d(a, b) * c.d() == pd);
assert(abs_int(mul_n(ab, c) * pd - pn * prod_d(ab, c)) * pow2(precision_b()) <= 1 * (prod_d(
ab,
c,
) * pd));
lemma_prod_magnitude_bound(ab, c, 2);
crate::round::lemma_r3_error(mul_n(ab, c), prod_d(ab, c), dir);
assert(left.d() * (2 * prod_d(ab, c)) == 2 * (left.d() * prod_d(ab, c))) by (nonlinear_arith);
assert(abs_int(left.n() * prod_d(ab, c) - mul_n(ab, c) * left.d()) * pow2(precision_b())
<= left.d() * max_int(prod_d(ab, c), abs_int(mul_n(ab, c))));
assert(left.d() * max_int(prod_d(ab, c), abs_int(mul_n(ab, c))) <= left.d() * (2 * prod_d(
ab,
c,
))) by (nonlinear_arith)
requires
max_int(prod_d(ab, c), abs_int(mul_n(ab, c))) <= 2 * prod_d(ab, c),
left.d() > 0,
;
assert(within_abs_error(left, mul_n(ab, c), prod_d(ab, c), 2, 1));
crate::lipschitz::lemma_frac_triangle(
left.n(),
left.d(),
mul_n(ab, c),
prod_d(ab, c),
pn,
pd,
2,
1,
pow2(precision_b()),
);
assert(within_abs_error(left, pn, pd, 3, 1));
lemma_frac_scale_nonneg(bc.n(), bc.d(), mul_n(b, c), prod_d(b, c), a.n(), a.d(), 1, pow2(
precision_b(),
));
assert(mul_n(bc, a) == bc.n() * a.n());
assert(prod_d(bc, a) == bc.d() * a.d());
lemma_mul3_ring(a, b, c);
assert(mul_n(b, c) * a.n() == pn);
assert(prod_d(b, c) * a.d() == pd);
assert(abs_int(mul_n(bc, a) * pd - pn * prod_d(bc, a)) * pow2(precision_b()) <= 1 * (prod_d(
bc,
a,
) * pd));
assert(mul_n(bc, a) == mul_n(a, bc)) by (nonlinear_arith)
requires
mul_n(bc, a) == bc.n() * a.n(),
mul_n(a, bc) == a.n() * bc.n(),
;
assert(prod_d(bc, a) == prod_d(a, bc)) by (nonlinear_arith)
requires
prod_d(bc, a) == bc.d() * a.d(),
prod_d(a, bc) == a.d() * bc.d(),
;
lemma_prod_magnitude_bound(bc, a, 2);
assert(max_int(prod_d(bc, a), abs_int(mul_n(bc, a))) <= 2 * prod_d(bc, a));
crate::round::lemma_r3_error(mul_n(a, bc), prod_d(a, bc), dir);
assert(within_error_bound(right, mul_n(bc, a), prod_d(bc, a)));
assert(right.d() * (2 * prod_d(bc, a)) == 2 * (right.d() * prod_d(bc, a)))
by (nonlinear_arith);
assert(abs_int(right.n() * prod_d(bc, a) - mul_n(bc, a) * right.d()) * pow2(precision_b())
<= right.d() * max_int(prod_d(bc, a), abs_int(mul_n(bc, a))));
assert(right.d() * max_int(prod_d(bc, a), abs_int(mul_n(bc, a))) <= right.d() * (2 * prod_d(
bc,
a,
))) by (nonlinear_arith)
requires
max_int(prod_d(bc, a), abs_int(mul_n(bc, a))) <= 2 * prod_d(bc, a),
right.d() > 0,
;
assert(within_abs_error(right, mul_n(bc, a), prod_d(bc, a), 2, 1));
crate::lipschitz::lemma_frac_triangle(
right.n(),
right.d(),
mul_n(bc, a),
prod_d(bc, a),
pn,
pd,
2,
1,
pow2(precision_b()),
);
assert(within_abs_error(right, pn, pd, 3, 1));
lemma_abs_int_neg(right.n() * pd - pn * right.d());
assert(pn * right.d() - right.n() * pd == -(right.n() * pd - pn * right.d()));
assert(abs_int(pn * right.d() - right.n() * pd) == abs_int(right.n() * pd - pn * right.d()));
assert(abs_int(right.n() * pd - pn * right.d()) * pow2(precision_b()) <= 3 * (right.d() * pd));
assert(right.d() * pd == pd * right.d()) by (nonlinear_arith);
assert(abs_int(pn * right.d() - right.n() * pd) * pow2(precision_b()) <= 3 * (pd
* right.d()));
crate::lipschitz::lemma_frac_triangle(
left.n(),
left.d(),
pn,
pd,
right.n(),
right.d(),
3,
3,
pow2(precision_b()),
);
}
pub proof fn theorem_neg_antitone(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
ensures
({
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let nb = Rat::from_raw_spec((-b.n()) as i64, b.d() as i64);
&&& na.wf()
&&& (q_le(a, b) <==> q_le(nb, na))
}),
{
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let nb = Rat::from_raw_spec((-b.n()) as i64, b.d() as i64);
Rat::lemma_from_raw_spec_components((-a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components((-b.n()) as i64, b.d() as i64);
assert(na.wf());
assert(abs_int(na.n()) == abs_int(a.n()));
assert((-b.n()) * a.d() == -(b.n() * a.d())) by (nonlinear_arith);
assert((-a.n()) * b.d() == -(a.n() * b.d())) by (nonlinear_arith);
}
pub proof fn theorem_abs_is_join(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
ensures
({
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let aa = Rat::from_raw_spec(abs_int(a.n()) as i64, a.d() as i64);
let naa = Rat::from_raw_spec(abs_int(na.n()) as i64, na.d() as i64);
&&& aa.wf()
&&& aa.n() >= 0
&&& q_le(a, aa)
&&& q_le(na, aa)
&&& (aa == a || aa == na)
&&& naa == aa
&&& (q_le(a, b) && q_le(na, b)) ==> q_le(aa, b)
}),
{
let na = Rat::from_raw_spec((-a.n()) as i64, a.d() as i64);
let aa = Rat::from_raw_spec(abs_int(a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components((-a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components(abs_int(a.n()) as i64, a.d() as i64);
Rat::lemma_from_raw_spec_components(abs_int(na.n()) as i64, na.d() as i64);
assert(abs_int(aa.n()) == abs_int(a.n()));
assert(a.n() * a.d() <= abs_int(a.n()) * a.d()) by (nonlinear_arith)
requires
a.d() > 0,
a.n() <= abs_int(a.n()),
;
assert((-a.n()) * a.d() <= abs_int(a.n()) * a.d()) by (nonlinear_arith)
requires
a.d() > 0,
-a.n() <= abs_int(a.n()),
;
if a.n() >= 0 {
assert(abs_int(a.n()) == a.n());
Rat::lemma_extensional(aa, a);
} else {
assert(abs_int(a.n()) == -a.n());
assert(aa.n() == na.n() && aa.d() == na.d());
Rat::lemma_extensional(aa, na);
}
}
pub proof fn theorem_add_monotone_exact(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
q_le(a, b),
ensures
add_n(a, c) * prod_d(b, c) <= add_n(b, c) * prod_d(a, c),
{
let an = a.n();
let ad = a.d();
let bn = b.n();
let bd = b.d();
let cn = c.n();
let cd = c.d();
let s = cd * cd;
let t = (cn * (ad * bd)) * cd;
assert(s > 0) by (nonlinear_arith)
requires
s == cd * cd,
cd > 0,
;
assert((an * bd) * s <= (bn * ad) * s) by (nonlinear_arith)
requires
an * bd <= bn * ad,
s > 0,
;
assert(add_n(a, c) == an * cd + cn * ad);
assert(prod_d(b, c) == bd * cd);
assert(add_n(b, c) == bn * cd + cn * bd);
assert(prod_d(a, c) == ad * cd);
assert((an * cd + cn * ad) * (bd * cd) == (an * cd) * (bd * cd) + (cn * ad) * (bd * cd))
by (nonlinear_arith);
assert((an * cd) * (bd * cd) == (an * bd) * s) by (nonlinear_arith)
requires
s == cd * cd,
;
assert((cn * ad) * (bd * cd) == t) by (nonlinear_arith)
requires
t == (cn * (ad * bd)) * cd,
;
assert((bn * cd + cn * bd) * (ad * cd) == (bn * cd) * (ad * cd) + (cn * bd) * (ad * cd))
by (nonlinear_arith);
assert((bn * cd) * (ad * cd) == (bn * ad) * s) by (nonlinear_arith)
requires
s == cd * cd,
;
assert((cn * bd) * (ad * cd) == t) by (nonlinear_arith)
requires
t == (cn * (ad * bd)) * cd,
;
assert(add_n(a, c) * prod_d(b, c) == (an * bd) * s + t);
assert(add_n(b, c) * prod_d(a, c) == (bn * ad) * s + t);
}
pub proof fn theorem_mul_monotone_nonneg_exact(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
q_le(a, b),
c.n() >= 0,
ensures
mul_n(a, c) * prod_d(b, c) <= mul_n(b, c) * prod_d(a, c),
{
let an = a.n();
let ad = a.d();
let bn = b.n();
let bd = b.d();
let cn = c.n();
let cd = c.d();
let k = cn * cd;
assert(k >= 0) by (nonlinear_arith)
requires
k == cn * cd,
cn >= 0,
cd > 0,
;
assert((an * bd) * k <= (bn * ad) * k) by (nonlinear_arith)
requires
an * bd <= bn * ad,
k >= 0,
;
assert(mul_n(a, c) == an * cn);
assert(prod_d(b, c) == bd * cd);
assert(mul_n(b, c) == bn * cn);
assert(prod_d(a, c) == ad * cd);
assert((an * cn) * (bd * cd) == (an * bd) * k) by (nonlinear_arith)
requires
k == cn * cd,
;
assert((bn * cn) * (ad * cd) == (bn * ad) * k) by (nonlinear_arith)
requires
k == cn * cd,
;
}
pub proof fn theorem_square_sign(a: Rat)
requires
a.wf(),
ensures
mul_n(a, a) >= 0,
mul_n(a, a) == 0 <==> a.n() == 0,
prod_d(a, a) > 0,
{
assert(a.n() * a.n() >= 0) by (nonlinear_arith);
if a.n() != 0 {
assert(a.n() * a.n() != 0) by (nonlinear_arith)
requires
a.n() != 0,
;
}
assert(a.d() * a.d() > 0) by (nonlinear_arith)
requires
a.d() > 0,
;
}
pub proof fn theorem_recip_antitone(a: Rat, b: Rat, ra: Rat, rb: Rat)
requires
a.wf(),
b.wf(),
ra.wf(),
rb.wf(),
a.n() > 0,
b.n() > 0,
q_le(a, b),
crate::q::q_is_recip(ra, a),
crate::q::q_is_recip(rb, b),
ensures
ra.n() > 0,
rb.n() > 0,
q_le(rb, ra),
{
assert(ra.n() > 0) by (nonlinear_arith)
requires
ra.n() * a.n() == a.d() * ra.d(),
a.n() > 0,
a.d() > 0,
ra.d() > 0,
;
assert(rb.n() > 0) by (nonlinear_arith)
requires
rb.n() * b.n() == b.d() * rb.d(),
b.n() > 0,
b.d() > 0,
rb.d() > 0,
;
let p = a.n() * b.n();
assert(p > 0) by (nonlinear_arith)
requires
p == a.n() * b.n(),
a.n() > 0,
b.n() > 0,
;
assert(ra.d() * rb.d() > 0) by (nonlinear_arith)
requires
ra.d() > 0,
rb.d() > 0,
;
assert((rb.n() * ra.d()) * p == (rb.n() * b.n()) * (a.n() * ra.d())) by (nonlinear_arith)
requires
p == a.n() * b.n(),
;
assert((rb.n() * b.n()) * (a.n() * ra.d()) == (b.d() * rb.d()) * (a.n() * ra.d()));
assert((b.d() * rb.d()) * (a.n() * ra.d()) == (a.n() * b.d()) * (ra.d() * rb.d()))
by (nonlinear_arith);
assert((a.n() * b.d()) * (ra.d() * rb.d()) <= (b.n() * a.d()) * (ra.d() * rb.d()))
by (nonlinear_arith)
requires
a.n() * b.d() <= b.n() * a.d(),
ra.d() * rb.d() > 0,
;
assert((b.n() * a.d()) * (ra.d() * rb.d()) == (a.d() * ra.d()) * (b.n() * rb.d()))
by (nonlinear_arith);
assert((a.d() * ra.d()) * (b.n() * rb.d()) == (ra.n() * a.n()) * (b.n() * rb.d()));
assert((ra.n() * a.n()) * (b.n() * rb.d()) == (ra.n() * rb.d()) * p) by (nonlinear_arith)
requires
p == a.n() * b.n(),
;
assert((rb.n() * ra.d()) * p <= (ra.n() * rb.d()) * p);
lemma_cancel_pos_le(rb.n() * ra.d(), ra.n() * rb.d(), p);
}
}