use ndarray::Array1;
use crate::manifold::RiemannianManifold;
use crate::manifolds::constant_curvature::ConstantCurvature;
use crate::manifolds::poincare;
struct Rng(u64);
impl Rng {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_u64(&mut self) -> u64 {
let mut x = self.0;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.0 = x;
x
}
fn uniform(&mut self) -> f64 {
(self.next_u64() >> 11) as f64 / (1u64 << 53) as f64
}
fn normal(&mut self) -> f64 {
let u1 = self.uniform().max(1.0e-300);
let u2 = self.uniform();
(-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos()
}
fn gaussian_vec(&mut self, n: usize) -> Array1<f64> {
Array1::from_shape_fn(n, |_| self.normal())
}
}
fn chart_point(rng: &mut Rng, dim: usize) -> Array1<f64> {
let mut v = rng.gaussian_vec(dim);
let magnitude = v.dot(&v).sqrt().max(1.0e-30);
v *= 0.45 * rng.uniform() / magnitude;
v
}
const CURVATURES: [f64; 11] = [
2.0, 1.0, 0.35, 0.01, 1.0e-6, 0.0, -1.0e-6, -0.01, -0.35, -1.0, -2.0,
];
const DIMS: [usize; 4] = [1, 2, 3, 5];
const TRIALS: usize = 200;
const SEED: u64 = 0x9E37_79B9_7F4A_7C15;
fn seed_for(dim: usize, kappa: f64) -> u64 {
SEED ^ ((dim as u64) << 32) ^ kappa.to_bits()
}
#[test]
fn sectional_curvature_is_kappa_everywhere() {
let mut verified = 0usize;
for dim in DIMS {
if dim < 2 {
continue;
}
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let u = manifold
.project_tangent(x.view(), rng.gaussian_vec(dim).view())
.expect("tangent u");
let v = manifold
.project_tangent(x.view(), rng.gaussian_vec(dim).view())
.expect("tangent v");
let Ok(k) = manifold.sectional_curvature(x.view(), (u.view(), v.view())) else {
continue;
};
assert!(
(k - kappa).abs() <= 1.0e-9 * kappa.abs().max(1.0),
"dim {dim} kappa {kappa}: sectional curvature is {k}, not kappa"
);
verified += 1;
}
}
}
assert!(verified > 0, "no sectional curvature was evaluated");
}
#[test]
fn distance_is_invariant_under_mobius_translation() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let g = chart_point(&mut rng, dim);
let base = manifold.distance(x.view(), y.view()).expect("d(x,y)");
let (Ok(gx), Ok(gy)) = (
manifold.mobius_add(g.view(), x.view()),
manifold.mobius_add(g.view(), y.view()),
) else {
continue;
};
let Ok(moved) = manifold.distance(gx.view(), gy.view()) else {
continue;
};
assert!(
(moved - base).abs() <= 1.0e-9 * base.max(1.0),
"dim {dim} kappa {kappa}: translation changed the distance \
({base} -> {moved})"
);
verified += 1;
}
}
}
assert!(verified > 0, "no translated distance was evaluated");
}
#[test]
fn the_kappa_family_is_one_manifold_rescaled() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
if kappa.abs() <= 1.0e-9 {
continue;
}
let manifold = ConstantCurvature::new(dim, kappa);
let unit = ConstantCurvature::new(dim, kappa.signum());
let scale = kappa.abs().sqrt();
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let here = manifold.distance(x.view(), y.view()).expect("d_kappa");
let Ok(there) = unit.distance((&x * scale).view(), (&y * scale).view()) else {
continue;
};
assert!(
(here - there / scale).abs() <= 1.0e-9 * here.max(1.0),
"dim {dim} kappa {kappa}: d_kappa = {here} but the rescaled unit \
form gives {}",
there / scale
);
verified += 1;
}
}
}
assert!(verified > 0, "no rescaling was evaluated");
}
#[test]
fn the_flat_and_hyperbolic_members_match_their_classical_forms() {
let mut flat_checked = 0usize;
let mut hyperbolic_checked = 0usize;
for dim in DIMS {
let flat = ConstantCurvature::new(dim, 0.0);
let hyperbolic = ConstantCurvature::new(dim, -1.0);
let mut rng = Rng::new(seed_for(dim, 0.0));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let got = flat.distance(x.view(), y.view()).expect("flat distance");
let want = 2.0 * (&x - &y).dot(&(&x - &y)).sqrt();
assert!(
(got - want).abs() <= 1.0e-12 * want.max(1.0),
"dim {dim}: flat distance {got} != 2||x-y|| = {want}"
);
flat_checked += 1;
let got = hyperbolic
.distance(x.view(), y.view())
.expect("hyperbolic distance");
let want =
poincare::poincare_distance(x.view(), y.view(), -1.0).expect("poincare distance");
assert!(
(got - want).abs() <= 1.0e-12 * want.max(1.0),
"dim {dim}: kappa=-1 distance {got} disagrees with poincare.rs {want}"
);
hyperbolic_checked += 1;
}
}
assert!(flat_checked > 0 && hyperbolic_checked > 0);
}
#[test]
fn distance_is_a_metric_and_agrees_with_the_logarithm() {
let mut verified = 0usize;
for dim in DIMS {
for kappa in CURVATURES {
let manifold = ConstantCurvature::new(dim, kappa);
let mut rng = Rng::new(seed_for(dim, kappa));
for _ in 0..TRIALS {
let x = chart_point(&mut rng, dim);
let y = chart_point(&mut rng, dim);
let z = chart_point(&mut rng, dim);
let dxy = manifold.distance(x.view(), y.view()).expect("d(x,y)");
let dyx = manifold.distance(y.view(), x.view()).expect("d(y,x)");
let dyz = manifold.distance(y.view(), z.view()).expect("d(y,z)");
let dxz = manifold.distance(x.view(), z.view()).expect("d(x,z)");
assert!(
(dxy - dyx).abs() <= 1.0e-12 * dxy.max(1.0),
"dim {dim} kappa {kappa}: distance is not symmetric ({dxy} vs {dyx})"
);
assert!(
dxz <= dxy + dyz + 1.0e-12 * dxz.max(1.0),
"dim {dim} kappa {kappa}: triangle inequality violated \
({dxz} > {dxy} + {dyz})"
);
let logarithm = manifold.log_map(x.view(), y.view()).expect("log");
let lambda = manifold
.conformal_factor(x.view())
.expect("conformal factor");
let metric_norm = lambda * logarithm.dot(&logarithm).sqrt();
assert!(
(metric_norm - dxy).abs() <= 1.0e-9 * dxy.max(1.0),
"dim {dim} kappa {kappa}: ||log|| = {metric_norm} != d = {dxy}"
);
verified += 1;
}
}
}
assert!(verified > 0, "no metric axiom was evaluated");
}
#[test]
fn spherical_branch_folds_at_kappa_r2_one_so_the_kappa_window_is_symmetric_2687() {
const R: f64 = 0.6;
let r2 = R * R;
let x = ndarray::array![R, 0.0];
let y = ndarray::array![-R, 0.0];
let cap = 0.5 / r2;
let manifold = ConstantCurvature::new(2, cap);
let d_cap = manifold
.distance(x.view(), y.view())
.expect("the shipped κ cap must be strictly inside the chart");
assert!(
(d_cap - 2.089_007_403_281_048).abs() <= 1.0e-12,
"κ = 0.5/R²: d = {d_cap}, predicted 2.089007403281048"
);
let frac_cap = d_cap * cap.sqrt() / std::f64::consts::PI;
assert!(
(frac_cap - 0.783_653_104_061_214_8).abs() <= 1.0e-12,
"κ = 0.5/R²: antipodal fraction = {frac_cap}, predicted 0.7836531040612148"
);
let fold = 1.0 / r2;
let refused = ConstantCurvature::new(2, fold).distance(x.view(), y.view());
assert!(
refused.is_err(),
"κ = 1/R² = {fold} is the κ>0 antipodal fold and must be refused; got {refused:?}"
);
let exact = ConstantCurvature::new(2, 4.0);
let exact_refused = exact.distance(
ndarray::array![0.5, 0.0].view(),
ndarray::array![-0.5, 0.0].view(),
);
assert!(
exact_refused.is_err(),
"κ‖x‖‖y‖ = 1 exactly must be refused; got {exact_refused:?}"
);
let kappa_wide = 9.5 / r2;
let kappa_twin = (1.0 / 9.5) / r2;
let d_wide = ConstantCurvature::new(2, kappa_wide)
.distance(x.view(), y.view())
.expect("past the fold the chart still evaluates — that is the problem");
let d_twin = ConstantCurvature::new(2, kappa_twin)
.distance(x.view(), y.view())
.expect("the twin is interior");
let frac_wide = d_wide * kappa_wide.sqrt() / std::f64::consts::PI;
let frac_twin = d_twin * kappa_twin.sqrt() / std::f64::consts::PI;
assert!(
(frac_wide - 0.399_450_751_329_086_6).abs() <= 1.0e-12,
"κ = 9.5/R²: antipodal fraction = {frac_wide}, predicted 0.3994507513290866"
);
assert!(
(frac_wide - frac_twin).abs() <= 1.0e-14,
"κ = 9.5/R² and κ = (1/9.5)/R² must be indistinguishable in the pair's \
scale-free geometry: {frac_wide} vs {frac_twin}"
);
assert!(
kappa_twin < cap && cap < fold && fold < kappa_wide,
"the ordering this test is about: twin {kappa_twin} < cap {cap} < fold \
{fold} < proposed {kappa_wide}"
);
let mut rng = Rng::new(0x2687);
let mut verified = 0usize;
for _ in 0..64 {
let p = chart_point(&mut rng, 2);
let q = chart_point(&mut rng, 2);
let ab2 = p.dot(&p) * q.dot(&q);
for s in [0.25_f64, 0.75, 3.0] {
let k_lo = s / ab2.sqrt();
let k_hi = 1.0 / (k_lo * ab2);
let (Ok(d_lo), Ok(d_hi)) = (
ConstantCurvature::new(2, k_lo).distance(p.view(), q.view()),
ConstantCurvature::new(2, k_hi).distance(p.view(), q.view()),
) else {
continue;
};
let f_lo = d_lo * k_lo.sqrt() / std::f64::consts::PI;
let f_hi = d_hi * k_hi.sqrt() / std::f64::consts::PI;
assert!(
(f_lo - f_hi).abs() <= 1.0e-12,
"κ ↦ 1/(κ‖x‖²‖y‖²) must fix the scale-free separation: \
κ={k_lo} gives {f_lo}, κ={k_hi} gives {f_hi}"
);
verified += 1;
}
}
assert!(verified > 0, "no reflected pair was evaluated");
}