use std::f64::consts::PI;
fn add(sum: &mut f64, correction: &mut f64, value: f64) {
let next = value - *correction;
let total = *sum + next;
*correction = (total - *sum) - next;
*sum = total;
}
fn integral(tau: f64, n: usize, panels: usize) -> (f64, f64) {
if tau == 0.0 {
return (if n == 0 { 1.0 } else { 0.0 }, 0.0);
}
assert!(panels.is_multiple_of(2));
let root = tau.sqrt();
let upper = (PI * root).min(24.0);
let h = upper / panels as f64;
let mut sum = 0.0;
let mut correction = 0.0;
for i in 0..=panels {
let u = i as f64 * h;
let sine = (u / (2.0 * root)).sin();
let weight = if i == 0 || i == panels {
1.0
} else if i % 2 == 0 {
2.0
} else {
4.0
};
add(
&mut sum,
&mut correction,
weight * (-2.0 * tau * sine * sine).exp() * (n as f64 * u / root).cos(),
);
}
let c = 2.0 / (PI * PI);
let tail = if upper < PI * root {
(-c * upper * upper).exp() / (2.0 * c * upper * PI * root)
} else {
0.0
};
(sum * h / (3.0 * PI * root), tail)
}
fn series(tau: f64, n: usize) -> f64 {
assert!((0.0..=700.0).contains(&tau));
let mut term = 1.0;
for k in 1..=n {
term *= (tau / 2.0) / k as f64;
}
let mut sum = term;
let mut correction = 0.0;
for j in 1..10_000 {
let ratio = (tau * tau / 4.0) / (j as f64 * (n + j) as f64);
term *= ratio;
add(&mut sum, &mut correction, term);
assert!(sum.is_finite());
if ratio < 1.0 && (term == 0.0 || term < sum * f64::EPSILON / 4.0) {
return sum * (-tau).exp();
}
}
panic!("series did not converge");
}
fn main() {
let times = [
0.0,
0.125,
0.5,
1.0,
3.0,
5.0,
20.0,
80.0,
100.0,
104.0,
500.0,
700.0,
1000.0,
10_000.0,
1_000_000.0,
f32::MAX as f64,
];
let orders = [0, 1, 2, 5, 10, 20, 40, 80, 160, 320];
let mut count = 0;
let mut series_count = 0;
let mut resolved_count = 0;
let mut max_refinement = 0.0_f64;
let mut max_series_error = 0.0_f64;
println!("{{\"rows\":[");
for tau in times {
for n in orders {
let (coarse, _) = integral(tau, n, 16_384);
let (fine, tail) = integral(tau, n, 32_768);
let scale = tau.sqrt().max(1.0);
let refinement = (coarse - fine).abs();
max_refinement = max_refinement.max(refinement);
assert!(refinement < 2e-13 / scale);
assert!(tail < 1e-49 / scale);
let (reference, method, series_json) = if tau <= 700.0 {
let value = series(tau, n);
let discrepancy = (value - fine).abs();
max_series_error = max_series_error.max(discrepancy);
assert!(discrepancy < 2e-13 / scale);
series_count += 1;
(Some(value), "positive_series", format!("{value:.17e}"))
} else {
if fine > 1e-9 / scale {
(Some(fine), "scaled_integral", "null".to_owned())
} else {
(None, "unresolved_integral", "null".to_owned())
}
};
let coefficient = reference.map(|value| {
value
* if n == 0 {
1.0
} else if n % 2 == 0 {
2.0
} else {
-2.0
}
});
resolved_count += usize::from(coefficient.is_some());
let coefficient_json =
coefficient.map_or_else(|| "null".to_owned(), |v| format!("{v:.17e}"));
let bits_json =
coefficient.map_or_else(|| "null".to_owned(), |v| (v as f32).to_bits().to_string());
let separator = if count == 0 { "" } else { "," };
println!(
"{separator}{{\"tau\":{tau:.17e},\"n\":{n},\"integral_coarse\":{coarse:.17e},\"integral_fine\":{fine:.17e},\"tail_bound\":{tail:.17e},\"series\":{series_json},\"reference_method\":\"{method}\",\"coefficient\":{coefficient_json},\"coefficient_f32_bits\":{bits_json}}}"
);
count += 1;
}
}
println!(
"],\"count\":{count},\"resolved_count\":{resolved_count},\"series_count\":{series_count},\"max_refinement_difference\":{max_refinement:.17e},\"max_series_integral_difference\":{max_series_error:.17e}}}"
);
}