use malachite_base::num::arithmetic::traits::Parity;
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::logic::traits::BitAccess;
use malachite_base::num::random::random_primitive_ints;
use malachite_base::random::EXAMPLE_SEED;
use malachite_base::test_util::bench::tune::interleaved_min_pair;
use malachite_nz::integer::Integer;
use malachite_nz::natural::Natural;
use malachite_nz::platform::Limb;
use malachite_q::Rational;
use malachite_q::rational_polynomial::arithmetic::evaluate::{
evaluate_integer_polynomial_divide_and_conquer, evaluate_integer_polynomial_horner,
};
use std::hint::black_box;
fn random_natural(seed: &str, bits: u64) -> Natural {
use malachite_base::num::arithmetic::traits::ModPowerOf2;
let limbs: Vec<Limb> = random_primitive_ints(EXAMPLE_SEED.fork(seed))
.take(usize::try_from(bits.div_ceil(Limb::WIDTH)).unwrap())
.collect();
let mut n = Natural::from_owned_limbs_asc(limbs).mod_power_of_2(bits);
n.set_bit(bits - 1);
n
}
fn random_integer(seed: &str, bits: u64) -> Integer {
let negative = random_primitive_ints::<u8>(EXAMPLE_SEED.fork(&format!("{seed}s")))
.next()
.unwrap()
.odd();
Integer::from_sign_and_abs(!negative, random_natural(seed, bits))
}
fn random_rational(seed: &str, numerator_bits: u64, denominator_bits: u64) -> Rational {
Rational::from_integers(
random_integer(&format!("{seed}n"), numerator_bits),
Integer::from(random_natural(&format!("{seed}d"), denominator_bits)),
)
}
type Inputs = Vec<(Vec<Integer>, Rational)>;
fn inputs(len: usize, coefficient_bits: u64, numerator_bits: u64, denominator_bits: u64) -> Inputs {
(0..4)
.map(|k| {
let cs = (0..len)
.map(|i| random_integer(&format!("c{k}_{i}"), coefficient_bits))
.collect();
(
cs,
random_rational(&format!("x{k}"), numerator_bits, denominator_bits),
)
})
.collect()
}
#[allow(clippy::print_stdout)]
fn tune_evaluate_rational() {
let lens =
[2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048];
for coefficient_bits in [8u64, 64, 256, 2048] {
for (numerator_bits, denominator_bits) in [
(8u64, 8u64),
(64, 64),
(8, 64),
(64, 8),
(256, 256),
(2048, 2048),
(64, 2048),
(2048, 64),
] {
let mut line =
format!("c {coefficient_bits:>5} a {numerator_bits:>5} b {denominator_bits:>5}:");
let mut first_win = None;
let mut wins = 0;
for &len in &lens {
let inputs = inputs(len, coefficient_bits, numerator_bits, denominator_bits);
let (mut i, mut j) = (0usize, 0usize);
let (th, td) = interleaved_min_pair(
&mut || {
let (cs, x) = &inputs[i % 4];
i += 1;
black_box(evaluate_integer_polynomial_horner(black_box(cs), x));
},
&mut || {
let (cs, x) = &inputs[j % 4];
j += 1;
black_box(evaluate_integer_polynomial_divide_and_conquer(
black_box(cs),
x,
));
},
);
let ratio = td / th;
line.push_str(&format!(" {len}:{ratio:.2}"));
if ratio < 1.0 {
wins += 1;
first_win.get_or_insert(len);
} else {
wins = 0;
first_win = None;
}
if wins >= 3 && ratio < 0.8 {
break;
}
if th > 2.0e8 {
break;
}
}
println!(
"{line} => {}",
first_win.map_or("none".to_string(), |l| l.to_string())
);
}
}
}
pub fn tune(key: &str) {
match key {
"evaluate_rational" => tune_evaluate_rational(),
_ => panic!("Invalid tuning key: {key}"),
}
}