refactor newton's method

This commit is contained in:
Simon Gardling 2022-03-24 08:45:09 -04:00
parent 7dc7167a57
commit 50892ed4b5
2 changed files with 40 additions and 29 deletions

View File

@ -2,7 +2,7 @@
use crate::egui_app::AppSettings;
use crate::function_output::FunctionOutput;
use crate::misc::{dyn_iter, newtons_method, resolution_helper, step_helper, SteppedVector};
use crate::misc::{dyn_iter, newtons_method_helper, resolution_helper, step_helper, SteppedVector};
use crate::parsing::BackingFunction;
use eframe::{egui, epaint};
use egui::{
@ -133,14 +133,14 @@ impl FunctionEntry {
fn newtons_method_helper(&self, threshold: f64, derivative_level: usize) -> Option<Vec<Value>> {
let newtons_method_output: Vec<f64> = match derivative_level {
0 => newtons_method(
0 => newtons_method_helper(
threshold,
self.min_x..self.max_x,
self.output.back.to_owned().unwrap(),
&|x: f64| self.function.get(x),
&|x: f64| self.function.get_derivative_1(x),
),
1 => newtons_method(
1 => newtons_method_helper(
threshold,
self.min_x..self.max_x,
self.output.derivative.to_owned().unwrap(),

View File

@ -173,14 +173,14 @@ pub fn decimal_round(x: f64, n: usize) -> f64 {
// off after the `n`th decimal place
}
/// Implements newton's method of finding roots.
/// `threshold` is the target accuracy threshold
/// Helper that assists with using newton's method of finding roots, iterating
/// over data `data` `threshold` is the target accuracy threshold
/// `range` is the range of valid x values (used to stop calculation when the
/// point won't display anyways) `data` is the data to iterate over (a Vector of
/// egui's `Value` struct) `f` is f(x)
/// `f_1` is f'(x) aka the derivative of f(x)
/// The function returns a Vector of `x` values where roots occur
pub fn newtons_method(
pub fn newtons_method_helper(
threshold: f64, range: std::ops::Range<f64>, data: Vec<EguiValue>, f: &dyn Fn(f64) -> f64,
f_1: &dyn Fn(f64) -> f64,
) -> Vec<f64> {
@ -190,34 +190,45 @@ pub fn newtons_method(
.filter(|(prev, curr)| prev.y.signum() != curr.y.signum())
.map(|(prev, _)| prev.x)
.map(|start_x| {
let mut x1: f64 = start_x;
let mut x2: f64;
let mut fail: bool = false;
loop {
x2 = x1 - (f(x1) / f_1(x1));
if !range.contains(&x2) {
fail = true;
break;
}
// If below threshold, break
if (x2 - x1).abs() < threshold {
break;
}
x1 = x2;
}
// If failed, return NaN, which is then filtered out
match fail {
true => f64::NAN,
false => x1,
}
newtons_method(f, f_1, start_x, range.clone(), threshold).unwrap_or(f64::NAN)
})
.filter(|x| !x.is_nan())
.collect()
}
/// `range` is the range of valid x values (used to stop calculation when the
/// `f` is f(x)
/// `f_1` is f'(x) aka the derivative of f(x)
/// The function returns an `Option<f64>` of the x value at which a root occurs
fn newtons_method(
f: &dyn Fn(f64) -> f64, f_1: &dyn Fn(f64) -> f64, start_x: f64, range: std::ops::Range<f64>,
threshold: f64,
) -> Option<f64> {
let mut x1: f64 = start_x;
let mut x2: f64;
let mut fail: bool = false;
loop {
x2 = x1 - (f(x1) / f_1(x1));
if !range.contains(&x2) {
fail = true;
break;
}
// If below threshold, break
if (x2 - x1).abs() < threshold {
break;
}
x1 = x2;
}
// If failed, return NaN, which is then filtered out
match fail {
true => None,
false => Some(x1),
}
}
// Returns a vector of length `max_i` starting at value `min_x` with resolution
// of `resolution`
pub fn resolution_helper(max_i: usize, min_x: f64, resolution: f64) -> Vec<f64> {