use super::*;
use alloc::vec::Vec;
struct Field<F>(F);
impl<F: Fn([f64; 3]) -> f64> Sdf for Field<F> {
type Scalar = f64;
fn sample(&self, p: [f64; 3]) -> f64 {
(self.0)(p)
}
}
fn roots_of(f: impl Fn(f64) -> f64, samples: u32) -> Vec<f64> {
let mut out = Vec::new();
all_roots(
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
&Field(|p: [f64; 3]| f(p[0])),
samples,
&mut out,
);
out
}
#[test]
fn a_monotone_crossing_gives_one_root_at_the_right_place() {
let roots = roots_of(|x| x - 1.0 / 3.0, 8);
assert_eq!(roots.len(), 1);
assert!(
(roots[0] - 1.0 / 3.0).abs() < 1e-15,
"root at {} rather than 1/3",
roots[0]
);
}
#[test]
fn no_sign_change_gives_no_roots() {
assert!(roots_of(|_| 1.0, 16).is_empty());
assert!(roots_of(|_| -1.0, 16).is_empty());
}
#[test]
fn a_slab_thinner_than_the_grid_still_has_two_roots() {
for w in [0.25, 0.05, 0.01, 0.001] {
let slab = move |x: f64| (x - 0.5).abs() - w;
assert!(slab(0.0) > 0.0 && slab(1.0) > 0.0);
let samples = (4.0 / w) as u32;
let roots = roots_of(slab, samples);
assert_eq!(roots.len(), 2, "w = {w} gave {roots:?}");
assert!((roots[0] - (0.5 - w)).abs() < 1e-12, "w = {w}: {roots:?}");
assert!((roots[1] - (0.5 + w)).abs() < 1e-12, "w = {w}: {roots:?}");
}
}
#[test]
fn a_slab_thinner_than_the_sampling_is_missed_and_that_is_the_stated_bound() {
let slab = |x: f64| (x - 0.5137).abs() - 1e-4;
assert!(
roots_of(slab, 8).is_empty(),
"8 samples should step over a slab 2e-4 wide"
);
assert_eq!(roots_of(slab, 100_000).len(), 2);
}
#[test]
fn roots_come_back_ascending_and_distinct() {
let f = |x: f64| (x - 0.2) * (x - 0.4) * (x - 0.6) * (x - 0.8);
let roots = roots_of(f, 32);
assert_eq!(roots.len(), 4);
for w in roots.windows(2) {
assert!(w[0] < w[1], "not ascending: {roots:?}");
}
for (found, expected) in roots.iter().zip([0.2, 0.4, 0.6, 0.8]) {
assert!((found - expected).abs() < 1e-12, "{roots:?}");
}
}
#[test]
fn zero_counts_as_outside_so_a_touch_is_not_a_crossing() {
assert!(roots_of(|x| (x - 0.5) * (x - 0.5), 64).is_empty());
assert_eq!(roots_of(|x| -((x - 0.5) * (x - 0.5)) + 0.04, 64).len(), 2);
}
#[test]
fn the_same_edge_from_either_direction_is_a_caller_obligation_not_a_promise() {
let f = |p: [f64; 3]| p[0] - 1.0 / 3.0;
let mut forward = Vec::new();
all_roots([0.0; 3], [1.0, 0.0, 0.0], &Field(f), 8, &mut forward);
let mut backward = Vec::new();
all_roots([1.0, 0.0, 0.0], [0.0; 3], &Field(f), 8, &mut backward);
assert_eq!(forward.len(), 1);
assert_eq!(backward.len(), 1);
let forward_x = forward[0];
let backward_x = 1.0 - backward[0];
assert!(
(forward_x - backward_x).abs() < 1e-15,
"{forward_x} vs {backward_x}"
);
}
#[test]
fn the_same_call_twice_is_bit_identical() {
let f = |p: [f64; 3]| (p[0] - 0.3) * (p[0] - 0.7);
let mut a = Vec::new();
let mut b = Vec::new();
all_roots([0.0; 3], [1.0, 0.0, 0.0], &Field(f), 16, &mut a);
all_roots([0.0; 3], [1.0, 0.0, 0.0], &Field(f), 16, &mut b);
assert_eq!(a, b);
assert_eq!(a.len(), 2);
}
#[expect(clippy::float_cmp, reason = "asserting a value was not written to")]
#[test]
fn results_append_rather_than_replace() {
let mut out = alloc::vec![-1.0f64];
all_roots(
[0.0; 3],
[1.0, 0.0, 0.0],
&Field(|p: [f64; 3]| p[0] - 0.5),
4,
&mut out,
);
assert_eq!(out.len(), 2);
assert_eq!(out[0], -1.0);
}
#[test]
fn zero_samples_finds_nothing_rather_than_dividing_by_zero() {
assert!(roots_of(|x| x - 0.5, 0).is_empty());
}