use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
use crate::ext::{Q, Sign};
use crate::types::Rat;
verus! {
pub fn isqrt_i64(n: i64) -> (r: i64)
requires
0 <= n <= crate::types::MAX_MAG,
ensures
r >= 0,
(r as int) * (r as int) <= n as int,
((r as int) + 1) * ((r as int) + 1) > n as int,
{
if n < 2 {
proof {
assert((n as int) * (n as int) <= n as int) by (nonlinear_arith)
requires
0 <= n as int <= 1,
;
assert(((n as int) + 1) * ((n as int) + 1) > n as int) by (nonlinear_arith)
requires
0 <= n as int <= 1,
;
}
return n;
}
let mut x: i64 = n;
let mut y: i64 = (n + 1) / 2;
while y < x
invariant
x >= 1,
y >= 1,
x <= n,
n <= crate::types::MAX_MAG,
decreases x,
{
x = y;
proof {
vstd::arithmetic::div_mod::lemma_div_is_ordered_by_denominator(
n as int,
x as int,
n as int,
);
vstd::arithmetic::div_mod::lemma_div_by_self(n as int);
}
y = (x + n / x) / 2;
}
let mut r: i64 = x;
proof {
assert(0 <= (r as int) * (r as int) <= (crate::types::MAX_MAG as int) * (crate::types::MAX_MAG as int))
by (nonlinear_arith)
requires
0 <= r as int <= crate::types::MAX_MAG as int,
;
}
let mut sq: i128 = (r as i128) * (r as i128);
while r > 0 && sq > n as i128
invariant
0 <= r <= n,
2 <= n <= crate::types::MAX_MAG,
sq as int == (r as int) * (r as int),
decreases r,
{
r = r - 1;
proof {
assert(0 <= (r as int) * (r as int) <= (crate::types::MAX_MAG as int) * (crate::types::MAX_MAG as int))
by (nonlinear_arith)
requires
0 <= r as int <= crate::types::MAX_MAG as int,
;
}
sq = (r as i128) * (r as i128);
}
proof {
assert((r as int) * (r as int) <= n as int) by (nonlinear_arith)
requires
0 <= r as int,
2 <= n as int,
r as int == 0 || (r as int) * (r as int) <= n as int,
;
}
proof {
assert(0 <= ((r as int) + 1) * ((r as int) + 1) <= ((crate::types::MAX_MAG as int) + 1) * (
(crate::types::MAX_MAG as int) + 1)) by (nonlinear_arith)
requires
0 <= r as int <= crate::types::MAX_MAG as int,
;
}
let mut nxt: i128 = ((r as i128) + 1) * ((r as i128) + 1);
while nxt <= n as i128
invariant
0 <= r <= n,
2 <= n <= crate::types::MAX_MAG,
(r as int) * (r as int) <= n as int,
nxt as int == ((r as int) + 1) * ((r as int) + 1),
decreases n - r,
{
proof {
assert((r as int) + 1 <= n as int) by (nonlinear_arith)
requires
0 <= r as int,
((r as int) + 1) * ((r as int) + 1) <= n as int,
;
}
r = r + 1;
proof {
assert(0 <= ((r as int) + 1) * ((r as int) + 1) <= ((crate::types::MAX_MAG as int) + 1) * (
(crate::types::MAX_MAG as int) + 1)) by (nonlinear_arith)
requires
0 <= r as int <= crate::types::MAX_MAG as int,
;
}
nxt = ((r as i128) + 1) * ((r as i128) + 1);
}
r
}
pub proof fn theorem_isqrt_unique(n: int, r1: int, r2: int)
requires
0 <= r1,
r1 * r1 <= n,
n < (r1 + 1) * (r1 + 1),
0 <= r2,
r2 * r2 <= n,
n < (r2 + 1) * (r2 + 1),
ensures
r1 == r2,
{
if r1 < r2 {
assert((r1 + 1) * (r1 + 1) <= r2 * r2) by (nonlinear_arith)
requires
0 <= r1 + 1,
r1 + 1 <= r2,
;
}
if r2 < r1 {
assert((r2 + 1) * (r2 + 1) <= r1 * r1) by (nonlinear_arith)
requires
0 <= r2 + 1,
r2 + 1 <= r1,
;
}
}
pub proof fn theorem_isqrt_monotone(n1: int, n2: int, r1: int, r2: int)
requires
n1 <= n2,
0 <= r1,
r1 * r1 <= n1,
n1 < (r1 + 1) * (r1 + 1),
0 <= r2,
r2 * r2 <= n2,
n2 < (r2 + 1) * (r2 + 1),
ensures
r1 <= r2,
{
if r2 < r1 {
assert((r2 + 1) * (r2 + 1) <= r1 * r1) by (nonlinear_arith)
requires
0 <= r2 + 1,
r2 + 1 <= r1,
;
}
}
pub proof fn theorem_isqrt_of_square(k: int, r: int)
requires
0 <= k,
0 <= r,
r * r <= k * k,
k * k < (r + 1) * (r + 1),
ensures
r == k,
{
assert(k * k < (k + 1) * (k + 1)) by (nonlinear_arith)
requires
0 <= k,
;
theorem_isqrt_unique(k * k, r, k);
}
fn sqrt_seed(x: Rat) -> (r: Q)
requires
x.wf(),
x.n() > 0,
ensures
r.wf(),
{
let rn = isqrt_i64(x.numerator());
let rd = isqrt_i64(x.denominator());
Q::new(if rn < 1 {
1
} else {
rn
}, if rd < 1 {
1
} else {
rd
})
}
impl Q {
pub fn sqrt(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
self.spec_is_nan() ==> r.spec_is_nan(),
self.spec_is_zero() ==> r.spec_is_zero(),
{
match self {
Q::Number(x) => {
let s = x.signum();
if s < 0 {
Q::Nan
} else if s == 0 {
Q::zero()
} else {
let two = Q::new(2, 1);
let mut y = sqrt_seed(x);
let q = Q::Number(x);
let mut i: u32 = 0;
while i < SQRT_ITERS
invariant
y.wf(),
q.wf(),
two.wf(),
i <= SQRT_ITERS,
decreases SQRT_ITERS - i,
{
y = Q::div(Q::add(y, Q::div(q, y)), two);
i = i + 1;
}
y
}
},
Q::PosSat => Q::Nan,
Q::NegSat => Q::Nan,
Q::PosInf => Q::PosInf,
Q::NegInf => Q::Nan,
Q::Nan => Q::Nan,
}
}
}
const SERIES_TERMS: u32 = 20;
const TRIG_TERMS: u32 = 11;
const SQRT_ITERS: u32 = 7;
const EXP_ARG_LIMIT: i64 = 44;
const HYP_ARG_LIMIT: i64 = 22;
pub const PI_NUM: i64 = 1_811_004_864_519_280_709;
pub const PI_DEN: i64 = 576_460_752_303_423_488;
pub proof fn lemma_fx_to_grid_decomposition(m: int, d: int, q: int, rem: int)
requires
0 <= m,
0 < d,
q == (m * crate::fx::FX_ONE as int) / d,
rem == (m * crate::fx::FX_ONE as int) % d,
ensures
m * crate::fx::FX_ONE as int == q * d + rem,
0 <= rem < d,
{
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(
m * crate::fx::FX_ONE as int,
d,
);
vstd::arithmetic::div_mod::lemma_mod_bound(m * crate::fx::FX_ONE as int, d);
}
pub proof fn lemma_fx_to_grid_nearest(
m: int,
d: int,
q: int,
rem: int,
rounded: int,
)
requires
0 <= m,
0 < d,
m * crate::fx::FX_ONE as int == q * d + rem,
0 <= rem < d,
rounded == if 2 * rem > d || (2 * rem == d && q % 2 == 1) {
q + 1
} else {
q
},
ensures
2 * crate::model::abs_int(rounded * d - m * crate::fx::FX_ONE as int) <= d,
{
let scaled = m * crate::fx::FX_ONE as int;
assert(scaled == q * d + rem);
if 2 * rem > d || (2 * rem == d && q % 2 == 1) {
assert(rounded == q + 1);
assert(2 * rem >= d);
assert(rounded * d == (q + 1) * d) by (nonlinear_arith)
requires
rounded == q + 1,
;
assert((q + 1) * d == q * d + d) by (nonlinear_arith);
assert(rounded * d - scaled == d - rem);
assert(0 <= d - rem);
assert(crate::model::abs_int(d - rem) == d - rem);
assert(2 * (d - rem) <= d);
} else {
assert(rounded == q);
assert(2 * rem <= d);
assert(rounded * d == q * d);
assert(rounded * d - scaled == -rem);
assert(crate::model::abs_int(-rem) == rem);
}
}
pub proof fn lemma_fx_to_grid_ties_even(
m: int,
d: int,
q: int,
rem: int,
rounded: int,
)
requires
0 <= m,
0 < d,
m * crate::fx::FX_ONE as int == q * d + rem,
0 <= rem < d,
rounded == if 2 * rem > d || (2 * rem == d && q % 2 == 1) {
q + 1
} else {
q
},
ensures
2 * crate::model::abs_int(rounded * d - m * crate::fx::FX_ONE as int) == d
==> rounded % 2 == 0,
{
let scaled = m * crate::fx::FX_ONE as int;
assert(scaled == q * d + rem);
vstd::arithmetic::div_mod::lemma_mod_bound(q, 2);
if 2 * rem > d || (2 * rem == d && q % 2 == 1) {
assert(rounded == q + 1);
assert(2 * rem >= d);
assert(rounded * d == (q + 1) * d) by (nonlinear_arith)
requires
rounded == q + 1,
;
assert((q + 1) * d == q * d + d) by (nonlinear_arith);
assert(rounded * d - scaled == d - rem);
assert(crate::model::abs_int(d - rem) == d - rem);
if 2 * crate::model::abs_int(rounded * d - m * crate::fx::FX_ONE as int) == d {
assert(2 * rem == d);
assert(q % 2 == 1);
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(q, 2);
vstd::arithmetic::div_mod::lemma_fundamental_div_mod_converse(
q + 1,
2,
q / 2 + 1,
0,
);
}
} else {
assert(rounded == q);
assert(2 * rem <= d);
assert(rounded * d == q * d);
assert(rounded * d - scaled == -rem);
assert(crate::model::abs_int(-rem) == rem);
if 2 * crate::model::abs_int(rounded * d - m * crate::fx::FX_ONE as int) == d {
assert(2 * rem == d);
assert(q % 2 != 1);
assert(q % 2 == 0);
}
}
}
pub proof fn lemma_fx_to_grid_bound(
m: int,
d: int,
q: int,
rem: int,
rounded: int,
neg: bool,
)
requires
0 <= m,
0 < d,
m * crate::fx::FX_ONE as int == q * d + rem,
0 <= rem < d,
rounded == if 2 * rem > d || (2 * rem == d && q % 2 == 1) {
q + 1
} else {
q
},
m * crate::fx::FX_ONE as int <= 405828369621610135552int * d,
ensures
crate::model::abs_int(if neg { -rounded } else { rounded })
<= 406000000000000000000int,
{
let scaled = m * crate::fx::FX_ONE as int;
vstd::arithmetic::mul::lemma_mul_nonnegative(m, crate::fx::FX_ONE as int);
assert(scaled >= 0);
assert(scaled == q * d + rem);
if q < 0 {
assert(q <= -1);
assert(q * d <= -d) by (nonlinear_arith)
requires
q <= -1,
d > 0,
;
assert(scaled < 0);
assert(false);
}
assert(q * d <= scaled);
assert(q * d <= 405828369621610135552int * d);
if q > 405828369621610135552int {
assert(q >= 405828369621610135553int);
assert(q * d >= 405828369621610135553int * d) by (nonlinear_arith)
requires
q >= 405828369621610135553int,
d > 0,
;
assert(q * d > 405828369621610135552int * d) by (nonlinear_arith)
requires
d > 0,
q * d >= 405828369621610135553int * d,
;
assert(false);
}
assert(q <= rounded <= q + 1);
assert(rounded <= 405828369621610135553int);
assert(405828369621610135553int <= 406000000000000000000int) by (compute);
if neg {
assert(crate::model::abs_int(-rounded) == rounded);
} else {
assert(crate::model::abs_int(rounded) == rounded);
}
}
pub open spec fn atan2_inf_target(y: Q, x: Q) -> Rat {
match (y, x) {
(Q::PosInf, Q::PosInf) => crate::round::round_frac(
PI_NUM as int,
4 * PI_DEN as int,
crate::types::Dir::Nearest,
),
(Q::PosInf, Q::NegInf) => crate::round::round_frac(
3 * PI_NUM as int,
4 * PI_DEN as int,
crate::types::Dir::Nearest,
),
(Q::NegInf, Q::PosInf) => crate::round::round_frac(
-(PI_NUM as int),
4 * PI_DEN as int,
crate::types::Dir::Nearest,
),
(Q::NegInf, Q::NegInf) => crate::round::round_frac(
-(3 * PI_NUM as int),
4 * PI_DEN as int,
crate::types::Dir::Nearest,
),
_ => crate::round::round_frac(0, 1, crate::types::Dir::Nearest),
}
}
fn atan2_inf_target_exec(y: Q, x: Q) -> (r: Rat)
requires
y.wf(),
x.wf(),
ensures
r == atan2_inf_target(y, x),
r.wf(),
{
proof {
crate::model::lemma_pow2_126();
crate::model::lemma_pow2_124();
}
match (y, x) {
(Q::PosInf, Q::PosInf) => {
crate::round::round_frac_exec(
PI_NUM as i128,
4 * PI_DEN as i128,
crate::types::Dir::Nearest,
)
},
(Q::PosInf, Q::NegInf) => {
crate::round::round_frac_exec(
3 * PI_NUM as i128,
4 * PI_DEN as i128,
crate::types::Dir::Nearest,
)
},
(Q::NegInf, Q::PosInf) => {
crate::round::round_frac_exec(
-(PI_NUM as i128),
4 * PI_DEN as i128,
crate::types::Dir::Nearest,
)
},
(Q::NegInf, Q::NegInf) => {
crate::round::round_frac_exec(
-(3 * PI_NUM as i128),
4 * PI_DEN as i128,
crate::types::Dir::Nearest,
)
},
_ => crate::round::round_frac_exec(0, 1, crate::types::Dir::Nearest),
}
}
pub proof fn lemma_atan2_inf_target_wf(y: Q, x: Q)
ensures
atan2_inf_target(y, x).wf(),
{
match (y, x) {
(Q::PosInf, Q::PosInf) => {
crate::round::lemma_round_frac_wf(
PI_NUM as int,
4 * PI_DEN as int,
crate::types::Dir::Nearest,
);
},
(Q::PosInf, Q::NegInf) => {
crate::round::lemma_round_frac_wf(
3 * PI_NUM as int,
4 * PI_DEN as int,
crate::types::Dir::Nearest,
);
},
(Q::NegInf, Q::PosInf) => {
crate::round::lemma_round_frac_wf(
-(PI_NUM as int),
4 * PI_DEN as int,
crate::types::Dir::Nearest,
);
},
(Q::NegInf, Q::NegInf) => {
crate::round::lemma_round_frac_wf(
-(3 * PI_NUM as int),
4 * PI_DEN as int,
crate::types::Dir::Nearest,
);
},
_ => {
crate::round::lemma_round_frac_wf(0, 1, crate::types::Dir::Nearest);
},
}
}
pub proof fn lemma_atan2_inf_target_sign(y: Q, x: Q)
requires
y.spec_is_infinite(),
x.spec_is_infinite(),
ensures
y == Q::PosInf ==> atan2_inf_target(y, x).n() > 0,
y == Q::NegInf ==> atan2_inf_target(y, x).n() < 0,
{
match (y, x) {
(Q::PosInf, Q::PosInf) => {
Rat::lemma_from_raw_spec_components(1_811_004_864_519_280_709, 2_305_843_009_213_693_952);
assert(crate::round::round_frac(PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(1_811_004_864_519_280_709, 2_305_843_009_213_693_952)) by (compute);
assert(crate::round::round_frac(PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest).n() > 0) by (compute);
},
(Q::PosInf, Q::NegInf) => {
Rat::lemma_from_raw_spec_components(339_563_412_097_365_133, 144_115_188_075_855_872);
assert(crate::round::round_frac(3 * PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(339_563_412_097_365_133, 144_115_188_075_855_872)) by (compute);
assert(crate::round::round_frac(3 * PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest).n() > 0) by (compute);
},
(Q::NegInf, Q::PosInf) => {
Rat::lemma_from_raw_spec_components(-1_811_004_864_519_280_709i64, 2_305_843_009_213_693_952);
assert(crate::round::round_frac(-(PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(-1_811_004_864_519_280_709i64, 2_305_843_009_213_693_952)) by (compute);
assert(crate::round::round_frac(-(PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest).n() < 0) by (compute);
},
(Q::NegInf, Q::NegInf) => {
Rat::lemma_from_raw_spec_components(-339_563_412_097_365_133i64, 144_115_188_075_855_872);
assert(crate::round::round_frac(-(3 * PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(-339_563_412_097_365_133i64, 144_115_188_075_855_872)) by (compute);
assert(crate::round::round_frac(-(3 * PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest).n() < 0) by (compute);
},
_ => assert(false),
}
}
pub proof fn lemma_atan2_inf_target_quadrant(y: Q, x: Q)
requires
y.spec_is_infinite(),
x.spec_is_infinite(),
ensures
y == Q::PosInf && x == Q::PosInf ==> crate::model::q_lt_frac(atan2_inf_target(y, x), PI_NUM as int, 2 * PI_DEN as int),
y == Q::PosInf && x == Q::NegInf ==> crate::model::q_lt_frac(atan2_inf_target(y, x), PI_NUM as int, PI_DEN as int)
&& crate::model::q_ge_frac(atan2_inf_target(y, x), PI_NUM as int, 2 * PI_DEN as int),
y == Q::NegInf && x == Q::PosInf ==> crate::model::q_ge_frac(atan2_inf_target(y, x), -(PI_NUM as int), 2 * PI_DEN as int),
y == Q::NegInf && x == Q::NegInf ==> crate::model::q_lt_frac(atan2_inf_target(y, x), -(PI_NUM as int), 2 * PI_DEN as int)
&& crate::model::q_ge_frac(atan2_inf_target(y, x), -(PI_NUM as int), PI_DEN as int),
{
match (y, x) {
(Q::PosInf, Q::PosInf) => {
Rat::lemma_from_raw_spec_components(1_811_004_864_519_280_709, 2_305_843_009_213_693_952);
assert(crate::round::round_frac(PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(1_811_004_864_519_280_709, 2_305_843_009_213_693_952)) by (compute);
assert(crate::model::q_lt_frac(
crate::round::round_frac(PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest), PI_NUM as int, 2 * PI_DEN as int)) by (compute);
},
(Q::PosInf, Q::NegInf) => {
Rat::lemma_from_raw_spec_components(339_563_412_097_365_133, 144_115_188_075_855_872);
assert(crate::round::round_frac(3 * PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(339_563_412_097_365_133, 144_115_188_075_855_872)) by (compute);
assert(crate::model::q_lt_frac(crate::round::round_frac(3 * PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest), PI_NUM as int, PI_DEN as int)) by (compute);
assert(crate::model::q_ge_frac(crate::round::round_frac(3 * PI_NUM as int, 4 * PI_DEN as int, crate::types::Dir::Nearest), PI_NUM as int, 2 * PI_DEN as int)) by (compute);
},
(Q::NegInf, Q::PosInf) => {
Rat::lemma_from_raw_spec_components(-1_811_004_864_519_280_709i64, 2_305_843_009_213_693_952);
assert(crate::round::round_frac(-(PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(-1_811_004_864_519_280_709i64, 2_305_843_009_213_693_952)) by (compute);
assert(crate::model::q_ge_frac(
crate::round::round_frac(-(PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest), -(PI_NUM as int), 2 * PI_DEN as int)) by (compute);
},
(Q::NegInf, Q::NegInf) => {
Rat::lemma_from_raw_spec_components(-339_563_412_097_365_133i64, 144_115_188_075_855_872);
assert(crate::round::round_frac(-(3 * PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest)
== Rat::from_raw_spec(-339_563_412_097_365_133i64, 144_115_188_075_855_872)) by (compute);
assert(crate::model::q_lt_frac(crate::round::round_frac(-(3 * PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest), -(PI_NUM as int), 2 * PI_DEN as int)) by (compute);
assert(crate::model::q_ge_frac(crate::round::round_frac(-(3 * PI_NUM as int), 4 * PI_DEN as int, crate::types::Dir::Nearest), -(PI_NUM as int), PI_DEN as int)) by (compute);
},
_ => assert(false),
}
}
#[allow(unused_variables)]
fn fx_to_grid(x: Rat) -> (r: i128)
requires
x.wf(),
crate::model::abs_int(x.n()) <= (EXP_ARG_LIMIT as int) * x.d(),
ensures
crate::model::abs_int(r as int) <= 406000000000000000000i128 as int,
{
proof {
crate::model::lemma_max_mag_pow2();
crate::model::lemma_pow2_125();
crate::model::lemma_pow2_63();
assert(crate::model::abs_int((x.n()) * (crate::fx::FX_ONE as int)) <= 405828369621610135552
* (x.d())) by (nonlinear_arith)
requires
crate::model::abs_int(x.n()) <= (EXP_ARG_LIMIT as int) * x.d(),
x.d() > 0,
(crate::fx::FX_ONE as int) == 9223372036854775808,
(EXP_ARG_LIMIT as int) == 44,
;
}
let raw: i128 = x.numerator() as i128;
let d: i128 = x.denominator() as i128;
let neg: bool = raw < 0;
let m: i128 = if neg {
0 - raw
} else {
raw
};
let scaled: i128 = m * crate::fx::FX_ONE;
let q: i128 = scaled / d;
let rem: i128 = scaled % d;
proof {
assert(m as int >= 0);
lemma_fx_to_grid_decomposition(m as int, d as int, q as int, rem as int);
assert((q as int) <= 405828369621610135552) by (nonlinear_arith)
requires
(m as int) * (crate::fx::FX_ONE as int)
== (q as int) * (d as int) + (rem as int),
(rem as int) >= 0,
(d as int) >= 1,
(m as int) * (crate::fx::FX_ONE as int)
<= 405828369621610135552 * (d as int),
;
assert((rem as int) * 2 < 9223372036854775808) by (nonlinear_arith)
requires
(rem as int) < (d as int),
(d as int) <= crate::model::max_mag(),
crate::model::max_mag() < 4611686018427387904,
;
}
let twice_rem: i128 = rem * 2;
let round_up: bool = twice_rem > d || (twice_rem == d && q % 2 == 1);
let rounded: i128 = if round_up { q + 1 } else { q };
proof {
assert((rounded as int) == if 2 * (rem as int) > (d as int)
|| (2 * (rem as int) == (d as int) && (q as int) % 2 == 1) {
(q as int) + 1
} else {
q as int
});
lemma_fx_to_grid_nearest(m as int, d as int, q as int, rem as int, rounded as int);
lemma_fx_to_grid_ties_even(m as int, d as int, q as int, rem as int, rounded as int);
lemma_fx_to_grid_bound(
m as int,
d as int,
q as int,
rem as int,
rounded as int,
neg,
);
}
if neg {
0 - rounded
} else {
rounded
}
}
fn fx_quarter(t: i128) -> (r: i64)
requires
crate::model::abs_int(t as int) <= crate::fx::FX_T_MAX as int,
ensures
crate::model::abs_int(r as int) <= crate::model::max_mag(),
{
proof {
crate::model::lemma_max_mag_pow2();
}
let neg: bool = t < 0;
let m: i128 = if neg {
0 - t
} else {
t
};
let q: i128 = (m + 2) / 4;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod((m as int) + 2, 4int);
vstd::arithmetic::div_mod::lemma_mod_bound((m as int) + 2, 4int);
assert((q as int) <= crate::model::max_mag()) by (nonlinear_arith)
requires
((m as int) + 2) == 4 * (q as int) + (((m as int) + 2) % 4),
(((m as int) + 2) % 4) >= 0,
(m as int) <= 14757395258967641292,
crate::model::max_mag() == 4611686018427387903,
;
}
if neg {
(0 - q) as i64
} else {
q as i64
}
}
fn pow2_q(m: i32) -> (r: Q)
ensures
r.wf(),
{
if m >= 62 {
Q::PosSat
} else if m <= -62 {
Q::zero()
} else if m >= 0 {
Q::new(1i64 << (m as u32), 1)
} else {
Q::new(1, 1i64 << ((0 - m) as u32))
}
}
fn fx_small_to_q(v: i128) -> (r: Q)
requires
crate::model::abs_int(v as int) <= 10350000000000000000i128 as int,
ensures
r.wf(),
{
proof {
crate::model::lemma_max_mag_pow2();
}
let neg: bool = v < 0;
let m: i128 = if neg {
0 - v
} else {
v
};
let q: i128 = (m + 2) / 4;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod((m as int) + 2, 4int);
vstd::arithmetic::div_mod::lemma_mod_bound((m as int) + 2, 4int);
assert((q as int) <= 2600000000000000000) by (nonlinear_arith)
requires
((m as int) + 2) == 4 * (q as int) + (((m as int) + 2) % 4),
(((m as int) + 2) % 4) >= 0,
(m as int) <= 10350000000000000000,
;
}
let num: i128 = if neg {
0 - q
} else {
q
};
Q::new(num as i64, 1i64 << 61)
}
impl Q {
pub fn exp(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
self.spec_is_nan() ==> r.spec_is_nan(),
{
match self {
Q::PosSat => Q::PosSat,
Q::NegSat => Q::Nan,
Q::PosInf => Q::PosInf,
Q::NegInf => Q::zero(),
Q::Nan => Q::Nan,
Q::Number(x) => {
proof {
crate::model::lemma_max_mag_pow2();
assert(44int * x.d() <= 202914184810805067732) by (nonlinear_arith)
requires
x.d() <= 4611686018427387903,
;
}
let lim: i128 = (EXP_ARG_LIMIT as i128) * (x.denominator() as i128);
let axn: i128 = if x.numerator() < 0 {
0 - (x.numerator() as i128)
} else {
x.numerator() as i128
};
if axn > lim {
return if x.numerator() > 0 {
Q::PosSat
} else {
Q::zero()
};
}
let xg: i128 = fx_to_grid(x);
let (t, m) = crate::fx::fx_exp_reduced(xg);
if m >= 62 {
let v: i128 = if m == 62 {
(t + 1) / 2
} else {
let pow: i128 = if m == 63 {
1
} else if m == 64 {
2
} else if m == 65 {
4
} else {
8
};
proof {
assert(crate::model::abs_int((t as int) * (pow as int))
<= 8 * (crate::fx::FX_T_MAX as int)) by (nonlinear_arith)
requires
crate::model::abs_int(t as int) <= crate::fx::FX_T_MAX as int,
1 <= pow as int <= 8,
;
}
t * pow
};
let mm: i128 = crate::types::MAX_MAG as i128;
if v > mm {
Q::PosSat
} else if v < 0 - mm {
Q::NegSat
} else {
Q::new(v as i64, 1)
}
} else if m <= -62 {
let q4: i64 = fx_quarter(t);
if m == -62 && q4 > (1i64 << 61) {
Q::new(1, 1i64 << 61)
} else {
Q::zero()
}
} else {
let mant = Q::new(fx_quarter(t), 1i64 << 61);
Q::mul(pow2_q(m), mant)
}
},
}
}
}
const MAX_BINARY_SHIFTS: u32 = 64;
fn atanh_series(z: Q) -> (r: Q)
requires
z.wf(),
ensures
r.wf(),
{
let z2 = Q::mul(z, z);
let mut term = z;
let mut sum = z;
let mut k: u32 = 1;
while k < SERIES_TERMS
invariant
term.wf(),
sum.wf(),
z2.wf(),
1 <= k <= SERIES_TERMS,
decreases SERIES_TERMS - k,
{
term = Q::mul(term, z2);
sum = Q::add(sum, Q::div(term, Q::new((2 * k + 1) as i64, 1)));
k = k + 1;
}
sum
}
pub fn e_series() -> (r: Q)
ensures
r.wf(),
{
let mut term = Q::one();
let mut sum = Q::one();
let mut i: u32 = 1;
while i <= SERIES_TERMS
invariant
term.wf(),
sum.wf(),
1 <= i <= SERIES_TERMS + 1,
decreases SERIES_TERMS + 1 - i,
{
term = Q::div(term, Q::new(i as i64, 1));
sum = Q::add(sum, term);
i = i + 1;
}
sum
}
pub fn e() -> (r: Q)
ensures
r.wf(),
{
Q::new(3133965575612453543, 1152921504606846976)
}
pub fn ln2_series() -> (r: Q)
ensures
r.wf(),
{
Q::mul(Q::new(2, 1), atanh_series(Q::new(1, 3)))
}
pub fn ln2() -> (r: Q)
ensures
r.wf(),
{
Q::new(399572145162582989, 576460752303423488)
}
impl Q {
pub fn ln(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
self.spec_is_nan() ==> r.spec_is_nan(),
{
match self {
Q::PosSat => Q::Nan,
Q::NegSat => Q::Nan,
Q::PosInf => Q::PosInf,
Q::NegInf => Q::Nan,
Q::Nan => Q::Nan,
Q::Number(x) => {
let s = x.signum();
if s < 0 {
return Q::Nan;
}
if s == 0 {
return Q::NegInf;
}
let two = Q::new(2, 1);
let half = Q::new(1, 2);
let mut m = Q::Number(x);
let mut up: u32 = 0;
while up < MAX_BINARY_SHIFTS && Q::gt(m, two)
invariant
m.wf(),
two.wf(),
half.wf(),
up <= MAX_BINARY_SHIFTS,
decreases MAX_BINARY_SHIFTS - up,
{
m = Q::div(m, two);
up = up + 1;
}
let mut down: u32 = 0;
while down < MAX_BINARY_SHIFTS && Q::lt(m, half)
invariant
m.wf(),
two.wf(),
half.wf(),
down <= MAX_BINARY_SHIFTS,
decreases MAX_BINARY_SHIFTS - down,
{
m = Q::mul(m, two);
down = down + 1;
}
let (mn, md): (i64, i64) = match m {
Q::Number(r) => (r.numerator(), r.denominator()),
_ => {
return Q::Nan;
},
};
proof {
crate::model::lemma_max_mag_pow2();
}
let diff: i128 = (mn as i128) - (md as i128);
let adiff: i128 = if diff < 0 {
0 - diff
} else {
diff
};
let near_one: bool = up == 0 && down == 0 && adiff * 256 < (md as i128);
let ln_m = if near_one {
let one = Q::one();
let z = Q::div(Q::sub(m, one), Q::add(m, one));
Q::mul(Q::new(2, 1), atanh_series(z))
} else {
if mn <= 0 {
return Q::Nan;
}
let zg: i128 = crate::fx::fx_ratio_z(mn as i128, md as i128);
Q::mul(Q::new(2, 1), fx_small_to_q(crate::fx::fx_atanh_series(zg)))
};
let k = Q::sub(Q::new(up as i64, 1), Q::new(down as i64, 1));
Q::add(ln_m, Q::mul(k, ln2()))
},
}
}
pub fn pow_i32(self, e: i32) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
if e >= 0 {
self.pow_u32(e as u32)
} else {
let n: i64 = -(e as i64);
self.pow_u32(n as u32).recip()
}
}
}
const ATAN_TERMS: u32 = 30;
const TRIG_ARG_LIMIT: i64 = 1 << 20;
fn atan_series(z: Q) -> (r: Q)
requires
z.wf(),
ensures
r.wf(),
{
let z2 = Q::mul(z, z);
let mut term = z;
let mut sum = z;
let mut k: u32 = 1;
while k < ATAN_TERMS
invariant
term.wf(),
sum.wf(),
z2.wf(),
1 <= k <= ATAN_TERMS,
decreases ATAN_TERMS - k,
{
term = Q::mul(term, z2);
let piece = Q::div(term, Q::new((2 * k + 1) as i64, 1));
sum = if k % 2 == 1 {
Q::sub(sum, piece)
} else {
Q::add(sum, piece)
};
k = k + 1;
}
sum
}
pub fn pi_series() -> (r: Q)
ensures
r.wf(),
{
let a = Q::mul(Q::new(16, 1), atan_series(Q::new(1, 5)));
let b = Q::mul(Q::new(4, 1), atan_series(Q::new(1, 239)));
Q::sub(a, b)
}
pub fn pi() -> (r: Q)
ensures
r.wf(),
{
Q::new(PI_NUM, PI_DEN)
}
pub fn half_pi() -> (r: Q)
ensures
r.wf(),
{
Q::div(pi(), Q::new(2, 1))
}
fn round_to_int(q: Q) -> (r: i64)
requires
q.wf(),
{
match q {
Q::Number(x) => {
let n = x.numerator();
let d = x.denominator();
if n >= 0 {
(n + d / 2) / d
} else {
(n - d / 2) / d
}
},
_ => 0,
}
}
fn sin_series(z: Q) -> (r: Q)
requires
z.wf(),
ensures
r.wf(),
{
let z2 = Q::mul(z, z);
let mut term = z;
let mut sum = z;
let mut k: u32 = 1;
while k < TRIG_TERMS
invariant
term.wf(),
sum.wf(),
z2.wf(),
1 <= k <= TRIG_TERMS,
decreases TRIG_TERMS - k,
{
let kk: i64 = k as i64;
assert(1 <= kk <= TRIG_TERMS as i64);
assert(2 * kk * (2 * kk + 1) <= 40 * 41) by (nonlinear_arith)
requires
1 <= kk <= 11,
;
let d = Q::new(2 * kk * (2 * kk + 1), 1);
term = Q::div(Q::mul(term, z2), d);
sum = if k % 2 == 1 {
Q::sub(sum, term)
} else {
Q::add(sum, term)
};
k = k + 1;
}
sum
}
fn cos_series(z: Q) -> (r: Q)
requires
z.wf(),
ensures
r.wf(),
{
let z2 = Q::mul(z, z);
let mut term = Q::one();
let mut sum = Q::one();
let mut k: u32 = 1;
while k < TRIG_TERMS
invariant
term.wf(),
sum.wf(),
z2.wf(),
1 <= k <= TRIG_TERMS,
decreases TRIG_TERMS - k,
{
let kk: i64 = k as i64;
assert(1 <= kk <= TRIG_TERMS as i64);
assert((2 * kk - 1) * (2 * kk) <= 39 * 40) by (nonlinear_arith)
requires
1 <= kk <= 11,
;
let d = Q::new((2 * kk - 1) * (2 * kk), 1);
term = Q::div(Q::mul(term, z2), d);
sum = if k % 2 == 1 {
Q::sub(sum, term)
} else {
Q::add(sum, term)
};
k = k + 1;
}
sum
}
impl Q {
pub fn atan(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
self.spec_is_nan() ==> r.spec_is_nan(),
{
match self {
Q::Nan => Q::Nan,
Q::PosSat => Q::Nan,
Q::NegSat => Q::Nan,
Q::PosInf => half_pi(),
Q::NegInf => half_pi().neg(),
Q::Number(x) => {
let q = Q::Number(x);
let one = Q::one();
let half = Q::new(1, 2);
let p = pi();
let quarter_pi = Q::div(p, Q::new(4, 1));
let big = Q::gt(q.abs(), one);
let base = if big {
Q::div(one, q)
} else {
q
};
let (core, shift) = if Q::gt(base, half) {
(Q::div(Q::sub(base, one), Q::add(base, one)), quarter_pi)
} else if Q::lt(base, half.neg()) {
(Q::div(Q::add(base, one), Q::sub(one, base)), quarter_pi.neg())
} else {
(base, Q::zero())
};
let inner = Q::add(atan_series(core), shift);
if big {
let hp = Q::div(p, Q::new(2, 1));
if Q::gt(q, Q::zero()) {
Q::sub(hp, inner)
} else {
Q::sub(hp.neg(), inner)
}
} else {
inner
}
},
}
}
pub fn sin(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::sin_cos(self, false)
}
pub fn cos(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::sin_cos(self, true)
}
fn sin_cos(self, want_cos: bool) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
match self {
Q::Number(x) => {
let q = Q::Number(x);
let limit = Q::new(TRIG_ARG_LIMIT, 1);
if Q::gt(q.abs(), limit) {
return Q::Nan;
}
let hp = half_pi();
let n = round_to_int(Q::div(q, hp));
let r = Q::sub(q, Q::mul(Q::new(n, 1), hp));
let m = ((n % 4) + 4) % 4;
let idx = if want_cos {
(m + 1) % 4
} else {
m
};
if idx == 0 {
sin_series(r)
} else if idx == 1 {
cos_series(r)
} else if idx == 2 {
sin_series(r).neg()
} else {
cos_series(r).neg()
}
},
_ => Q::Nan,
}
}
pub fn tan(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::div(self.sin(), self.cos())
}
}
pub fn ln10_series() -> (r: Q)
ensures
r.wf(),
{
Q::new(10, 1).ln()
}
pub fn ln10() -> (r: Q)
ensures
r.wf(),
{
Q::new(2654699869899991811, 1152921504606846976)
}
fn pow2_exponent_i64(n: i64) -> (r: Option<u8>)
ensures
r.is_some() ==> r.unwrap() <= 62 && n == crate::model::pow2(r.unwrap() as nat),
r.is_none() ==> forall|k: nat| k <= 62 ==> n != crate::model::pow2(k),
{
let mut k: u32 = 0;
let mut found: bool = false;
while k <= 62 && !found
invariant
k <= 63,
!found ==> forall|j: nat| j < k ==> n != crate::model::pow2(j),
found ==> k <= 62 && n == crate::model::pow2(k as nat),
decreases 63 - k + (if found { 0int } else { 1int }),
{
if crate::round::pow2_i128(k) == n as i128 {
found = true;
} else {
k = k + 1;
}
}
if found {
Some(k as u8)
} else {
None
}
}
fn pow10_exponent_i64(n: i64) -> (r: Option<u8>)
ensures
r.is_some() ==> r.unwrap() <= 18 && n == crate::model::pow10(r.unwrap() as nat),
r.is_none() ==> forall|k: nat| k <= 18 ==> n != crate::model::pow10(k),
{
let mut k: u8 = 0;
let mut found: bool = false;
while k <= 18 && !found
invariant
k <= 19,
!found ==> forall|j: nat| j < k ==> n != crate::model::pow10(j),
found ==> k <= 18 && n == crate::model::pow10(k as nat),
decreases 19 - k + (if found { 0int } else { 1int }),
{
if crate::q::pow10_i64(k) == n {
found = true;
} else {
k = k + 1;
}
}
if found {
Some(k)
} else {
None
}
}
fn small_int_q(i: i64) -> (r: Q)
requires
-62 <= i <= 62,
ensures
r.wf(),
r.spec_is_value(i as int, 1),
{
match Rat::from_int(i) {
Some(x) => Q::Number(x),
None => {
proof {
assert(false);
}
Q::Nan
},
}
}
impl Q {
pub fn log2(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
forall|k: nat| #![trigger crate::model::pow2(k)]
k <= 62 && self.spec_is_value(crate::model::pow2(k), 1)
==> r.spec_is_value(k as int, 1),
forall|k: nat| #![trigger crate::model::pow2(k)]
k <= 62 && self.spec_is_value(1, crate::model::pow2(k))
==> r.spec_is_value(-(k as int), 1),
{
match self {
Q::Number(x) => {
let n = x.numerator();
let d = x.denominator();
if d == 1 {
if n >= 1 {
match pow2_exponent_i64(n) {
Some(k) => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow2(k2)]
k2 <= 62 && self.spec_is_value(crate::model::pow2(k2), 1)
implies k2 == k as nat by {
crate::model::lemma_pow2_injective(k2, k as nat);
}
assert forall|k2: nat| #![trigger crate::model::pow2(k2)]
k2 <= 62 && self.spec_is_value(1, crate::model::pow2(k2))
implies k2 == 0 && k == 0 by {
crate::model::lemma_pow2_injective(k2, 0);
crate::model::lemma_pow2_injective(k as nat, 0);
}
}
return small_int_q(k as i64);
},
None => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow2(k2)]
k2 <= 62 implies !self.spec_is_value(1, crate::model::pow2(k2)) by {
if self.spec_is_value(1, crate::model::pow2(k2)) {
crate::model::lemma_pow2_injective(k2, 0);
}
}
}
},
}
} else {
proof {
assert forall|k2: nat| #![trigger crate::model::pow2(k2)]
k2 <= 62 implies !self.spec_is_value(crate::model::pow2(k2), 1) by {
crate::model::lemma_pow2_pos(k2);
}
}
}
} else if n == 1 {
match pow2_exponent_i64(d) {
Some(k) => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow2(k2)]
k2 <= 62 && self.spec_is_value(1, crate::model::pow2(k2))
implies k2 == k as nat by {
crate::model::lemma_pow2_injective(k2, k as nat);
}
}
return small_int_q(0 - (k as i64));
},
None => {},
}
}
},
_ => {},
}
Q::div(self.ln(), ln2())
}
pub fn log10(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
forall|k: nat| #![trigger crate::model::pow10(k)]
k <= 18 && self.spec_is_value(crate::model::pow10(k), 1)
==> r.spec_is_value(k as int, 1),
forall|k: nat| #![trigger crate::model::pow10(k)]
k <= 18 && self.spec_is_value(1, crate::model::pow10(k))
==> r.spec_is_value(-(k as int), 1),
{
match self {
Q::Number(x) => {
let n = x.numerator();
let d = x.denominator();
if d == 1 {
if n >= 1 {
match pow10_exponent_i64(n) {
Some(k) => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow10(k2)]
k2 <= 18 && self.spec_is_value(crate::model::pow10(k2), 1)
implies k2 == k as nat by {
crate::model::lemma_pow10_injective(k2, k as nat);
}
assert forall|k2: nat| #![trigger crate::model::pow10(k2)]
k2 <= 18 && self.spec_is_value(1, crate::model::pow10(k2))
implies k2 == 0 && k == 0 by {
crate::model::lemma_pow10_injective(k2, 0);
crate::model::lemma_pow10_injective(k as nat, 0);
}
}
return small_int_q(k as i64);
},
None => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow10(k2)]
k2 <= 18 implies !self.spec_is_value(1, crate::model::pow10(k2)) by {
if self.spec_is_value(1, crate::model::pow10(k2)) {
crate::model::lemma_pow10_injective(k2, 0);
}
}
}
},
}
} else {
proof {
assert forall|k2: nat| #![trigger crate::model::pow10(k2)]
k2 <= 18 implies !self.spec_is_value(crate::model::pow10(k2), 1) by {
crate::model::lemma_pow10_pos(k2);
}
}
}
} else if n == 1 {
match pow10_exponent_i64(d) {
Some(k) => {
proof {
assert forall|k2: nat| #![trigger crate::model::pow10(k2)]
k2 <= 18 && self.spec_is_value(1, crate::model::pow10(k2))
implies k2 == k as nat by {
crate::model::lemma_pow10_injective(k2, k as nat);
}
}
return small_int_q(0 - (k as i64));
},
None => {},
}
}
},
_ => {},
}
Q::div(self.ln(), ln10())
}
pub fn log(self, base: Q) -> (r: Q)
requires
self.wf(),
base.wf(),
ensures
r.wf(),
forall|k: nat| #![trigger crate::model::pow2(k)]
base.spec_is_value(2, 1) && k <= 62 && self.spec_is_value(crate::model::pow2(k), 1)
==> r.spec_is_value(k as int, 1),
forall|k: nat| #![trigger crate::model::pow2(k)]
base.spec_is_value(2, 1) && k <= 62 && self.spec_is_value(1, crate::model::pow2(k))
==> r.spec_is_value(-(k as int), 1),
forall|k: nat| #![trigger crate::model::pow10(k)]
base.spec_is_value(10, 1) && k <= 18 && self.spec_is_value(crate::model::pow10(k), 1)
==> r.spec_is_value(k as int, 1),
forall|k: nat| #![trigger crate::model::pow10(k)]
base.spec_is_value(10, 1) && k <= 18 && self.spec_is_value(1, crate::model::pow10(k))
==> r.spec_is_value(-(k as int), 1),
{
match base {
Q::Number(b) => {
let bn = b.numerator();
let bd = b.denominator();
if bd == 1 && bn == 2 {
return self.log2();
}
if bd == 1 && bn == 10 {
return self.log10();
}
},
_ => {},
}
Q::div(self.ln(), base.ln())
}
pub fn exp2(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::mul(self, ln2()).exp()
}
pub fn powf(self, exponent: Q) -> (r: Q)
requires
self.wf(),
exponent.wf(),
ensures
r.wf(),
exponent.spec_is_zero() ==> r.spec_is_value(1, 1),
self.spec_is_zero() && exponent.spec_signum() == Some(Sign::Positive)
==> r.spec_is_value(0, 1),
self.spec_is_zero() && exponent.spec_signum() == Some(Sign::Negative)
==> r == Q::PosInf,
self.spec_is_zero() && exponent.spec_is_nan() ==> r == Q::Nan,
{
if exponent.is_zero() {
return Q::one();
}
if self.is_zero() {
return match exponent.signum() {
Some(Sign::Positive) => Q::zero(),
Some(Sign::Negative) => Q::PosInf,
Some(Sign::Zero) => Q::one(),
None => Q::Nan,
};
}
Q::mul(exponent, self.ln()).exp()
}
pub fn cbrt(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
self.spec_is_nan() ==> r.spec_is_nan(),
{
match self {
Q::Nan => Q::Nan,
Q::PosInf => Q::PosInf,
Q::NegInf => Q::NegInf,
Q::PosSat => Q::Nan,
Q::NegSat => Q::Nan,
Q::Number(x) => {
let s = x.signum();
if s == 0 {
return Q::zero();
}
let mag = Q::Number(x).abs();
let root = Q::div(mag.ln(), Q::new(3, 1)).exp();
if s < 0 {
root.neg()
} else {
root
}
},
}
}
pub fn hypot(self, other: Q) -> (r: Q)
requires
self.wf(),
other.wf(),
ensures
r.wf(),
{
let a = self.abs();
let b = other.abs();
let (big, small) = if Q::ge(a, b) {
(a, b)
} else {
(b, a)
};
if big.is_zero() {
return Q::zero();
}
let ratio = Q::div(small, big);
Q::mul(big, Q::add(Q::one(), Q::mul(ratio, ratio)).sqrt())
}
pub fn sinh(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
match self {
Q::Nan => Q::Nan,
Q::PosInf => Q::PosInf,
Q::NegInf => Q::NegInf,
Q::PosSat => Q::PosSat,
Q::NegSat => Q::NegSat,
Q::Number(_) => {
let lim = Q::new(HYP_ARG_LIMIT, 1);
if Q::gt(self, lim) {
Q::sub(self, ln2()).exp()
} else if Q::lt(self, lim.neg()) {
Q::sub(self.neg(), ln2()).exp().neg()
} else {
Q::div(Q::sub(self.exp(), self.neg().exp()), Q::new(2, 1))
}
},
}
}
pub fn cosh(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
match self {
Q::Nan => Q::Nan,
Q::PosInf | Q::NegInf => Q::PosInf,
Q::PosSat | Q::NegSat => Q::PosSat,
Q::Number(_) => {
let lim = Q::new(HYP_ARG_LIMIT, 1);
if Q::gt(self.abs(), lim) {
Q::sub(self.abs(), ln2()).exp()
} else {
Q::div(Q::add(self.exp(), self.neg().exp()), Q::new(2, 1))
}
},
}
}
pub fn tanh(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
match self {
Q::Nan => Q::Nan,
Q::PosInf | Q::PosSat => Q::one(),
Q::NegInf | Q::NegSat => Q::one().neg(),
Q::Number(_) => {
let lim = Q::new(HYP_ARG_LIMIT, 1);
if Q::ge(self, lim) {
Q::one()
} else if Q::le(self, lim.neg()) {
Q::one().neg()
} else {
Q::div(self.sinh(), self.cosh())
}
},
}
}
pub fn asin(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
match self {
Q::Number(x) => {
let q = Q::Number(x);
let one = Q::one();
if Q::gt(q.abs(), one) {
return Q::Nan;
}
if q.abs() == one {
let hp = Q::div(pi(), Q::new(2, 1));
return if Q::gt(q, Q::zero()) {
hp
} else {
hp.neg()
};
}
let denom = Q::sub(one, Q::mul(q, q)).sqrt();
Q::div(q, denom).atan()
},
_ => Q::Nan,
}
}
pub fn acos(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::sub(Q::div(pi(), Q::new(2, 1)), self.asin())
}
pub fn atan2(self, x: Q) -> (r: Q)
requires
self.wf(),
x.wf(),
ensures
r.wf(),
self.spec_is_infinite() && x.spec_is_infinite()
==> r == Q::Number(atan2_inf_target(self, x)),
{
let y = self;
let zero = Q::zero();
let p = pi();
let hp = Q::div(p, Q::new(2, 1));
if y.is_nan() || x.is_nan() {
return Q::Nan;
}
if y.is_infinite() && x.is_infinite() {
return Q::Number(atan2_inf_target_exec(y, x));
}
if x.is_zero() && y.is_zero() {
return Q::Nan;
}
if x.is_zero() {
return if Q::gt(y, zero) {
hp
} else {
hp.neg()
};
}
let base = Q::div(y, x).atan();
if Q::gt(x, zero) {
base
} else if Q::ge(y, zero) {
Q::add(base, p)
} else {
Q::sub(base, p)
}
}
}
}
#[cfg(test)]
mod tests {
use super::fx_to_grid;
use crate::Rat;
#[test]
fn fx_to_grid_rounds_nearest_ties_even() {
assert_eq!(fx_to_grid(Rat::zero()), 0);
assert_eq!(fx_to_grid(Rat::one()), 9_223_372_036_854_775_808);
assert_eq!(fx_to_grid(Rat::neg_one()), -9_223_372_036_854_775_808);
assert_eq!(
fx_to_grid(Rat::new(1, 2).unwrap()),
4_611_686_018_427_387_904
);
assert_eq!(
fx_to_grid(Rat::new(1, 3).unwrap()),
3_074_457_345_618_258_603
);
assert_eq!(
fx_to_grid(Rat::new(-1, 3).unwrap()),
-3_074_457_345_618_258_603
);
}
}