use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::types::{MAX_MAG, Rat};
verus! {
pub open spec fn pow2(n: nat) -> int
decreases n,
{
if n == 0 {
1int
} else {
2 * pow2((n - 1) as nat)
}
}
pub open spec fn pow10(n: nat) -> int
decreases n,
{
if n == 0 {
1int
} else {
10 * pow10((n - 1) as nat)
}
}
pub open spec fn abs_int(x: int) -> int {
if x >= 0 { x } else { -x }
}
pub open spec fn max_int(a: int, b: int) -> int {
if a >= b { a } else { b }
}
pub open spec fn divides(d: int, n: int) -> bool {
exists|k: int| n == #[trigger] (d * k)
}
pub open spec fn gcd_nat(a: nat, b: nat) -> nat
decreases b,
{
if b == 0 {
a
} else {
gcd_nat(b, (a % b) as nat)
}
}
pub open spec fn gcd_int(a: int, b: int) -> int {
gcd_nat(abs_int(a) as nat, abs_int(b) as nat) as int
}
pub open spec fn bitlen(x: int) -> nat
decreases x,
{
if x <= 0 {
0nat
} else {
(bitlen(x / 2) + 1) as nat
}
}
pub open spec fn max_mag() -> int {
MAX_MAG as int
}
pub open spec fn fits_budget(n: int, d: int) -> bool {
&&& abs_int(n) <= max_mag()
&&& d <= max_mag()
}
pub open spec fn magnitude_fits(n: int, d: int) -> bool {
abs_int(n) <= max_mag() * d
}
impl Rat {
pub open spec fn wf(self) -> bool {
&&& self.den > 0
&&& gcd_int(self.num as int, self.den as int) == 1
&&& (self.num == 0 ==> self.den == 1)
&&& abs_int(self.num as int) <= max_mag()
&&& (self.den as int) <= max_mag()
}
pub open spec fn n(self) -> int {
self.num as int
}
pub open spec fn d(self) -> int {
self.den as int
}
}
pub open spec fn q_eq(a: Rat, b: Rat) -> bool {
a.n() * b.d() == b.n() * a.d()
}
pub open spec fn q_le(a: Rat, b: Rat) -> bool {
a.n() * b.d() <= b.n() * a.d()
}
pub open spec fn q_lt(a: Rat, b: Rat) -> bool {
a.n() * b.d() < b.n() * a.d()
}
pub open spec fn q_is(r: Rat, n: int, d: int) -> bool {
r.n() * d == n * r.d()
}
pub open spec fn q_le_frac(r: Rat, n: int, d: int) -> bool {
r.n() * d <= n * r.d()
}
pub open spec fn q_ge_frac(r: Rat, n: int, d: int) -> bool {
r.n() * d >= n * r.d()
}
pub open spec fn precision_b() -> nat {
61nat
}
pub open spec fn within_error_bound(r: Rat, n: int, d: int) -> bool {
abs_int(r.n() * d - n * r.d()) * pow2(precision_b()) <= r.d() * max_int(d, abs_int(n))
}
pub open spec fn precision_b_nearest() -> nat {
62nat
}
pub open spec fn within_error_bound_nearest(r: Rat, n: int, d: int) -> bool {
abs_int(r.n() * d - n * r.d()) * pow2(precision_b_nearest()) <= r.d() * max_int(
d,
abs_int(n),
)
}
pub open spec fn within_error_bound_k(r: Rat, n: int, d: int, k: nat) -> bool {
abs_int(r.n() * d - n * r.d()) * pow2(precision_b()) <= (k as int) * r.d() * max_int(
d,
abs_int(n),
)
}
pub open spec fn within_abs_error(r: Rat, n: int, d: int, k: nat, m: int) -> bool {
abs_int(r.n() * d - n * r.d()) * pow2(precision_b()) <= (k as int) * m * (r.d() * d)
}
pub proof fn lemma_pow2_pos(n: nat)
ensures
pow2(n) > 0,
decreases n,
{
if n == 0 {
} else {
lemma_pow2_pos((n - 1) as nat);
}
}
pub proof fn lemma_pow2_mono(a: nat, b: nat)
requires
a <= b,
ensures
pow2(a) <= pow2(b),
decreases b,
{
if a == b {
} else {
lemma_pow2_mono(a, (b - 1) as nat);
lemma_pow2_pos((b - 1) as nat);
}
}
pub proof fn lemma_pow2_add(a: nat, b: nat)
ensures
pow2(a + b) == pow2(a) * pow2(b),
decreases b,
{
if b == 0 {
} else {
lemma_pow2_add(a, (b - 1) as nat);
assert(pow2(a + b) == 2 * pow2((a + b - 1) as nat));
assert(pow2(b) == 2 * pow2((b - 1) as nat));
assert(2 * (pow2(a) * pow2((b - 1) as nat)) == pow2(a) * (2 * pow2(
(b - 1) as nat,
))) by (nonlinear_arith);
}
}
pub proof fn lemma_bitlen_char(x: int)
requires
x >= 0,
ensures
x < pow2(bitlen(x)),
x > 0 ==> pow2((bitlen(x) - 1) as nat) <= x,
decreases x,
{
if x <= 0 {
assert(pow2(0) == 1);
} else {
lemma_bitlen_char(x / 2);
assert(x / 2 * 2 <= x < x / 2 * 2 + 2) by (nonlinear_arith);
assert(pow2(bitlen(x)) == 2 * pow2(bitlen(x / 2)));
if bitlen(x / 2) == 0 {
assert(x / 2 == 0);
assert(pow2(bitlen(x)) == 2);
}
}
}
pub proof fn lemma_bitlen_mono(x: int, y: int)
requires
0 <= x <= y,
ensures
bitlen(x) <= bitlen(y),
decreases y,
{
if x <= 0 {
} else {
assert(x / 2 <= y / 2) by (nonlinear_arith)
requires
0 < x <= y,
;
lemma_bitlen_mono(x / 2, y / 2);
}
}
pub proof fn lemma_divides_basic(d: int)
ensures
divides(d, d),
divides(d, 0),
{
assert(d == d * 1);
assert(0 == d * 0);
}
pub proof fn lemma_divides_linear(d: int, a: int, b: int, s: int, t: int)
requires
divides(d, a),
divides(d, b),
ensures
divides(d, s * a + t * b),
{
let ka = choose|k: int| a == #[trigger] (d * k);
let kb = choose|k: int| b == #[trigger] (d * k);
assert(s * a + t * b == d * (s * ka + t * kb)) by (nonlinear_arith)
requires
a == d * ka,
b == d * kb,
;
}
pub proof fn lemma_divides_trans(a: int, b: int, c: int)
requires
divides(a, b),
divides(b, c),
ensures
divides(a, c),
{
let k1 = choose|k: int| b == #[trigger] (a * k);
let k2 = choose|k: int| c == #[trigger] (b * k);
assert(c == a * (k1 * k2)) by (nonlinear_arith)
requires
b == a * k1,
c == b * k2,
;
}
pub proof fn lemma_divides_one(d: int)
requires
divides(d, 1int),
d >= 0,
ensures
d == 1,
{
let k = choose|k: int| 1int == #[trigger] (d * k);
assert(d > 0) by (nonlinear_arith)
requires
1int == d * k,
d >= 0,
;
lemma_divides_le(d, 1int);
}
pub proof fn lemma_pow2_small()
ensures
pow2(0) == 1,
pow2(1) == 2,
{
reveal_with_fuel(pow2, 3);
}
pub proof fn lemma_pow2_61()
ensures
pow2(61) == 2305843009213693952,
{
reveal_with_fuel(pow2, 62);
}
pub proof fn lemma_pow2_62()
ensures
pow2(62) == 4611686018427387904,
{
reveal_with_fuel(pow2, 63);
}
pub proof fn lemma_pow2_63()
ensures
pow2(63) == 9223372036854775808,
{
reveal_with_fuel(pow2, 64);
}
pub proof fn lemma_pow2_64()
ensures
pow2(64) == 18446744073709551616,
{
reveal_with_fuel(pow2, 65);
}
pub proof fn lemma_pow2_124()
ensures
pow2(124) == 21267647932558653966460912964485513216,
{
lemma_pow2_62();
lemma_pow2_add(62, 62);
}
pub proof fn lemma_pow2_125()
ensures
pow2(125) == 42535295865117307932921825928971026432,
{
lemma_pow2_124();
assert(pow2(125) == 2 * pow2(124));
}
pub proof fn lemma_pow2_126()
ensures
pow2(126) == 85070591730234615865843651857942052864,
{
lemma_pow2_125();
assert(pow2(126) == 2 * pow2(125));
}
pub proof fn lemma_max_mag_pow2()
ensures
max_mag() == pow2(62) - 1,
pow2(61) <= max_mag(),
max_mag() > 0,
{
lemma_pow2_61();
lemma_pow2_62();
}
pub proof fn lemma_mul_in_i128(x: int, y: int)
requires
abs_int(x) <= max_mag(),
abs_int(y) <= max_mag(),
ensures
abs_int(x * y) < pow2(124),
{
lemma_max_mag_pow2();
lemma_pow2_62();
lemma_pow2_124();
assert(abs_int(x * y) == abs_int(x) * abs_int(y)) by (nonlinear_arith)
requires
abs_int(x) == (if x >= 0 { x } else { -x }),
abs_int(y) == (if y >= 0 { y } else { -y }),
;
assert(abs_int(x) * abs_int(y) <= max_mag() * max_mag()) by (nonlinear_arith)
requires
0 <= abs_int(x) <= max_mag(),
0 <= abs_int(y) <= max_mag(),
;
assert(max_mag() * max_mag() < pow2(124)) by (nonlinear_arith)
requires
max_mag() == 4611686018427387903,
pow2(124) == 21267647932558653966460912964485513216,
;
}
pub proof fn lemma_abs_mul_pos(x: int, c: int)
requires
c > 0,
ensures
abs_int(x * c) == abs_int(x) * c,
{
if x >= 0 {
assert(x * c >= 0) by (nonlinear_arith)
requires
x >= 0,
c > 0,
;
} else {
assert(x * c < 0) by (nonlinear_arith)
requires
x < 0,
c > 0,
;
assert(-(x * c) == (-x) * c) by (nonlinear_arith);
}
}
pub proof fn lemma_gcd_unit(x: int)
ensures
gcd_int(x, 1) == 1,
{
reveal_with_fuel(gcd_nat, 3);
assert(abs_int(x) % 1 == 0);
}
pub proof fn lemma_divides_le(d: int, n: int)
requires
d > 0,
n > 0,
divides(d, n),
ensures
d <= n,
{
let k = choose|k: int| n == #[trigger] (d * k);
assert(k >= 1) by (nonlinear_arith)
requires
d > 0,
n > 0,
n == d * k,
;
assert(d * 1 <= d * k) by (nonlinear_arith)
requires
d > 0,
k >= 1,
;
}
}