use crate::Float;
use core::cmp::Ordering;
use malachite_base::num::arithmetic::traits::{
AddMul, CeilingLogBase2, DivRound, MulAddMul, Pow, Square,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::rounding_modes::RoundingMode::{self, *};
use malachite_nz::natural::Natural;
use malachite_nz::natural::arithmetic::float::round::float_can_round;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
struct SplitState {
p: Natural,
q: Natural,
t: Natural,
c: Natural,
d: Natural,
v: Natural,
}
fn s1(n1: u64, n2: u64, n_squared: &Natural, cont: bool) -> SplitState {
if n2 - n1 == 1 {
let d = Natural::from(n1 + 1);
let q = (&d).square();
SplitState {
p: n_squared.clone(),
q,
t: n_squared.clone(),
c: Natural::ONE,
d,
v: n_squared.clone(),
}
} else {
let m = (n1 + n2) >> 1;
let l = s1(n1, m, n_squared, true);
let r = s1(m, n2, n_squared, true);
let t = &l.p * r.t;
SplitState {
t: (&t).add_mul(&r.q, &l.t),
c: if cont {
(r.c * &l.d).add_mul(&l.c, &r.d)
} else {
Natural::ZERO
},
v: (&r.q * l.v)
.add_mul(t, l.c)
.mul_add_mul(&r.d, &l.p * r.v, &l.d),
p: if cont { l.p * r.p } else { Natural::ZERO },
q: l.q * r.q,
d: l.d * r.d,
}
}
}
fn s2(
n1: u64,
n2: u64,
big_n: u64,
n_squared: &Natural,
cont: bool,
) -> (Natural, Natural, Natural) {
if n2 - n1 == 1 {
if n1 == 0 {
(Natural::ONE, Natural::from(big_n) << 2u64, Natural::ONE)
} else {
let p = Natural::from((n1 << 1) - 1).pow(3);
let q = (Natural::from(n1) * n_squared) << 5u64;
(p.clone(), q, p)
}
} else {
let m = (n1 + n2) >> 1;
let (p, q, t) = s2(n1, m, big_n, n_squared, true);
let (p2, q2, t2) = s2(m, n2, big_n, n_squared, true);
let big_t = (t * &q2).add_mul(t2, &p);
(if cont { p * p2 } else { Natural::ZERO }, q * q2, big_t)
}
}
impl Float {
pub fn eulers_constant_prec_round(prec: u64, rm: RoundingMode) -> (Self, Ordering) {
let mut wp = prec + prec.ceiling_log_base_2() + 5;
let mut increment = Limb::WIDTH;
loop {
let n = u64::exact_from(
((u128::from(wp) + 5) * 866434)
.div_round(10000000, Ceiling)
.0,
);
let big_n =
u64::exact_from((u128::from(n) * 4970626).div_round(1000000, Ceiling).0) + 1;
let n_squared = Natural::from(n).square();
let sum = s1(0, big_n, &n_squared, false);
let t = sum.t + &sum.q;
let s_over_i = (sum.v << wp) / (&t * sum.d);
let (_, q2, t2) = s2(0, n << 1, n, &n_squared, false);
let u_over_i_squared = ((sum.q.square() * t2) << wp) / (t.square() * q2);
let v = s_over_i - u_over_i_squared;
let magn = n.ceiling_log_base_2();
let y = -(Self::ln_unsigned_prec_round(n, wp + magn, Down)
.0
.sub_rational_prec_round(Rational::from(v) >> wp, wp + magn, Down)
.0);
if float_can_round(y.significand_ref().unwrap(), wp - 3, prec, rm) {
return Self::from_float_prec_round(y, prec, rm);
}
wp += increment;
increment = wp >> 1;
}
}
#[inline]
pub fn eulers_constant_prec(prec: u64) -> (Self, Ordering) {
Self::eulers_constant_prec_round(prec, Nearest)
}
}