#![allow(
clippy::float_cmp,
clippy::incompatible_msrv,
clippy::unreadable_literal,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::cast_lossless,
clippy::unwrap_used
)]
#![cfg(feature = "stage-counters")]
mod support;
use std::sync::{Mutex, MutexGuard, PoisonError};
use arithmetica::counters::{Counts, Predicate, reset, snapshot};
use support::{
Pred, SplitMix64, call, cocircular, collinear, config_span, coplanar, cospherical, oracle,
};
use arithmetica::Sign;
const fn seed(number: u64) -> u64 {
0x2026_1010_D1D1_u64.wrapping_add(number)
}
fn power_of_two(exponent: i32) -> f64 {
assert!((-1022..=1023).contains(&exponent));
f64::from_bits(((exponent + 1023) as u64) << 52)
}
fn in_window_shifts(pred: Pred, config: &[f64]) -> (i32, i32) {
let (q_min, e_max) = pred.window();
let (highest, lowest) = config_span(config).expect("the fixtures are not all zero");
(q_min - lowest, e_max - highest)
}
fn scaled(config: &[f64], factor: f64) -> Vec<f64> {
assert_eq!(factor.to_bits() & 0x000F_FFFF_FFFF_FFFF, 0);
config.iter().map(|&c| c * factor).collect()
}
static SERIAL: Mutex<()> = Mutex::new(());
fn serialize() -> MutexGuard<'static, ()> {
SERIAL.lock().unwrap_or_else(PoisonError::into_inner)
}
fn predicate_of(pred: Pred) -> Predicate {
match pred {
Pred::Orient2d => Predicate::Orient2d,
Pred::Incircle => Predicate::Incircle,
Pred::Orient3d => Predicate::Orient3d,
Pred::Insphere => Predicate::Insphere,
}
}
fn counted(pred: Pred, config: &[f64]) -> (Sign, Counts) {
reset();
let sign = call(pred, config);
(sign, snapshot(predicate_of(pred)))
}
fn assert_stage_is_scale_invariant(
pred: Pred,
build: fn(&mut SplitMix64) -> Vec<f64>,
cases: usize,
number: u64,
) {
let _guard = serialize();
let mut rng = SplitMix64::new(seed(number));
let mut divergences = Vec::new();
let mut checked = 0_u32;
let mut reached_exact = 0_u32;
for _ in 0..cases {
let config = build(&mut rng);
assert_eq!(
oracle(pred, &config),
Sign::Zero,
"the fixture is not degenerate: {config:?}"
);
let (base_sign, base) = counted(pred, &config);
assert_eq!(base_sign, Sign::Zero, "{config:?}");
reached_exact += u32::from(base.exact > 0);
let (lowest, highest) = in_window_shifts(pred, &config);
for shift in [lowest, highest, i32::midpoint(lowest, highest)] {
if !(-1022..=1023).contains(&shift) || shift == 0 {
continue;
}
let moved = scaled(&config, power_of_two(shift));
let (sign, after) = counted(pred, &moved);
checked += 1;
assert_eq!(sign, Sign::Zero, "shift {shift}: {config:?}");
if after != base {
divergences.push(format!(
"shift {shift}: {base:?} -> {after:?} for {config:?}"
));
}
}
}
assert!(checked > 0, "no rescaling was checked");
assert!(
reached_exact > 0,
"{}: no fixture reached the exact stage, so the deeper stages were not compared",
pred.name()
);
assert!(
divergences.is_empty(),
"{}: {} of {checked} in-window rescalings changed which stage answered:\n{}",
pred.name(),
divergences.len(),
divergences.join("\n")
);
}
#[test]
fn orient2d_stage_is_scale_invariant() {
assert_stage_is_scale_invariant(Pred::Orient2d, collinear, 400, 0);
}
#[test]
fn incircle_stage_is_scale_invariant() {
assert_stage_is_scale_invariant(Pred::Incircle, cocircular, 150, 1);
}
#[test]
fn orient3d_stage_is_scale_invariant() {
assert_stage_is_scale_invariant(Pred::Orient3d, coplanar, 150, 2);
}
#[test]
fn insphere_stage_is_scale_invariant() {
assert_stage_is_scale_invariant(Pred::Insphere, cospherical, 50, 3);
}