use astro_float_num::{
BigFloat, Consts, Exponent, Radix, RoundingMode, Sign, Word, WORD_BIT_SIZE,
WORD_SIGNIFICANT_BIT,
};
use gmp_mpfr_sys::mpfr::{self, rnd_t};
use rand::random;
use rug::Float;
macro_rules! test_astro_op {
($eq:literal, $n1:ident, $n2:ident, $astro_op:ident, $f1:ident, $f2:ident, $mpfr_op:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n3 = BigFloat::$astro_op(&($n1), &($n2), $p, $rm, &mut $cc);
let mut f3 = Float::with_val($p as u32, 1);
unsafe { mpfr::$mpfr_op(f3.as_raw_mut(), ($f1).as_raw(), ($f2).as_raw(), $rnd) };
assert_float_close(n3, f3, $p, &format!("{:?}", $op_info), $eq, &mut $cc);
};
($eq:literal, $n1:ident, $astro_op:ident, $f1:ident, $mpfr_op:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n3 = BigFloat::$astro_op(&($n1), $p, $rm, &mut $cc);
let mut f3 = Float::with_val($p as u32, 1);
unsafe { mpfr::$mpfr_op(f3.as_raw_mut(), ($f1).as_raw(), $rnd) };
assert_float_close(n3, f3, $p, &format!("{:?}", $op_info), $eq, &mut $cc);
};
($eq:literal, $n1:ident, $astro_op:ident, $f1:ident, $mpfr_op:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n3 = BigFloat::$astro_op(&($n1), $p, $rm, &mut $cc);
let mut f3 = Float::with_val($p as u32, 1);
unsafe { mpfr::$mpfr_op(f3.as_raw_mut(), ($f1).as_raw(), $rnd) };
assert_float_close(n3, f3, $p, &format!("{:?}", $op_info), $eq, &mut $cc);
};
}
macro_rules! test_astro_op_no_cc {
($eq:literal, $n1:ident, $n2:ident, $astro_op:ident, $f1:ident, $f2:ident, $mpfr_op:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n3 = BigFloat::$astro_op(&($n1), &($n2), $p, $rm);
let mut f3 = Float::with_val($p as u32, 1);
unsafe { mpfr::$mpfr_op(f3.as_raw_mut(), ($f1).as_raw(), ($f2).as_raw(), $rnd) };
assert_float_close(n3, f3, $p, &format!("{:?}", $op_info), $eq, &mut $cc);
};
($eq:literal, $n1:ident, $astro_op:ident, $f1:ident, $mpfr_op:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n3 = BigFloat::$astro_op(&($n1), $p, $rm);
let mut f3 = Float::with_val($p as u32, 1);
unsafe { mpfr::$mpfr_op(f3.as_raw_mut(), ($f1).as_raw(), $rnd) };
assert_float_close(n3, f3, $p, &format!("{:?}", $op_info), $eq, &mut $cc);
};
}
macro_rules! test_astro_const {
($astro_const:ident, $mpfr_const:ident, $p:ident, $rm:ident, $rnd:ident, $op_info:expr, $cc:ident) => {
let n1: BigFloat = $cc.$astro_const($p, $rm);
let mut f1 = Float::with_val($p as u32, 1);
unsafe {
mpfr::$mpfr_const(f1.as_raw_mut(), $rnd);
}
assert_float_close(n1, f1, $p, &format!("{:?}", $op_info), true, &mut $cc);
};
}
pub(crate) use test_astro_const;
pub(crate) use test_astro_op;
pub(crate) use test_astro_op_no_cc;
pub const fn get_prec_rng() -> usize {
#[cfg(not(debug_assertions))]
{
157
}
#[cfg(debug_assertions)]
{
32
}
}
pub fn get_float_pair(
p: usize,
emin: Exponent,
emax: Exponent,
cc: &mut Consts,
) -> (BigFloat, Float) {
let n = BigFloat::random_normal(p, emin, emax);
let f = conv_to_mpfr(p, &n, cc);
(n, f)
}
pub fn conv_to_mpfr(p: usize, n: &BigFloat, cc: &mut Consts) -> Float {
let s1 = conv_str_to_mpfr_compat(n.format(Radix::Bin, RoundingMode::ToEven, cc).unwrap());
let f = Float::with_val(p as u32, Float::parse_radix(s1, 2).unwrap());
let s2 = conv_str_from_mpfr_compat(f.to_string_radix(2, None));
assert_eq!(
*n,
BigFloat::parse(&s2, Radix::Bin, p, RoundingMode::None, cc)
);
f
}
pub fn assert_float_close(n: BigFloat, f: Float, p: usize, op: &str, eq: bool, cc: &mut Consts) {
if n.is_inf() {
let ovf = unsafe { mpfr::overflow_p() };
assert!(f.is_infinite() || ovf != 0, "{}", op);
if n.is_positive() {
assert!(f.is_sign_positive(), "{}", op);
} else {
assert!(f.is_sign_negative(), "{}", op);
}
} else if n.is_nan() {
assert!(f.is_nan(), "{}", op);
} else if n.is_subnormal() || n.is_zero() {
let unf = unsafe { mpfr::underflow_p() };
if !f.is_zero() {
assert!(unf != 0, "{}", op);
}
} else if eq {
let s1 = f.to_string_radix(2, None);
let s2 = conv_str_from_mpfr_compat(s1);
let n2 = BigFloat::parse(&s2, Radix::Bin, p, RoundingMode::None, cc);
assert_eq!(n, n2, "{}", op);
} else {
let s1 = f.to_string_radix(2, None);
let s2 = conv_str_from_mpfr_compat(s1);
let n2 = BigFloat::parse(&s2, Radix::Bin, p, RoundingMode::None, cc);
assert_eq!(
n.mantissa_max_bit_len(),
n2.mantissa_max_bit_len(),
"{}",
op
);
let d = n.sub(&n2, 1, RoundingMode::None).abs();
let e1 = n.exponent().unwrap() as isize
- (n.mantissa_max_bit_len().unwrap() - n.precision().unwrap()) as isize;
let e2 = d.exponent().unwrap() as isize
- (d.mantissa_max_bit_len().unwrap() - d.precision().unwrap()) as isize;
if !d.is_zero() {
assert!(
e1 - e2 >= n.mantissa_max_bit_len().unwrap() as isize - 1,
"{}",
op
);
}
}
}
pub fn conv_str_to_mpfr_compat(s: String) -> String {
let (sig, exp) = if let Some(pos) = s.find('e') {
s.split_at(pos + 1)
} else {
(s.as_str(), "0")
};
let expn = i64::from_str_radix(exp, 2).unwrap();
sig.to_owned() + &expn.to_string()
}
pub fn conv_str_from_mpfr_compat(s: String) -> String {
if let Some(epos) = s.find('e') {
let (sig, exp) = s.split_at(epos + 1);
let expn = exp.parse::<i64>().unwrap();
if expn < 0 {
sig.to_owned() + "-" + &format!("{:b}", -expn)
} else {
sig.to_owned() + &format!("{:b}", expn)
}
} else {
s
}
}
pub fn get_random_rnd_pair() -> (RoundingMode, rnd_t) {
match random::<u8>() % 5 {
0 => (RoundingMode::ToEven, rnd_t::RNDN),
1 => (RoundingMode::Up, rnd_t::RNDU),
2 => (RoundingMode::Down, rnd_t::RNDD),
3 => (RoundingMode::FromZero, rnd_t::RNDA),
4 => (RoundingMode::ToZero, rnd_t::RNDZ),
_ => unreachable!(),
}
}
pub fn get_oned_zeroed(p: usize, exp_from: Exponent, exp_to: Exponent) -> BigFloat {
let zero_bits = random::<usize>() % (p - 1);
let mut m1 = vec![Word::MAX; p / WORD_BIT_SIZE];
let i = zero_bits / WORD_BIT_SIZE;
m1.iter_mut().take(i).for_each(|v| *v = 0);
m1[i] <<= zero_bits % WORD_BIT_SIZE;
bf_from_mantissa_and_exp_rng(&m1, exp_from, exp_to)
}
pub fn get_oned_sides(p: usize, exp_from: Exponent, exp_to: Exponent) -> BigFloat {
let one_bits = random::<usize>() % (p / 2).min(WORD_BIT_SIZE * 2) + 1;
let mut m1 = vec![0; p / WORD_BIT_SIZE];
let i = one_bits / WORD_BIT_SIZE;
let m1l = m1.len();
m1.iter_mut().take(i + 1).for_each(|v| *v = Word::MAX);
m1[i] >>= one_bits % WORD_BIT_SIZE;
m1.iter_mut().rev().take(i + 1).for_each(|v| *v = Word::MAX);
m1[m1l - i - 1] <<= one_bits % WORD_BIT_SIZE;
bf_from_mantissa_and_exp_rng(&m1, exp_from, exp_to)
}
pub fn get_periodic(p: usize, exp_from: Exponent, exp_to: Exponent) -> BigFloat {
let nbits = 4;
let bits = random::<Word>() % ((1 << nbits) - 1) + 1;
let mut w: Word = bits;
for _ in 1..WORD_BIT_SIZE / nbits {
w <<= nbits;
w |= bits;
}
let mut m1 = vec![w; p / WORD_BIT_SIZE];
*(m1.last_mut().unwrap()) |= 1 << (WORD_BIT_SIZE - 1);
bf_from_mantissa_and_exp_rng(&m1, exp_from, exp_to)
}
pub fn get_last_zero(p: usize, exp_from: Exponent, exp_to: Exponent) -> BigFloat {
let mut m1 = vec![Word::MAX; p / WORD_BIT_SIZE];
m1[0] = Word::MAX - 1;
bf_from_mantissa_and_exp_rng(&m1, exp_from, exp_to)
}
pub fn get_near_one(p: usize) -> BigFloat {
let e = (rand::random::<u8>() & 1) as Exponent;
let random_bits = random::<usize>() % (p - 1);
let i = random_bits / WORD_BIT_SIZE;
let mut m1;
if e == 0 {
m1 = vec![Word::MAX; p / WORD_BIT_SIZE];
} else {
m1 = vec![0; p / WORD_BIT_SIZE];
}
m1[i] ^= random::<Word>() >> (random_bits % WORD_BIT_SIZE);
m1.iter_mut().take(i).for_each(|v| *v = random());
*m1.last_mut().unwrap() |= WORD_SIGNIFICANT_BIT;
bf_from_mantissa_and_exp_rng(&m1, e, e)
}
pub fn bf_from_mantissa_and_exp_rng(m: &[Word], exp_from: Exponent, exp_to: Exponent) -> BigFloat {
let e = if exp_from < exp_to {
(rand::random::<isize>().abs() % (exp_to as isize - exp_from as isize) + exp_from as isize)
as Exponent
} else {
exp_from
};
let s = if rand::random::<u8>() & 1 == 0 { Sign::Pos } else { Sign::Neg };
BigFloat::from_words(m, s, e)
}