use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
use crate::ext::Q;
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;
#[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)) <= 406000000000000000000
* (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 scaled: i128 = (x.num as i128) * crate::fx::FX_ONE;
let d: i128 = x.den as i128;
let neg: bool = scaled < 0;
let m: i128 = if neg {
0 - scaled
} else {
scaled
};
let q: i128 = m / d;
let rem: i128 = m % d;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(m as int, d as int);
vstd::arithmetic::div_mod::lemma_div_pos_is_pos(m as int, d as int);
vstd::arithmetic::div_mod::lemma_mod_bound(m as int, d as int);
assert((m as int) == (q as int) * (d as int) + (rem as int)) by (nonlinear_arith)
requires
(m as int) == (d as int) * (q as int) + (rem as int),
;
assert((q as int) <= 406000000000000000000) by (nonlinear_arith)
requires
(m as int) == (q as int) * (d as int) + (rem as int),
(rem as int) >= 0,
(d as int) >= 1,
(m as int) <= 406000000000000000000 * (d as int),
;
}
if neg {
0 - q
} else {
q
}
}
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.den as i128);
let axn: i128 = if x.num < 0 {
0 - (x.num as i128)
} else {
x.num as i128
};
if axn > lim {
return if x.num > 0 {
Q::PosSat
} else {
Q::zero()
};
}
let xg: i128 = fx_to_grid(x);
let (t, m) = crate::fx::fx_exp_reduced(xg);
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(1811004864519280709, 576460752303423488)
}
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)
}
impl Q {
pub fn log2(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::div(self.ln(), ln2())
}
pub fn log10(self) -> (r: Q)
requires
self.wf(),
ensures
r.wf(),
{
Q::div(self.ln(), ln10())
}
pub fn log(self, base: Q) -> (r: Q)
requires
self.wf(),
base.wf(),
ensures
r.wf(),
{
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(),
{
if exponent.is_zero() {
return Q::one();
}
if self.is_zero() {
return Q::zero();
}
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(),
{
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(),
{
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(),
{
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(),
{
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 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)
}
}
}
}