pub fn bisect(mut below: impl FnMut(f64) -> bool, lo: f64, hi: f64, iters: usize) -> f64 {
let (mut lo, mut hi) = (lo, hi);
for _ in 0..iters {
let mid = 0.5 * (lo + hi);
if below(mid) {
lo = mid;
} else {
hi = mid;
}
}
0.5 * (lo + hi)
}
pub fn regula_falsi(mut f: impl FnMut(f64) -> f64, a: f64, b: f64, x_tol: f64, f_tol: f64, iters: usize) -> f64 {
let (mut a, mut b) = (a, b);
let (mut fa, mut fb) = (f(a), f(b));
let mut side = 0;
for _ in 0..iters {
let c = (a * fb - b * fa) / (fb - fa);
let fc = f(c);
if fc.abs() < f_tol || (b - a).abs() < x_tol {
return c;
}
if (fc < 0.0) == (fa < 0.0) {
(a, fa) = (c, fc);
if side == -1 {
fb /= 2.0;
}
side = -1;
} else {
(b, fb) = (c, fc);
if side == 1 {
fa /= 2.0;
}
side = 1;
}
}
0.5 * (a + b)
}
pub fn golden_max(mut f: impl FnMut(f64) -> f64, a: f64, b: f64, iters: usize) -> f64 {
let gr = (5f64.sqrt() - 1.0) / 2.0;
let (mut a, mut b) = (a, b);
let mut c = b - gr * (b - a);
let mut d = a + gr * (b - a);
let (mut fc, mut fd) = (f(c), f(d));
for _ in 0..iters {
if fc > fd {
b = d;
d = c;
fd = fc;
c = b - gr * (b - a);
fc = f(c);
} else {
a = c;
c = d;
fc = fd;
d = a + gr * (b - a);
fd = f(d);
}
}
0.5 * (a + b)
}
pub fn gcd(a: usize, b: usize) -> usize {
let (mut a, mut b) = (a, b);
while b != 0 {
(a, b) = (b, a % b);
}
a
}
pub fn argmax(values: impl IntoIterator<Item = f64>) -> Option<(usize, f64)> {
let mut best: Option<(usize, f64)> = None;
for (i, v) in values.into_iter().enumerate() {
if !v.is_nan() && best.is_none_or(|(_, b)| v > b) {
best = Some((i, v));
}
}
best
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn argmax_is_the_former_fold() {
let fold = |v: &[f64]| {
v.iter().enumerate().fold((0, f64::NEG_INFINITY), |b, (k, &x)| if x > b.1 { (k, x) } else { b })
};
let mut r = comnum_core::rng::Rng::new(3);
for n in 1..40 {
let v: Vec<f64> = (0..n).map(|_| (r.gaussian() * 3.0).round()).collect();
assert_eq!(argmax(v.iter().copied()), Some(fold(&v)), "{v:?}");
}
assert_eq!(argmax([f64::NAN, f64::NAN]), None);
assert_eq!(argmax([f64::NEG_INFINITY]), Some((0, f64::NEG_INFINITY)));
}
#[test]
fn bisection_and_golden_section_reach_their_targets() {
assert_eq!(bisect(|_| true, 0.0, 1.0, 0), 0.5);
assert!(bisect(|_| true, 0.0, 1.0, 60) > 1.0 - 1e-15);
let x = golden_max(|x| -(x - 2.5).abs(), -10.0, 10.0, 100);
assert!((x - 2.5).abs() < 1e-9);
assert_eq!((gcd(1, 1), gcd(36, 24), gcd(17, 0)), (1, 12, 17));
}
#[test]
fn regula_falsi_finds_the_roots_of_increasing_and_decreasing_functions() {
let up = regula_falsi(|x| x.exp() - 3.0, 0.0, 5.0, 1e-13, 1e-15, 200);
assert!((up - 3f64.ln()).abs() < 1e-12);
let down = regula_falsi(|x| 1.0 - x * x, 0.0, 4.0, 1e-13, 1e-15, 200);
assert!((down - 1.0).abs() < 1e-12);
assert_eq!(regula_falsi(|x| x, -1.0, 3.0, 0.0, 0.0, 0), 1.0);
}
}