use verus_builtin_macros::verus;
#[allow(unused_imports)]
use verus_builtin::*;
#[allow(unused_imports)]
use vstd::prelude::*;
#[allow(unused_imports)]
use crate::model::*;
verus! {
pub const FX_SHIFT: u32 = 63;
pub const FX_ONE: i128 = 9223372036854775808i128;
pub open spec fn fx_one() -> int {
pow2(63)
}
pub proof fn lemma_fx_one()
ensures
FX_ONE as int == fx_one(),
{
crate::model::lemma_pow2_63();
}
pub fn fx_mul(a: i128, b: i128) -> (r: i128)
requires
abs_int((a as int) * (b as int)) < pow2(126),
ensures
2 * abs_int((r as int) * fx_one() - (a as int) * (b as int)) <= fx_one(),
abs_int(r as int) * fx_one() <= abs_int((a as int) * (b as int)) + fx_one(),
{
proof {
crate::model::lemma_pow2_126();
lemma_fx_one();
}
let p: i128 = a * b;
let neg: bool = p < 0;
let m: i128 = if neg {
0 - p
} else {
p
};
let q: i128 = m / FX_ONE;
let rem: i128 = m % FX_ONE;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(m as int, FX_ONE as int);
vstd::arithmetic::div_mod::lemma_div_pos_is_pos(m as int, FX_ONE as int);
vstd::arithmetic::div_mod::lemma_mod_bound(m as int, FX_ONE as int);
}
let qr: i128 = if rem * 2 >= FX_ONE {
q + 1
} else {
q
};
proof {
assert((qr as int) * fx_one() - (m as int) == (qr as int) * fx_one() - ((q as int)
* fx_one() + (rem as int)));
assert(2 * abs_int((qr as int) * fx_one() - (m as int)) <= fx_one()) by (nonlinear_arith)
requires
(m as int) == (q as int) * fx_one() + (rem as int),
(rem as int) >= 0,
(rem as int) < fx_one(),
(rem as int) * 2 >= fx_one() ==> (qr as int) == (q as int) + 1,
(rem as int) * 2 < fx_one() ==> (qr as int) == q as int,
;
assert(abs_int(qr as int) * fx_one() <= (m as int) + fx_one()) by (nonlinear_arith)
requires
(m as int) == (q as int) * fx_one() + (rem as int),
(rem as int) >= 0,
(rem as int) < fx_one(),
q as int >= 0,
fx_one() > 0,
(rem as int) * 2 >= fx_one() ==> (qr as int) == (q as int) + 1,
(rem as int) * 2 < fx_one() ==> (qr as int) == q as int,
;
}
if neg {
0 - qr
} else {
qr
}
}
pub fn fx_div_int(v: i128, k: u32) -> (r: i128)
requires
k > 0,
abs_int(v as int) < pow2(126),
ensures
2 * abs_int((r as int) * (k as int) - (v as int)) <= k as int,
abs_int(r as int) <= abs_int(v as int),
{
proof {
crate::model::lemma_pow2_126();
}
let neg: bool = v < 0;
let m: i128 = if neg {
0 - v
} else {
v
};
let kk: i128 = k as i128;
let q: i128 = m / kk;
let rem: i128 = m % kk;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(m as int, kk as int);
vstd::arithmetic::div_mod::lemma_div_pos_is_pos(m as int, kk as int);
vstd::arithmetic::div_mod::lemma_mod_bound(m as int, kk as int);
}
let qr: i128 = if rem * 2 >= kk {
q + 1
} else {
q
};
proof {
assert((m as int) == (q as int) * (kk as int) + (rem as int)) by (nonlinear_arith)
requires
(m as int) == (kk as int) * (q as int) + (rem as int),
;
assert(2 * abs_int((qr as int) * (kk as int) - (m as int)) <= kk as int)
by (nonlinear_arith)
requires
(m as int) == (q as int) * (kk as int) + (rem as int),
(rem as int) >= 0,
(rem as int) < (kk as int),
(rem as int) * 2 >= kk as int ==> (qr as int) == (q as int) + 1,
(rem as int) * 2 < kk as int ==> (qr as int) == q as int,
;
assert(abs_int(qr as int) <= m as int) by (nonlinear_arith)
requires
(m as int) == (q as int) * (kk as int) + (rem as int),
(rem as int) >= 0,
(rem as int) < (kk as int),
q as int >= 0,
kk as int >= 1,
(rem as int) * 2 >= kk as int ==> (qr as int) == (q as int) + 1,
(rem as int) * 2 < kk as int ==> (qr as int) == q as int,
;
}
let r: i128 = if neg {
0 - qr
} else {
qr
};
proof {
assert((r as int) * (kk as int) - (v as int) == if neg {
-((qr as int) * (kk as int) - (m as int))
} else {
(qr as int) * (kk as int) - (m as int)
}) by (nonlinear_arith)
requires
neg ==> ((r as int) == -(qr as int) && (v as int) == -(m as int)),
!neg ==> ((r as int) == qr as int && (v as int) == m as int),
;
}
r
}
pub const FX_R_MAX: i128 = 3200000000000000000i128;
pub const FX_T_MAX: i128 = 14757395258967641292i128;
pub const FX_EXP_TERMS: u32 = 16;
pub fn fx_exp_series(z: i128) -> (t: i128)
requires
abs_int(z as int) <= FX_R_MAX as int,
ensures
abs_int(t as int) <= FX_T_MAX as int,
{
proof {
crate::model::lemma_pow2_126();
lemma_fx_one();
}
let mut t: i128 = FX_ONE;
let mut k: u32 = FX_EXP_TERMS;
while k >= 1
invariant
abs_int(t as int) <= FX_T_MAX as int,
abs_int(z as int) <= FX_R_MAX as int,
k <= FX_EXP_TERMS,
decreases k,
{
proof {
crate::model::lemma_pow2_126();
lemma_fx_one();
assert(abs_int((z as int) * (t as int)) < pow2(126)) by (nonlinear_arith)
requires
abs_int(z as int) <= FX_R_MAX as int,
abs_int(t as int) <= FX_T_MAX as int,
pow2(126) == 85070591730234615865843651857942052864,
;
}
let p: i128 = fx_mul(z, t);
proof {
lemma_fx_one();
assert(abs_int(p as int) <= 5200000000000000000i128 as int) by (nonlinear_arith)
requires
abs_int(p as int) * fx_one() <= abs_int((z as int) * (t as int)) + fx_one(),
abs_int(z as int) <= FX_R_MAX as int,
abs_int(t as int) <= FX_T_MAX as int,
fx_one() == 9223372036854775808,
;
}
let q: i128 = fx_div_int(p, k);
t = FX_ONE + q;
k = k - 1;
}
t
}
pub const FX_LN2_HI: i128 = 6393154322601327830i128;
pub const FX_LN2_LO: i128 = -974768846722515540i128;
pub fn fx_exp_reduced(x: i128) -> (r: (i128, i32))
requires
abs_int(x as int) <= 406000000000000000000i128 as int,
ensures
abs_int(r.0 as int) <= FX_T_MAX as int,
-66 <= r.1 <= 66,
{
proof {
crate::model::lemma_pow2_126();
lemma_fx_one();
}
let neg: bool = x < 0;
let ax: i128 = if neg {
0 - x
} else {
x
};
let qm: i128 = ax / FX_LN2_HI;
let rm: i128 = ax % FX_LN2_HI;
let am: i128 = if rm * 2 >= FX_LN2_HI {
qm + 1
} else {
qm
};
let m: i128 = if neg {
0 - am
} else {
am
};
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(ax as int, FX_LN2_HI as int);
vstd::arithmetic::div_mod::lemma_div_pos_is_pos(ax as int, FX_LN2_HI as int);
vstd::arithmetic::div_mod::lemma_mod_bound(ax as int, FX_LN2_HI as int);
assert(abs_int(m as int) <= 66) by (nonlinear_arith)
requires
(ax as int) == (FX_LN2_HI as int) * (qm as int) + (rm as int),
(rm as int) >= 0,
(rm as int) < FX_LN2_HI as int,
(ax as int) <= 406000000000000000000,
abs_int(m as int) <= (qm as int) + 1,
(qm as int) >= 0,
;
}
let hi: i128 = x - m * FX_LN2_HI;
let lo: i128 = fx_mul(m, FX_LN2_LO);
let r: i128 = hi - lo;
#[allow(clippy::manual_clamp)]
let rc: i128 = if r > FX_R_MAX {
FX_R_MAX
} else if r < 0 - FX_R_MAX {
0 - FX_R_MAX
} else {
r
};
let t: i128 = fx_exp_series(rc);
proof {
assert(abs_int(m as int) <= 66);
}
(t, m as i32)
}
pub const FX_ATANH_TERMS: u32 = 18;
pub const FX_Z_MAX: i128 = 3150000000000000000i128;
pub fn fx_atanh_series(z: i128) -> (t: i128)
requires
abs_int(z as int) <= FX_Z_MAX as int,
ensures
abs_int(t as int) <= 10350000000000000000i128 as int,
{
proof {
crate::model::lemma_pow2_126();
lemma_fx_one();
assert(abs_int((z as int) * (z as int)) < pow2(126)) by (nonlinear_arith)
requires
abs_int(z as int) <= FX_Z_MAX as int,
pow2(126) == 85070591730234615865843651857942052864,
;
}
let zz: i128 = fx_mul(z, z);
proof {
assert(abs_int(zz as int) <= 1100000000000000000i128 as int) by (nonlinear_arith)
requires
abs_int(zz as int) * fx_one() <= abs_int((z as int) * (z as int)) + fx_one(),
abs_int(z as int) <= FX_Z_MAX as int,
fx_one() == 9223372036854775808,
;
}
let mut term: i128 = z;
let mut sum: i128 = z;
let mut k: u32 = 1;
while k <= FX_ATANH_TERMS
invariant
abs_int(term as int) <= FX_Z_MAX as int,
abs_int(zz as int) <= 1100000000000000000,
abs_int(sum as int) <= (FX_Z_MAX as int) + ((k - 1) as int) * 400000000000000000,
1 <= k <= FX_ATANH_TERMS + 1,
decreases FX_ATANH_TERMS + 1 - k,
{
proof {
lemma_fx_one();
crate::model::lemma_pow2_126();
assert(abs_int((term as int) * (zz as int)) < pow2(126)) by (nonlinear_arith)
requires
abs_int(term as int) <= FX_Z_MAX as int,
abs_int(zz as int) <= 1100000000000000000,
pow2(126) == 85070591730234615865843651857942052864,
;
}
let next: i128 = fx_mul(term, zz);
proof {
lemma_fx_one();
assert(abs_int(next as int) <= 1100000000000000000) by (nonlinear_arith)
requires
abs_int(next as int) * fx_one() <= abs_int((term as int) * (zz as int))
+ fx_one(),
abs_int(term as int) <= FX_Z_MAX as int,
abs_int(zz as int) <= 1100000000000000000,
fx_one() == 9223372036854775808,
;
}
let piece: i128 = fx_div_int(next, 2 * k + 1);
proof {
assert(abs_int(piece as int) <= 400000000000000000) by (nonlinear_arith)
requires
2 * abs_int((piece as int) * ((2 * k + 1) as int) - (next as int)) <= (2 * k
+ 1) as int,
abs_int(next as int) <= 1100000000000000000,
((2 * k + 1) as int) >= 3,
;
}
term = next;
sum = sum + piece;
k = k + 1;
}
sum
}
#[allow(unused_variables)]
pub fn fx_ratio_z(bign: i128, bigd: i128) -> (z: i128)
requires
bign > 0,
bigd > 0,
bign <= 4611686018427387904i128 as int,
bigd <= 4611686018427387904i128 as int,
ensures
abs_int(z as int) <= FX_Z_MAX as int,
{
proof {
lemma_fx_one();
crate::model::lemma_pow2_126();
}
let diff: i128 = bign - bigd;
let sum: i128 = bign + bigd;
let neg: bool = diff < 0;
let ad: i128 = if neg {
0 - diff
} else {
diff
};
proof {
assert((ad as int) * fx_one() <= 42535295865117307932921825928971026432)
by (nonlinear_arith)
requires
(ad as int) <= 4611686018427387904,
fx_one() == 9223372036854775808,
;
}
let scaled: i128 = ad * FX_ONE;
let q: i128 = scaled / sum;
let rem: i128 = scaled % sum;
proof {
vstd::arithmetic::div_mod::lemma_fundamental_div_mod(scaled as int, sum as int);
vstd::arithmetic::div_mod::lemma_div_pos_is_pos(scaled as int, sum as int);
vstd::arithmetic::div_mod::lemma_mod_bound(scaled as int, sum as int);
assert((scaled as int) == (q as int) * (sum as int) + (rem as int)) by (nonlinear_arith)
requires
(scaled as int) == (sum as int) * (q as int) + (rem as int),
;
assert((q as int) >= 0);
}
let qc: i128 = if q > FX_Z_MAX {
FX_Z_MAX
} else {
q
};
if neg {
0 - qc
} else {
qc
}
}
}