use gmp_mpfr_sys::mpfr;
use rug::Float;
use crate::rfloat::RFloat;
use crate::util::{mpfr_flags, MPFRFlags};
#[derive(Clone, Debug)]
pub struct MPFRResult {
num: RFloat,
prec: usize,
flags: MPFRFlags,
}
impl MPFRResult {
pub fn new(val: Float, t: i32, flags: MPFRFlags, prec: usize) -> Self {
let num = RFloat::from(val).with_ternary(t);
Self { num, prec, flags }
}
pub fn num(&self) -> &RFloat {
&self.num
}
pub fn prec(&self) -> usize {
self.prec
}
pub fn flags(&self) -> &MPFRFlags {
&self.flags
}
}
impl RFloat {
pub(crate) fn with_ternary(mut self, t: i32) -> Self {
if let RFloat::Real(_, exp, c) = &mut self {
if !c.is_zero() {
*c <<= 1;
*exp -= 1;
if t != 0 {
*c += 1;
}
}
}
self
}
}
macro_rules! mpfr_1ary {
($name:ident, $mpfr:ident, $cname:expr) => {
#[doc = "Computes `"]
#[doc = $cname]
#[doc = "` to `p` binary digits of precision, rounding to odd."]
pub fn $name(src: RFloat, p: usize) -> MPFRResult {
assert!(
p as i64 > mpfr::PREC_MIN && p as i64 <= mpfr::PREC_MAX,
"precision must be between {} and {}",
mpfr::PREC_MIN + 1,
mpfr::PREC_MAX
);
let mut dst = Float::new((p - 1) as u32);
let src = Float::from(src);
let (t, flags) = unsafe {
mpfr::clear_flags();
let t = mpfr::$mpfr(dst.as_raw_mut(), src.as_raw(), mpfr::rnd_t::RNDZ);
(t, mpfr_flags())
};
MPFRResult::new(dst, t, flags, p)
}
};
}
macro_rules! mpfr_2ary {
($name:ident, $mpfr:ident, $cname:expr) => {
#[doc = "Computes `"]
#[doc = $cname]
#[doc = "` to `p` binary digits of precision, rounding to odd."]
pub fn $name(src1: RFloat, src2: RFloat, p: usize) -> MPFRResult {
assert!(
p as i64 > mpfr::PREC_MIN && p as i64 <= mpfr::PREC_MAX,
"precision must be between {} and {}",
mpfr::PREC_MIN + 1,
mpfr::PREC_MAX
);
let mut dst = Float::new((p - 1) as u32);
let src1 = Float::from(src1);
let src2 = Float::from(src2);
let (t, flags) = unsafe {
mpfr::clear_flags();
let t = mpfr::$mpfr(
dst.as_raw_mut(),
src1.as_raw(),
src2.as_raw(),
mpfr::rnd_t::RNDZ,
);
(t, mpfr_flags())
};
MPFRResult::new(dst, t, flags, p)
}
};
}
macro_rules! mpfr_3ary {
($name:ident, $mpfr:ident, $cname:expr) => {
#[doc = "Computes `"]
#[doc = $cname]
#[doc = "` to `p` binary digits of precision, rounding to odd."]
pub fn $name(src1: RFloat, src2: RFloat, src3: RFloat, p: usize) -> MPFRResult {
assert!(
p as i64 > mpfr::PREC_MIN && p as i64 <= mpfr::PREC_MAX,
"precision must be between {} and {}",
mpfr::PREC_MIN + 1,
mpfr::PREC_MAX
);
let mut dst = Float::new((p - 1) as u32);
let src1 = Float::from(src1);
let src2 = Float::from(src2);
let src3 = Float::from(src3);
let (t, flags) = unsafe {
mpfr::clear_flags();
let t = mpfr::$mpfr(
dst.as_raw_mut(),
src1.as_raw(),
src2.as_raw(),
src3.as_raw(),
mpfr::rnd_t::RNDZ,
);
(t, mpfr_flags())
};
MPFRResult::new(dst, t, flags, p)
}
};
}
mpfr_1ary!(mpfr_neg, neg, "(- x)");
mpfr_1ary!(mpfr_abs, abs, "|x|");
mpfr_1ary!(mpfr_sqrt, sqrt, "sqrt(x)");
mpfr_1ary!(mpfr_cbrt, cbrt, "cbrt(x)");
mpfr_1ary!(mpfr_recip_sqrt, rec_sqrt, "1/sqrt(x)");
mpfr_1ary!(mpfr_exp, exp, "exp(x)");
mpfr_1ary!(mpfr_exp2, exp2, "2^x");
mpfr_1ary!(mpfr_exp10, exp10, "exp10(x)");
mpfr_1ary!(mpfr_log, log, "ln(x)");
mpfr_1ary!(mpfr_log2, log2, "log2(x)");
mpfr_1ary!(mpfr_log10, log10, "log10(x)");
mpfr_1ary!(mpfr_expm1, expm1, "e^x - 1");
mpfr_1ary!(mpfr_exp2m1, exp2m1, "2^x - 1");
mpfr_1ary!(mpfr_exp10m1, exp10m1, "10^x - 1");
mpfr_1ary!(mpfr_log1p, log1p, "ln(x + 1)");
mpfr_1ary!(mpfr_log2p1, log2p1, "log2(x + 1)");
mpfr_1ary!(mpfr_log10p1, log10p1, "log10(x + 1)");
mpfr_1ary!(mpfr_sin, sin, "sin(x)");
mpfr_1ary!(mpfr_cos, cos, "cos(x)");
mpfr_1ary!(mpfr_tan, tan, "tan(x)");
mpfr_1ary!(mpfr_sin_pi, sinpi, "sin(pi * x)");
mpfr_1ary!(mpfr_cos_pi, cospi, "cos(pi * x)");
mpfr_1ary!(mpfr_tan_pi, tanpi, "tan(pi * x)");
mpfr_1ary!(mpfr_asin, asin, "arcsin(x)");
mpfr_1ary!(mpfr_acos, acos, "arccos(x)");
mpfr_1ary!(mpfr_atan, atan, "arctan(x)");
mpfr_1ary!(mpfr_sinh, sinh, "sinh(x)");
mpfr_1ary!(mpfr_cosh, cosh, "cosh(x)");
mpfr_1ary!(mpfr_tanh, tanh, "tanh(x)");
mpfr_1ary!(mpfr_asinh, asinh, "arsinh(x)");
mpfr_1ary!(mpfr_acosh, acosh, "arcosh(x)");
mpfr_1ary!(mpfr_atanh, atanh, "artanh(x)");
mpfr_1ary!(mpfr_erf, erf, "erf(x)");
mpfr_1ary!(mpfr_erfc, erfc, "erfc(x)");
mpfr_1ary!(mpfr_tgamma, gamma, "tgamma(x)");
mpfr_1ary!(mpfr_lgamma, lngamma, "lgamma(x)");
mpfr_2ary!(mpfr_add, add, "x + y");
mpfr_2ary!(mpfr_sub, sub, "x - y");
mpfr_2ary!(mpfr_mul, mul, "x * y");
mpfr_2ary!(mpfr_div, div, "x / y");
mpfr_2ary!(mpfr_pow, pow, "x ^ y");
mpfr_2ary!(mpfr_hypot, hypot, "sqrt(x^2 + y^2)");
mpfr_2ary!(mpfr_fmod, fmod, "fmod(x, y)");
mpfr_2ary!(mpfr_remainder, remainder, "remainder(x, y)");
mpfr_2ary!(mpfr_atan2, atan2, "arctan(y / x)");
mpfr_3ary!(mpfr_fma, fma, "a * b + c");
pub fn mpfr_recip(src: RFloat, p: usize) -> MPFRResult {
assert!(
p as i64 > mpfr::PREC_MIN && p as i64 <= mpfr::PREC_MAX,
"precision must be between {} and {}",
mpfr::PREC_MIN + 1,
mpfr::PREC_MAX
);
let mut dst = Float::new((p - 1) as u32);
let src = Float::from(src);
let (t, flags) = unsafe {
mpfr::clear_flags();
let t = mpfr::ui_div(dst.as_raw_mut(), 1, src.as_raw(), mpfr::rnd_t::RNDZ);
(t, mpfr_flags())
};
MPFRResult {
num: RFloat::from(dst).with_ternary(t),
prec: p,
flags,
}
}