1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//! Utility functions.

use crate::{Num, R};

/// Finds (at most) one zero of a real or complex function using the
/// [secant method](https://en.wikipedia.org/wiki/Secant_method).
///
/// `f` is the (real or complex) function whose zero is to be searched,
/// `guesses` contains two distinct initial guesses, `tol` is the tolerance
/// requested for the absolute value of the function at its candidate zero,
/// `max_iter` is the maximum number of iterations. After `max_iter` iterations
/// the algorithm is forced to fail and the function returns `None`.
///
/// When the algorithm doesn't fail, `find_root` returns `Some((z0, f0))`, where
/// `z0` is the candidate zero and `f0 = f(z0)`, with `abs(f0)` guaranteed to be
/// smaller than `tol`.
///
/// # Panics
///
/// This function panics if `guesses.0 == guesses.1` or if `tol <= 0.`.
pub fn find_root<T: Num, F: Fn(T) -> T>(
    f: F,
    guesses: (T, T),
    tol: R,
    max_iter: usize,
) -> Option<(T, T)> {
    assert!(guesses.0 != guesses.1);
    assert!(tol > 0.);

    let (mut z0, mut z1) = guesses;
    let (mut f0, mut f1) = (f(z0), f(z1));
    for _ in 0..max_iter {
        if f1.re().is_nan() || f1.im().is_nan() {
            return None;
        } else if f1.abs() < tol {
            return Some((z1, f1));
        }
        let z2 = (z0 * f1 - z1 * f0) / (f1 - f0);
        (f0, f1) = (f1, f(z2));
        (z0, z1) = (z1, z2);
    }
    None
}