use crate::test_util::common::rug_float_significant_bits;
use core::cmp::Ordering::{self, *};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_q::Rational;
use rug::float::Round;
use rug::ops::AssignRound;
pub fn rug_log_base_power_of_2_prec_round(
x: &rug::Float,
pow: i64,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
let target_prec = u32::exact_from(prec);
let mut working_prec = (prec << 1) + 128 + (rug_float_significant_bits(x) << 1);
loop {
let wp = u32::exact_from(working_prec);
let mut t_lo = rug::Float::with_val(wp, 0);
t_lo.assign_round(x.log2_ref(), Round::Down);
let mut t_hi = rug::Float::with_val(wp, 0);
t_hi.assign_round(x.log2_ref(), Round::Up);
let pow_float = rug::Float::with_val(wp, pow);
let (num_lo, num_hi) = if pow > 0 {
(&t_lo, &t_hi)
} else {
(&t_hi, &t_lo)
};
let mut q_lo = rug::Float::with_val(wp, 0);
q_lo.assign_round(num_lo / &pow_float, Round::Down);
let mut q_hi = rug::Float::with_val(wp, 0);
q_hi.assign_round(num_hi / &pow_float, Round::Up);
let mut l_lo = rug::Float::with_val(target_prec, 0);
let mut o_lo = l_lo.assign_round(&q_lo, rm);
let mut l_hi = rug::Float::with_val(target_prec, 0);
let mut o_hi = l_hi.assign_round(&q_hi, rm);
if l_lo.is_nan() && l_hi.is_nan() {
return (l_lo, Equal);
}
if o_lo == Equal {
o_lo = o_hi;
}
if o_hi == Equal {
o_hi = o_lo;
}
if l_lo == l_hi && o_lo == o_hi {
return (l_lo, o_lo);
}
working_prec += working_prec >> 1;
}
}
pub fn rug_log_base_power_of_2_prec(x: &rug::Float, pow: i64, prec: u64) -> (rug::Float, Ordering) {
rug_log_base_power_of_2_prec_round(x, pow, prec, Round::Nearest)
}
pub fn rug_log_base_power_of_2_round(
x: &rug::Float,
pow: i64,
rm: Round,
) -> (rug::Float, Ordering) {
rug_log_base_power_of_2_prec_round(x, pow, rug_float_significant_bits(x), rm)
}
pub fn rug_log_base_power_of_2(x: &rug::Float, pow: i64) -> rug::Float {
rug_log_base_power_of_2_prec_round(x, pow, rug_float_significant_bits(x), Round::Nearest).0
}
pub fn rug_log_base_power_of_2_rational_prec_round(
x: &Rational,
pow: i64,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
let target_prec = u32::exact_from(prec);
let (k, shifted) = if *x > 0 {
let k = x.floor_log_base_2_abs();
(k, x >> k)
} else {
(0, x.clone())
};
let rug_x = rug::Rational::from(&shifted);
let mut working_prec = (prec << 1) + 128 + ((&shifted).significant_bits() << 1);
loop {
let wp = u32::exact_from(working_prec);
let mut lo = rug::Float::with_val(wp, 0);
lo.assign_round(&rug_x, Round::Down);
let mut hi = rug::Float::with_val(wp, 0);
hi.assign_round(&rug_x, Round::Up);
let mut t_lo = rug::Float::with_val(wp, 0);
t_lo.assign_round(lo.log2_ref(), Round::Down);
let mut t_hi = rug::Float::with_val(wp, 0);
t_hi.assign_round(hi.log2_ref(), Round::Up);
let mut s_lo = rug::Float::with_val(wp, 0);
s_lo.assign_round(&t_lo + k, Round::Down);
let mut s_hi = rug::Float::with_val(wp, 0);
s_hi.assign_round(&t_hi + k, Round::Up);
let pow_float = rug::Float::with_val(wp, pow);
let (num_lo, num_hi) = if pow > 0 {
(&s_lo, &s_hi)
} else {
(&s_hi, &s_lo)
};
let mut q_lo = rug::Float::with_val(wp, 0);
q_lo.assign_round(num_lo / &pow_float, Round::Down);
let mut q_hi = rug::Float::with_val(wp, 0);
q_hi.assign_round(num_hi / &pow_float, Round::Up);
let mut l_lo = rug::Float::with_val(target_prec, 0);
let mut o_lo = l_lo.assign_round(&q_lo, rm);
let mut l_hi = rug::Float::with_val(target_prec, 0);
let mut o_hi = l_hi.assign_round(&q_hi, rm);
if l_lo.is_nan() && l_hi.is_nan() {
return (l_lo, Equal);
}
if o_lo == Equal {
o_lo = o_hi;
}
if o_hi == Equal {
o_hi = o_lo;
}
if l_lo == l_hi && o_lo == o_hi {
return (l_lo, o_lo);
}
working_prec += working_prec >> 1;
}
}
pub fn rug_log_base_power_of_2_rational_prec(
x: &Rational,
pow: i64,
prec: u64,
) -> (rug::Float, Ordering) {
rug_log_base_power_of_2_rational_prec_round(x, pow, prec, Round::Nearest)
}