1use crate::error::{finite, tolerance, NumericError, NumericResult, Status};
17
18#[derive(Debug, Clone, Copy, PartialEq)]
20#[must_use]
21pub struct Root {
22 pub x: f64,
24 pub value: f64,
26 pub lower: f64,
28 pub upper: f64,
31 pub iterations: usize,
33 pub evaluations: usize,
35 pub status: Status,
38}
39
40impl Root {
41 pub fn error_bound(&self) -> f64 {
43 self.upper - self.lower
44 }
45}
46
47const MAX_ITERATIONS: usize = 10_000;
51
52pub fn find_root<F>(mut f: F, lower: f64, upper: f64, x_tolerance: f64) -> NumericResult<Root>
65where
66 F: FnMut(f64) -> f64,
67{
68 finite(lower, "lower bound")?;
69 finite(upper, "upper bound")?;
70 tolerance(x_tolerance, "x tolerance")?;
71 if lower > upper {
72 return Err(NumericError::InvalidArgument {
73 name: "bracket",
74 reason: "must have lower <= upper",
75 });
76 }
77 let mut evaluations = 0usize;
78 let mut eval = |x: f64| -> NumericResult<f64> {
79 evaluations += 1;
80 let v = f(x);
81 if v.is_finite() {
82 Ok(v)
83 } else {
84 Err(NumericError::NonFiniteEvaluation {
85 name: "function",
86 at: Some(x),
87 })
88 }
89 };
90
91 let (mut a, mut b) = (lower, upper);
92 let mut fa = eval(a)?;
93 let mut fb = eval(b)?;
94 if fa == 0.0 {
95 return Ok(exact(a, 0, evaluations));
96 }
97 if fb == 0.0 {
98 return Ok(exact(b, 0, evaluations));
99 }
100 if fa.signum() == fb.signum() {
101 return Err(NumericError::NoSignChange {
102 f_lower: fa,
103 f_upper: fb,
104 });
105 }
106
107 let (mut c, mut fc) = (a, fa);
108 let mut d = b - a;
109 let mut e = d;
110 let mut iterations = 0usize;
111 loop {
112 if fb.signum() == fc.signum() {
113 c = a;
114 fc = fa;
115 d = b - a;
116 e = d;
117 }
118 if fc.abs() < fb.abs() {
119 a = b;
120 b = c;
121 c = a;
122 fa = fb;
123 fb = fc;
124 fc = fa;
125 }
126 let tol1 = 2.0 * f64::EPSILON * b.abs() + 0.5 * x_tolerance;
127 let xm = 0.5 * (c - b);
128 let midpoint = b + xm;
129 if xm.abs() <= tol1 || fb == 0.0 || midpoint == b || midpoint == c {
130 break;
131 }
132 if iterations >= MAX_ITERATIONS {
133 return Ok(finish(
134 b,
135 fb,
136 c,
137 iterations,
138 evaluations,
139 Status::BudgetExhausted,
140 ));
141 }
142 iterations += 1;
143
144 if e.abs() >= tol1 && fa.abs() > fb.abs() {
145 let s = fb / fa;
146 let (mut p, mut q);
147 if a == c {
148 p = 2.0 * xm * s;
149 q = 1.0 - s;
150 } else {
151 let qq = fa / fc;
152 let r = fb / fc;
153 p = s * (2.0 * xm * qq * (qq - r) - (b - a) * (r - 1.0));
154 q = (qq - 1.0) * (r - 1.0) * (s - 1.0);
155 }
156 if p > 0.0 {
157 q = -q;
158 } else {
159 p = -p;
160 }
161 if 2.0 * p < (3.0 * xm * q - (tol1 * q).abs()).min((e * q).abs()) {
162 e = d;
163 d = p / q;
164 } else {
165 d = xm;
166 e = d;
167 }
168 } else {
169 d = xm;
170 e = d;
171 }
172 a = b;
173 fa = fb;
174 b += if d.abs() > tol1 { d } else { tol1.copysign(xm) };
175 fb = eval(b)?;
176 }
177 if fb == 0.0 {
178 return Ok(exact(b, iterations, evaluations));
179 }
180 Ok(finish(b, fb, c, iterations, evaluations, Status::Converged))
181}
182
183fn exact(x: f64, iterations: usize, evaluations: usize) -> Root {
184 Root {
185 x,
186 value: 0.0,
187 lower: x,
188 upper: x,
189 iterations,
190 evaluations,
191 status: Status::Converged,
192 }
193}
194
195fn finish(b: f64, fb: f64, c: f64, iterations: usize, evaluations: usize, status: Status) -> Root {
196 Root {
197 x: b,
198 value: fb,
199 lower: b.min(c),
200 upper: b.max(c),
201 iterations,
202 evaluations,
203 status,
204 }
205}