use crate::test_util::common::rug_float_significant_bits;
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::num::logic::traits::SignificantBits;
use malachite_q::Rational;
use rug::float::{Round, Special};
use rug::ops::AssignRound;
use rug::ops::Pow;
use std::cmp::Ordering::{self, *};
use std::cmp::max;
pub fn rug_pow_prec_round(
x: &rug::Float,
y: &rug::Float,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
let mut power = rug::Float::with_val(u32::exact_from(prec), 0);
let o = power.assign_round(Pow::pow(x, y), rm);
(power, o)
}
#[inline]
pub fn rug_pow_round(x: &rug::Float, y: &rug::Float, rm: Round) -> (rug::Float, Ordering) {
rug_pow_prec_round(
x,
y,
max(rug_float_significant_bits(x), rug_float_significant_bits(y)),
rm,
)
}
#[inline]
pub fn rug_pow_prec(x: &rug::Float, y: &rug::Float, prec: u64) -> (rug::Float, Ordering) {
rug_pow_prec_round(x, y, prec, Round::Nearest)
}
pub fn rug_pow(x: &rug::Float, y: &rug::Float) -> rug::Float {
rug_pow_prec_round(
x,
y,
max(rug_float_significant_bits(x), rug_float_significant_bits(y)),
Round::Nearest,
)
.0
}
pub fn rug_pow_integer_prec_round(
x: &rug::Float,
y: &rug::Integer,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
let mut power = rug::Float::with_val(u32::exact_from(prec), 0);
let o = power.assign_round(Pow::pow(x, y), rm);
(power, o)
}
#[inline]
pub fn rug_pow_integer_round(
x: &rug::Float,
y: &rug::Integer,
rm: Round,
) -> (rug::Float, Ordering) {
rug_pow_integer_prec_round(x, y, rug_float_significant_bits(x), rm)
}
#[inline]
pub fn rug_pow_integer_prec(x: &rug::Float, y: &rug::Integer, prec: u64) -> (rug::Float, Ordering) {
rug_pow_integer_prec_round(x, y, prec, Round::Nearest)
}
pub fn rug_pow_integer(x: &rug::Float, y: &rug::Integer) -> rug::Float {
rug_pow_integer_prec_round(x, y, rug_float_significant_bits(x), Round::Nearest).0
}
pub fn rug_pow_u_prec_round(
x: &rug::Float,
n: u64,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
rug_pow_integer_prec_round(x, &rug::Integer::from(n), prec, rm)
}
#[inline]
pub fn rug_pow_u_round(x: &rug::Float, n: u64, rm: Round) -> (rug::Float, Ordering) {
rug_pow_u_prec_round(x, n, rug_float_significant_bits(x), rm)
}
#[inline]
pub fn rug_pow_u_prec(x: &rug::Float, n: u64, prec: u64) -> (rug::Float, Ordering) {
rug_pow_u_prec_round(x, n, prec, Round::Nearest)
}
pub fn rug_pow_u(x: &rug::Float, n: u64) -> rug::Float {
rug_pow_u_prec_round(x, n, rug_float_significant_bits(x), Round::Nearest).0
}
pub fn rug_pow_s_prec_round(
x: &rug::Float,
n: i64,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
rug_pow_integer_prec_round(x, &rug::Integer::from(n), prec, rm)
}
#[inline]
pub fn rug_pow_s_round(x: &rug::Float, n: i64, rm: Round) -> (rug::Float, Ordering) {
rug_pow_s_prec_round(x, n, rug_float_significant_bits(x), rm)
}
#[inline]
pub fn rug_pow_s_prec(x: &rug::Float, n: i64, prec: u64) -> (rug::Float, Ordering) {
rug_pow_s_prec_round(x, n, prec, Round::Nearest)
}
pub fn rug_pow_s(x: &rug::Float, n: i64) -> rug::Float {
rug_pow_s_prec_round(x, n, rug_float_significant_bits(x), Round::Nearest).0
}
pub fn rug_unsigned_pow_rational_prec_round(
k: u64,
q: &Rational,
prec: u64,
rm: Round,
) -> (rug::Float, Ordering) {
let rug_q = rug::Rational::from(q);
let q_positive = *q > 0u32;
let target_prec = u32::exact_from(prec);
let mut max_finite = rug::Float::with_val(target_prec, Special::Infinity);
max_finite.next_down();
let mut min_pos = rug::Float::with_val(target_prec, 0);
min_pos.next_up();
let mut working_prec = (prec << 1) + 128 + (q.significant_bits() << 1);
loop {
let wp = u32::exact_from(working_prec);
let k_float = rug::Float::with_val(wp, k);
let mut b_lo = rug::Float::with_val(wp, 0);
b_lo.assign_round(k_float.ln_ref(), Round::Down);
let mut b_hi = rug::Float::with_val(wp, 0);
b_hi.assign_round(k_float.ln_ref(), Round::Up);
let mut t_lo = rug::Float::with_val(wp, 0);
t_lo.assign_round(if q_positive { &b_lo } else { &b_hi } * &rug_q, Round::Down);
let mut t_hi = rug::Float::with_val(wp, 0);
t_hi.assign_round(if q_positive { &b_hi } else { &b_lo } * &rug_q, Round::Up);
let mut v_lo = rug::Float::with_val(wp, 0);
v_lo.assign_round(t_lo.exp_ref(), Round::Down);
let mut v_hi = rug::Float::with_val(wp, 0);
v_hi.assign_round(t_hi.exp_ref(), Round::Up);
let mut p_lo = rug::Float::with_val(target_prec, 0);
let mut o_lo = p_lo.assign_round(&v_lo, rm);
if v_hi.is_infinite() {
if p_lo.is_infinite() {
return (p_lo, Greater);
}
if p_lo == max_finite {
return (p_lo, Less);
}
} else if v_lo.is_zero() && v_hi <= min_pos {
match rm {
Round::Zero | Round::Down => {
return (rug::Float::with_val(target_prec, 0), Less);
}
Round::Up | Round::AwayZero => {
return (min_pos.clone(), Greater);
}
Round::Nearest => {
const TIE_EXPONENT: i32 = -(1 << 30) - 1;
let two = rug::Float::with_val(wp, 2u32);
let mut c_lo = rug::Float::with_val(wp, 0);
c_lo.assign_round(two.ln_ref(), Round::Up);
c_lo *= TIE_EXPONENT;
let mut c_hi = rug::Float::with_val(wp, 0);
c_hi.assign_round(two.ln_ref(), Round::Down);
c_hi *= TIE_EXPONENT;
if t_hi < c_lo {
return (rug::Float::with_val(target_prec, 0), Less);
}
if t_lo > c_hi {
return (min_pos.clone(), Greater);
}
}
_ => unreachable!(),
}
} else {
let mut p_hi = rug::Float::with_val(target_prec, 0);
let mut o_hi = p_hi.assign_round(&v_hi, rm);
if o_lo == Equal {
o_lo = o_hi;
}
if o_hi == Equal {
o_hi = o_lo;
}
if p_lo == p_hi && o_lo == o_hi {
return (p_lo, o_lo);
}
}
working_prec += working_prec >> 1;
}
}