// hpstat: High-performance statistics implementations // Copyright © 2023 Lee Yingtong Li (RunasSudo) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . pub struct IllinoisRootFinder { bound_lower: f64, bound_upper: f64, value_lower: f64, value_upper: f64, last_sign: f64 // Sign of the function at last evaluation (1.0 or -1.0) or 0.0 if first iteration } impl IllinoisRootFinder { pub fn new(bound_lower: f64, bound_upper: f64, value_lower: f64, value_upper: f64) -> IllinoisRootFinder { return IllinoisRootFinder { bound_lower: bound_lower, bound_upper: bound_upper, value_lower: value_lower, value_upper: value_upper, last_sign: 0.0 } } pub fn update(&mut self, guess: f64, mut value: f64) { if value > 0.0 { if self.value_lower > 0.0 || self.value_upper < 0.0 { self.bound_lower = guess; self.value_lower = value; if self.last_sign == 1.0 { // Illinois adjustment: Halve the y-value of the retained end point (the other end point) when the new y-value has the same sign as the previous one self.value_upper *= 0.5; } } else { self.bound_upper = guess; self.value_upper = value; if self.last_sign == 1.0 { self.value_lower *= 0.5; } } self.last_sign = 1.0; } else { if self.value_lower < 0.0 || self.value_upper > 0.0 { self.bound_lower = guess; self.value_lower = value; if self.last_sign == -1.0 { self.value_upper *= 0.5; } } else { self.bound_upper = guess; self.value_upper = value; if self.last_sign == -1.0 { self.value_lower *= 0.5; } } self.last_sign = -1.0; } } pub fn next_guess(&self) -> f64 { if self.value_lower.is_nan() || self.value_upper.is_nan() { // Fall back to interval bisection return (self.bound_lower + self.bound_upper) / 2.0; } else { // Regula falsi return (self.bound_lower * self.value_upper - self.bound_upper * self.value_lower) / (self.value_upper - self.value_lower); } } pub fn precision(&self) -> f64 { return (self.bound_upper - self.bound_lower).abs(); } }