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::round::*;
use crate::types::{Dir, MAX_DEC_PLACES, MAX_MAG, Rat};
verus! {
#[verifier::external_body]
pub fn require_condition(condition: bool, msg: &str)
requires
condition,
{
assert!(condition, "{}", msg);
}
pub open spec fn add_n(a: Rat, b: Rat) -> int {
a.n() * b.d() + b.n() * a.d()
}
pub open spec fn sub_n(a: Rat, b: Rat) -> int {
a.n() * b.d() - b.n() * a.d()
}
pub open spec fn mul_n(a: Rat, b: Rat) -> int {
a.n() * b.n()
}
pub open spec fn prod_d(a: Rat, b: Rat) -> int {
a.d() * b.d()
}
pub open spec fn div_n(a: Rat, b: Rat) -> int {
if b.n() > 0 {
a.n() * b.d()
} else {
-(a.n() * b.d())
}
}
pub open spec fn div_d(a: Rat, b: Rat) -> int {
if b.n() > 0 {
a.d() * b.n()
} else {
-(a.d() * b.n())
}
}
pub open spec fn signed_den_num(num: int, den: int) -> int {
if den < 0 {
-num
} else {
num
}
}
impl Rat {
pub fn zero() -> (r: Rat)
ensures
r.wf(),
r == Rat::from_raw_spec(0, 1),
r.n() == 0,
r.d() == 1,
{
proof {
crate::round::lemma_gcd_one();
lemma_max_mag_pow2();
}
Rat::from_raw_parts(0, 1)
}
pub fn one() -> (r: Rat)
ensures
r.wf(),
r == Rat::from_raw_spec(1, 1),
r.n() == 1,
r.d() == 1,
{
proof {
crate::round::lemma_gcd_one();
lemma_max_mag_pow2();
}
Rat::from_raw_parts(1, 1)
}
pub fn neg_one() -> (r: Rat)
ensures
r.wf(),
r.n() == -1,
r.d() == 1,
{
Rat::one().neg()
}
pub fn from_int(i: i64) -> (r: Option<Rat>)
ensures
r.is_some() ==> {
&&& r.unwrap().wf()
&&& r.unwrap().n() == i as int
&&& r.unwrap().d() == 1
},
r.is_none() ==> abs_int(i as int) > crate::model::max_mag(),
{
if i > MAX_MAG || i < -MAX_MAG {
None
} else {
proof {
crate::round::lemma_gcd_one();
lemma_max_mag_pow2();
}
Some(Rat::from_raw_parts(i, 1))
}
}
pub fn new(num: i64, den: i64) -> (r: Option<Rat>)
ensures
den == 0 ==> r.is_none(),
r.is_some() ==> {
&&& r.unwrap().wf()
&&& q_is(r.unwrap(), num as int, den as int)
},
(den != 0 && abs_int(num as int) <= max_mag() && abs_int(den as int) <= max_mag())
==> r.is_some(),
{
if den == 0 {
return None;
}
let mut n: i128 = num as i128;
let mut d: i128 = den as i128;
let neg: bool = d < 0;
if neg {
n = 0 - n;
d = 0 - d;
}
let g: i128 = gcd_abs_i128(n, d);
let rn: i128 = n / g;
let rd: i128 = d / g;
let arn: i128 = if rn < 0 {
0 - rn
} else {
rn
};
if arn <= MAX_MAG as i128 && rd <= MAX_MAG as i128 {
proof {
lemma_max_mag_pow2();
crate::round::lemma_reduce_exact(n as int, d as int);
crate::gcd::lemma_gcd_reduce_coprime(abs_int(n as int) as nat, d as nat);
crate::round::lemma_reduce_abs(n as int, d as int);
if num == 0 {
crate::gcd::lemma_gcd_zero(d as nat);
vstd::arithmetic::div_mod::lemma_fundamental_div_mod_converse(
d as int,
d as int,
1,
0,
);
}
lemma_new_value(
num as int,
den as int,
n as int,
d as int,
rn as int,
rd as int,
g as int,
neg,
);
if rn == 0 {
assert(n == 0) by (nonlinear_arith)
requires
n as int == (rn as int) * (g as int),
rn == 0,
;
assert(num == 0);
assert(rd == 1);
}
}
Some(Rat::from_raw_parts(rn as i64, rd as i64))
} else {
proof {
lemma_max_mag_pow2();
crate::round::lemma_reduce_shrinks(n as int, d as int);
}
None
}
}
pub fn new_rounded(num: i64, den: i64, dir: Dir) -> (r: Option<Rat>)
ensures
r.is_none() <==> den == 0,
r.is_some() ==> r.unwrap().wf(),
r.is_some() ==> r.unwrap() == round_frac(
signed_den_num(num as int, den as int),
abs_int(den as int),
dir,
),
(r.is_some() && !saturated(signed_den_num(num as int, den as int), abs_int(
den as int,
))) ==> {
&&& dir == Dir::Down ==> q_le_frac(
r.unwrap(),
signed_den_num(num as int, den as int),
abs_int(den as int),
)
&&& dir == Dir::Up ==> q_ge_frac(
r.unwrap(),
signed_den_num(num as int, den as int),
abs_int(den as int),
)
&&& within_error_bound(
r.unwrap(),
signed_den_num(num as int, den as int),
abs_int(den as int),
)
},
{
if den == 0 {
return None;
}
let mut n: i128 = num as i128;
let mut d: i128 = den as i128;
if d < 0 {
n = 0 - n;
d = 0 - d;
}
proof {
lemma_pow2_124();
lemma_pow2_126();
if !saturated(n as int, d as int) {
crate::round::lemma_r2_r3_directed(n as int, d as int, dir);
}
}
Some(round_frac_exec(n, d, dir))
}
pub fn from_decimal(mantissa: i64, dec_places: u8) -> (r: Option<Rat>)
ensures
r.is_some() ==> r.unwrap().wf(),
dec_places > MAX_DEC_PLACES ==> r.is_none(),
r.is_some() ==> q_is(r.unwrap(), mantissa as int, pow10(dec_places as nat)),
r.is_some() <==> (dec_places <= MAX_DEC_PLACES && abs_int(mantissa as int)
<= max_mag()),
{
if dec_places > MAX_DEC_PLACES {
return None;
}
if mantissa > MAX_MAG || mantissa < -MAX_MAG {
return None;
}
let scale: i64 = pow10_i64(dec_places);
proof {
lemma_max_mag_pow2();
}
Rat::new(mantissa, scale)
}
}
pub fn pow10_i64(n: u8) -> (r: i64)
requires
n <= MAX_DEC_PLACES,
ensures
1 <= r <= 1000000000000000000,
r == pow10(n as nat),
{
reveal_with_fuel(pow10, 20);
match n {
0 => 1,
1 => 10,
2 => 100,
3 => 1000,
4 => 10000,
5 => 100000,
6 => 1000000,
7 => 10000000,
8 => 100000000,
9 => 1000000000,
10 => 10000000000,
11 => 100000000000,
12 => 1000000000000,
13 => 10000000000000,
14 => 100000000000000,
15 => 1000000000000000,
16 => 10000000000000000,
17 => 100000000000000000,
_ => 1000000000000000000,
}
}
impl Rat {
pub fn add_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(add_n(a, b), prod_d(a, b), dir),
exact_path(add_n(a, b), prod_d(a, b)) ==> q_is(r, add_n(a, b), prod_d(a, b)),
!saturated(add_n(a, b), prod_d(a, b)) ==> within_error_bound(
r,
add_n(a, b),
prod_d(a, b),
),
{
proof {
lemma_op_widths(a, b);
}
let n: i128 = (a.numerator() as i128) * (b.denominator() as i128)
+ (b.numerator() as i128) * (a.denominator() as i128);
let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
let r = round_frac_exec(n, d, dir);
proof {
if exact_path(add_n(a, b), prod_d(a, b)) {
crate::round::lemma_r1_identity(add_n(a, b), prod_d(a, b), dir);
}
if !saturated(add_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error(add_n(a, b), prod_d(a, b), dir);
}
}
r
}
pub fn sub_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(sub_n(a, b), prod_d(a, b), dir),
exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(r, sub_n(a, b), prod_d(a, b)),
!saturated(sub_n(a, b), prod_d(a, b)) ==> within_error_bound(
r,
sub_n(a, b),
prod_d(a, b),
),
{
proof {
lemma_op_widths(a, b);
}
let n: i128 = (a.numerator() as i128) * (b.denominator() as i128)
- (b.numerator() as i128) * (a.denominator() as i128);
let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
let r = round_frac_exec(n, d, dir);
proof {
if exact_path(sub_n(a, b), prod_d(a, b)) {
crate::round::lemma_r1_identity(sub_n(a, b), prod_d(a, b), dir);
}
if !saturated(sub_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error(sub_n(a, b), prod_d(a, b), dir);
}
}
r
}
pub fn mul_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(mul_n(a, b), prod_d(a, b), dir),
exact_path(mul_n(a, b), prod_d(a, b)) ==> q_is(r, mul_n(a, b), prod_d(a, b)),
!saturated(mul_n(a, b), prod_d(a, b)) ==> within_error_bound(
r,
mul_n(a, b),
prod_d(a, b),
),
{
proof {
lemma_op_widths(a, b);
}
let n: i128 = (a.numerator() as i128) * (b.numerator() as i128);
let d: i128 = (a.denominator() as i128) * (b.denominator() as i128);
let r = round_frac_exec(n, d, dir);
proof {
if exact_path(mul_n(a, b), prod_d(a, b)) {
crate::round::lemma_r1_identity(mul_n(a, b), prod_d(a, b), dir);
}
if !saturated(mul_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error(mul_n(a, b), prod_d(a, b), dir);
}
}
r
}
pub fn div_dir(a: Rat, b: Rat, dir: Dir) -> (r: Rat)
requires
a.wf(),
b.wf(),
b.n() != 0,
ensures
r.wf(),
r == round_frac(div_n(a, b), div_d(a, b), dir),
exact_path(div_n(a, b), div_d(a, b)) ==> q_is(r, div_n(a, b), div_d(a, b)),
!saturated(div_n(a, b), div_d(a, b)) ==> within_error_bound(
r,
div_n(a, b),
div_d(a, b),
),
{
require_condition(
b.numerator() != 0,
"the-q: Rat::div by zero. Rat::div requires a nonzero divisor. Use \
Rat::checked_div, which returns None, or Q::div, which returns an \
infinity or Nan.",
);
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_124();
crate::model::lemma_pow2_126();
assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
requires
a.d() > 0,
;
assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
requires
a.d() > 0,
;
}
let mut n: i128 = (a.numerator() as i128) * (b.denominator() as i128);
let mut d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
if d < 0 {
n = 0 - n;
d = 0 - d;
}
let r = round_frac_exec(n, d, dir);
proof {
if exact_path(div_n(a, b), div_d(a, b)) {
crate::round::lemma_r1_identity(div_n(a, b), div_d(a, b), dir);
}
if !saturated(div_n(a, b), div_d(a, b)) {
crate::round::lemma_r3_error(div_n(a, b), div_d(a, b), dir);
}
}
r
}
pub fn add(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(add_n(a, b), prod_d(a, b), Dir::Nearest),
exact_path(add_n(a, b), prod_d(a, b)) ==> q_is(r, add_n(a, b), prod_d(a, b)),
!saturated(add_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
r,
add_n(a, b),
prod_d(a, b),
),
{
let r = Rat::add_dir(a, b, Dir::Nearest);
proof {
lemma_op_widths(a, b);
if !saturated(add_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error_nearest(add_n(a, b), prod_d(a, b));
}
}
r
}
pub fn sub(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(sub_n(a, b), prod_d(a, b), Dir::Nearest),
exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(r, sub_n(a, b), prod_d(a, b)),
!saturated(sub_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
r,
sub_n(a, b),
prod_d(a, b),
),
{
let r = Rat::sub_dir(a, b, Dir::Nearest);
proof {
lemma_op_widths(a, b);
if !saturated(sub_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error_nearest(sub_n(a, b), prod_d(a, b));
}
}
r
}
pub fn mul(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == round_frac(mul_n(a, b), prod_d(a, b), Dir::Nearest),
exact_path(mul_n(a, b), prod_d(a, b)) ==> q_is(r, mul_n(a, b), prod_d(a, b)),
!saturated(mul_n(a, b), prod_d(a, b)) ==> crate::model::within_error_bound_nearest(
r,
mul_n(a, b),
prod_d(a, b),
),
{
let r = Rat::mul_dir(a, b, Dir::Nearest);
proof {
lemma_op_widths(a, b);
if !saturated(mul_n(a, b), prod_d(a, b)) {
crate::round::lemma_r3_error_nearest(mul_n(a, b), prod_d(a, b));
}
}
r
}
pub fn div(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
b.n() != 0,
ensures
r.wf(),
r == round_frac(div_n(a, b), div_d(a, b), Dir::Nearest),
exact_path(div_n(a, b), div_d(a, b)) ==> q_is(r, div_n(a, b), div_d(a, b)),
!saturated(div_n(a, b), div_d(a, b)) ==> crate::model::within_error_bound_nearest(
r,
div_n(a, b),
div_d(a, b),
),
{
let r = Rat::div_dir(a, b, Dir::Nearest);
proof {
lemma_op_widths(a, b);
assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
requires
a.d() > 0,
;
assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
requires
a.d() > 0,
;
if !saturated(div_n(a, b), div_d(a, b)) {
crate::round::lemma_r3_error_nearest(div_n(a, b), div_d(a, b));
}
}
r
}
pub fn checked_add(a: Rat, b: Rat) -> (r: Option<Rat>)
requires
a.wf(),
b.wf(),
ensures
r.is_none() <==> saturated(add_n(a, b), prod_d(a, b)),
r.is_some() ==> r.unwrap() == round_frac(add_n(a, b), prod_d(a, b), Dir::Nearest),
r.is_some() ==> r.unwrap().wf(),
{
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_126();
}
if magnitude_fits_exec(add_n_exec(a, b), prod_d_exec(a, b)) {
Some(Rat::add(a, b))
} else {
None
}
}
pub fn checked_mul(a: Rat, b: Rat) -> (r: Option<Rat>)
requires
a.wf(),
b.wf(),
ensures
r.is_none() <==> saturated(mul_n(a, b), prod_d(a, b)),
r.is_some() ==> r.unwrap() == round_frac(mul_n(a, b), prod_d(a, b), Dir::Nearest),
r.is_some() ==> r.unwrap().wf(),
{
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_126();
}
if magnitude_fits_exec(mul_n_exec(a, b), prod_d_exec(a, b)) {
Some(Rat::mul(a, b))
} else {
None
}
}
pub fn checked_sub(a: Rat, b: Rat) -> (r: Option<Rat>)
requires
a.wf(),
b.wf(),
ensures
r.is_none() <==> saturated(sub_n(a, b), prod_d(a, b)),
r.is_some() ==> r.unwrap() == round_frac(sub_n(a, b), prod_d(a, b), Dir::Nearest),
r.is_some() ==> r.unwrap().wf(),
{
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_126();
}
if magnitude_fits_exec(sub_n_exec(a, b), prod_d_exec(a, b)) {
Some(Rat::sub(a, b))
} else {
None
}
}
pub fn checked_sub_dir(a: Rat, b: Rat, dir: Dir) -> (r: Option<Rat>)
requires
a.wf(),
b.wf(),
ensures
r.is_none() <==> saturated(sub_n(a, b), prod_d(a, b)),
r.is_some() ==> r.unwrap() == round_frac(sub_n(a, b), prod_d(a, b), dir),
r.is_some() ==> r.unwrap().wf(),
r.is_some() && dir == Dir::Down ==> q_le_frac(
r.unwrap(), sub_n(a, b), prod_d(a, b),
),
r.is_some() && dir == Dir::Up ==> q_ge_frac(
r.unwrap(), sub_n(a, b), prod_d(a, b),
),
r.is_some() && exact_path(sub_n(a, b), prod_d(a, b)) ==> q_is(
r.unwrap(), sub_n(a, b), prod_d(a, b),
),
r.is_some() ==> within_error_bound(
r.unwrap(), sub_n(a, b), prod_d(a, b),
),
{
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_126();
}
if magnitude_fits_exec(sub_n_exec(a, b), prod_d_exec(a, b)) {
let value = Rat::sub_dir(a, b, dir);
proof {
crate::round::lemma_r2_directed(sub_n(a, b), prod_d(a, b));
}
Some(value)
} else {
None
}
}
pub fn checked_div(a: Rat, b: Rat) -> (r: Option<Rat>)
requires
a.wf(),
b.wf(),
ensures
b.n() == 0 ==> r.is_none(),
b.n() != 0 ==> (r.is_none() <==> saturated(div_n(a, b), div_d(a, b))),
r.is_some() ==> b.n() != 0,
r.is_some() ==> r.unwrap() == round_frac(div_n(a, b), div_d(a, b), Dir::Nearest),
r.is_some() ==> r.unwrap().wf(),
{
if b.numerator() == 0 {
return None;
}
proof {
lemma_op_widths(a, b);
crate::model::lemma_pow2_126();
}
if magnitude_fits_exec(div_n_exec(a, b), div_d_exec(a, b)) {
Some(Rat::div(a, b))
} else {
None
}
}
pub fn neg(self) -> (r: Rat)
requires
self.wf(),
ensures
r.wf(),
r.n() == -self.n(),
r.d() == self.d(),
{
proof {
lemma_max_mag_pow2();
assert(gcd_int(-self.n(), self.d()) == gcd_int(self.n(), self.d()));
}
Rat::from_raw_parts(0 - self.numerator(), self.denominator())
}
pub fn abs(self) -> (r: Rat)
requires
self.wf(),
ensures
r.wf(),
r.n() == abs_int(self.n()),
r.d() == self.d(),
{
if self.numerator() < 0 {
self.neg()
} else {
self
}
}
pub fn recip(self) -> (r: Rat)
requires
self.wf(),
self.n() != 0,
ensures
r.wf(),
r.n() * self.n() > 0,
q_is_recip(r, self),
{
require_condition(
self.numerator() != 0,
"the-q: Rat::recip of zero. The reciprocal of zero is not a \
rational and no Rat denotes it. Use Q::recip, which returns \
PosInf.",
);
proof {
lemma_max_mag_pow2();
}
if self.numerator() > 0 {
proof {
assert(gcd_int(self.d(), self.n()) == gcd_int(self.n(), self.d())) by {
lemma_gcd_sym(abs_int(self.n()) as nat, self.d() as nat);
}
assert(self.d() * self.n() > 0) by (nonlinear_arith)
requires
self.d() > 0,
self.n() > 0,
;
}
Rat::from_raw_parts(self.denominator(), self.numerator())
} else {
proof {
assert(gcd_int(-self.d(), -self.n()) == gcd_int(self.n(), self.d())) by {
lemma_gcd_sym(abs_int(self.n()) as nat, self.d() as nat);
}
assert((-self.d()) * self.n() > 0) by (nonlinear_arith)
requires
self.d() > 0,
self.n() < 0,
;
assert((-self.d()) * self.n() == self.d() * (-self.n())) by (nonlinear_arith);
}
Rat::from_raw_parts(0 - self.denominator(), 0 - self.numerator())
}
}
pub fn pow_u32(self, e: u32) -> (r: Rat)
requires
self.wf(),
ensures
r.wf(),
{
let mut acc = Rat::one();
let mut i: u32 = 0;
while i < e
invariant
acc.wf(),
self.wf(),
i <= e,
decreases e - i,
{
acc = Rat::mul(acc, self);
i = i + 1;
}
acc
}
}
pub open spec fn q_is_recip(r: Rat, q: Rat) -> bool {
r.n() * q.n() == q.d() * r.d()
}
pub proof fn lemma_gcd_sym(a: nat, b: nat)
ensures
crate::model::gcd_nat(a, b) == crate::model::gcd_nat(b, a),
{
crate::gcd::lemma_gcd_divides(a, b);
crate::gcd::lemma_gcd_divides(b, a);
crate::gcd::lemma_gcd_greatest(a, b, crate::model::gcd_nat(b, a) as int);
crate::gcd::lemma_gcd_greatest(b, a, crate::model::gcd_nat(a, b) as int);
if a > 0 || b > 0 {
crate::gcd::lemma_gcd_pos(a, b);
crate::gcd::lemma_gcd_pos(b, a);
crate::model::lemma_divides_le(
crate::model::gcd_nat(a, b) as int,
crate::model::gcd_nat(b, a) as int,
);
crate::model::lemma_divides_le(
crate::model::gcd_nat(b, a) as int,
crate::model::gcd_nat(a, b) as int,
);
}
}
pub proof fn lemma_op_widths(a: Rat, b: Rat)
requires
a.wf(),
b.wf(),
ensures
abs_int(a.n() * b.d()) < 21267647932558653966460912964485513216,
abs_int(b.n() * a.d()) < 21267647932558653966460912964485513216,
abs_int(a.d() * b.n()) < 21267647932558653966460912964485513216,
abs_int(a.d() * b.n()) <= pow2(124),
abs_int(a.n() * b.d()) <= pow2(124),
abs_int(a.n() * b.n()) < 21267647932558653966460912964485513216,
abs_int(a.d() * b.d()) < 21267647932558653966460912964485513216,
abs_int(add_n(a, b)) < 42535295865117307932921825928971026432,
abs_int(sub_n(a, b)) < 42535295865117307932921825928971026432,
abs_int(mul_n(a, b)) < 42535295865117307932921825928971026432,
a.d() * b.d() > 0,
a.d() > 0,
b.d() > 0,
abs_int(a.d() * b.d()) <= pow2(124),
abs_int(add_n(a, b)) < pow2(126),
abs_int(sub_n(a, b)) < pow2(126),
abs_int(mul_n(a, b)) < pow2(126),
{
lemma_mul_in_i128(a.n(), b.d());
lemma_mul_in_i128(b.n(), a.d());
lemma_mul_in_i128(a.d(), b.n());
lemma_mul_in_i128(a.n(), b.n());
lemma_mul_in_i128(a.d(), b.d());
lemma_pow2_124();
lemma_pow2_126();
assert(a.d() * b.d() > 0) by (nonlinear_arith)
requires
a.d() > 0,
b.d() > 0,
;
}
pub proof fn lemma_new_value(
num: int,
den: int,
n: int,
d: int,
rn: int,
rd: int,
g: int,
neg: bool,
)
requires
den != 0,
g > 0,
n == rn * g,
d == rd * g,
neg ==> (n == -num && d == -den),
!neg ==> (n == num && d == den),
ensures
rn * den == num * rd,
{
if neg {
assert(den == -(rd * g) && num == -(rn * g));
assert(rn * den == -((rn * rd) * g)) by (nonlinear_arith)
requires
den == -(rd * g),
;
assert(num * rd == -((rn * rd) * g)) by (nonlinear_arith)
requires
num == -(rn * g),
;
} else {
assert(den == rd * g && num == rn * g);
assert(rn * den == (rn * rd) * g) by (nonlinear_arith)
requires
den == rd * g,
;
assert(num * rd == (rn * rd) * g) by (nonlinear_arith)
requires
num == rn * g,
;
}
}
pub fn add_n_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
ensures
r as int == add_n(a, b),
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
}
(a.numerator() as i128) * (b.denominator() as i128)
+ (b.numerator() as i128) * (a.denominator() as i128)
}
pub fn sub_n_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
ensures
r as int == sub_n(a, b),
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
}
(a.numerator() as i128) * (b.denominator() as i128)
- (b.numerator() as i128) * (a.denominator() as i128)
}
pub fn mul_n_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
ensures
r as int == mul_n(a, b),
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
}
(a.numerator() as i128) * (b.numerator() as i128)
}
pub fn prod_d_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
ensures
r as int == prod_d(a, b),
r > 0,
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
}
(a.denominator() as i128) * (b.denominator() as i128)
}
pub fn div_n_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
b.n() != 0,
ensures
r as int == div_n(a, b),
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
requires
a.d() > 0,
;
assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
requires
a.d() > 0,
;
}
let mut n: i128 = (a.numerator() as i128) * (b.denominator() as i128);
let d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
if d < 0 {
n = 0 - n;
}
n
}
pub fn div_d_exec(a: Rat, b: Rat) -> (r: i128)
requires
a.wf(),
b.wf(),
b.n() != 0,
ensures
r as int == div_d(a, b),
r > 0,
{
proof {
lemma_op_widths(a, b);
lemma_pow2_126();
assert(b.n() > 0 ==> a.d() * b.n() > 0) by (nonlinear_arith)
requires
a.d() > 0,
;
assert(b.n() < 0 ==> a.d() * b.n() < 0) by (nonlinear_arith)
requires
a.d() > 0,
;
}
let mut d: i128 = (a.denominator() as i128) * (b.numerator() as i128);
if d < 0 {
d = 0 - d;
}
d
}
pub fn magnitude_fits_exec(n: i128, d: i128) -> (r: bool)
requires
d > 0,
abs_int(n as int) < crate::round::num_input_bound(),
ensures
r <==> magnitude_fits(n as int, d as int),
{
proof {
crate::model::lemma_pow2_126();
}
let m: i128 = if n < 0 {
0 - n
} else {
n
};
let ip: i128 = m / d;
let fr: i128 = m % d;
proof {
crate::round::lemma_magnitude_test(m as int, d as int, ip as int, fr as int);
}
ip < MAX_MAG as i128 || (ip == MAX_MAG as i128 && fr == 0)
}
impl Rat {
pub fn compare(a: Rat, b: Rat) -> (r: i32)
requires
a.wf(),
b.wf(),
ensures
r == 0 <==> q_eq(a, b),
r < 0 <==> q_lt(a, b),
r > 0 <==> q_lt(b, a),
{
proof {
lemma_op_widths(a, b);
}
let l: i128 = (a.numerator() as i128) * (b.denominator() as i128);
let r: i128 = (b.numerator() as i128) * (a.denominator() as i128);
if l < r {
-1
} else if l > r {
1
} else {
0
}
}
pub fn eq_q(a: Rat, b: Rat) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_eq(a, b),
{
Rat::compare(a, b) == 0
}
pub fn lt(a: Rat, b: Rat) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_lt(a, b),
{
Rat::compare(a, b) < 0
}
pub fn le(a: Rat, b: Rat) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_le(a, b),
{
Rat::compare(a, b) <= 0
}
pub fn gt(a: Rat, b: Rat) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_lt(b, a),
{
Rat::compare(a, b) > 0
}
pub fn ge(a: Rat, b: Rat) -> (r: bool)
requires
a.wf(),
b.wf(),
ensures
r <==> q_le(b, a),
{
Rat::compare(a, b) >= 0
}
pub fn is_zero(&self) -> (r: bool)
requires
self.wf(),
ensures
r <==> self.n() == 0,
{
self.numerator() == 0
}
pub fn is_one(&self) -> (r: bool)
requires
self.wf(),
ensures
r <==> self.n() == self.d(),
{
proof {
if self.n() == self.d() {
crate::model::lemma_divides_basic(self.n());
crate::gcd::lemma_gcd_greatest(
abs_int(self.n()) as nat,
self.d() as nat,
self.n(),
);
crate::model::lemma_divides_le(self.n(), 1);
}
}
self.numerator() == 1 && self.denominator() == 1
}
pub fn signum(&self) -> (r: i32)
requires
self.wf(),
ensures
r == 0 <==> self.n() == 0,
r < 0 <==> self.n() < 0,
r > 0 <==> self.n() > 0,
{
if self.numerator() < 0 {
-1
} else if self.numerator() > 0 {
1
} else {
0
}
}
pub fn in_unit_interval(&self) -> (r: bool)
requires
self.wf(),
ensures
r <==> (self.n() >= 0 && self.n() <= self.d()),
{
self.numerator() >= 0 && self.numerator() <= self.denominator()
}
pub fn min(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == a || r == b,
q_le(r, a),
q_le(r, b),
q_le(a, b) ==> r == a,
!q_le(a, b) ==> r == b,
{
if Rat::le(a, b) {
a
} else {
b
}
}
pub fn max(a: Rat, b: Rat) -> (r: Rat)
requires
a.wf(),
b.wf(),
ensures
r.wf(),
r == a || r == b,
q_le(a, r),
q_le(b, r),
q_le(a, b) ==> r == b,
!q_le(a, b) ==> r == a,
{
if Rat::le(a, b) {
b
} else {
a
}
}
pub fn clamp(a: Rat, lo: Rat, hi: Rat) -> (r: Rat)
requires
a.wf(),
lo.wf(),
hi.wf(),
q_le(lo, hi),
ensures
r.wf(),
r == a || r == lo || r == hi,
q_le(lo, r),
q_le(r, hi),
(q_le(lo, a) && q_le(a, hi)) ==> r == a,
!q_le(lo, a) ==> r == lo,
!q_le(a, hi) ==> r == hi,
{
proof {
lemma_le_trans(lo, hi, a);
lemma_le_trans(a, lo, hi);
}
if Rat::lt(a, lo) {
lo
} else if Rat::lt(hi, a) {
hi
} else {
a
}
}
}
pub proof fn lemma_le_trans(a: Rat, b: Rat, c: Rat)
requires
a.wf(),
b.wf(),
c.wf(),
ensures
(q_le(a, b) && q_le(b, c)) ==> q_le(a, c),
{
if q_le(a, b) && q_le(b, c) {
assert(a.n() * c.d() <= c.n() * a.d()) by (nonlinear_arith)
requires
a.d() > 0,
b.d() > 0,
c.d() > 0,
a.n() * b.d() <= b.n() * a.d(),
b.n() * c.d() <= c.n() * b.d(),
;
}
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl PartialOrd for Rat {
fn partial_cmp(&self, other: &Rat) -> Option<core::cmp::Ordering> {
Some(<Rat as Ord>::cmp(self, other))
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl Ord for Rat {
fn cmp(&self, other: &Rat) -> core::cmp::Ordering {
Rat::compare(*self, *other).cmp(&0)
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::ops::Add for Rat {
type Output = Rat;
fn add(self, rhs: Rat) -> Rat {
Rat::add(self, rhs)
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::ops::Sub for Rat {
type Output = Rat;
fn sub(self, rhs: Rat) -> Rat {
Rat::sub(self, rhs)
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::ops::Mul for Rat {
type Output = Rat;
fn mul(self, rhs: Rat) -> Rat {
Rat::mul(self, rhs)
}
}
#[cfg_attr(verus_keep_ghost, verifier::external)]
impl core::ops::Neg for Rat {
type Output = Rat;
fn neg(self) -> Rat {
Rat::neg(self)
}
}