From 50892ed4b5ca034436e03312c3b0b758eea738d6 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Thu, 24 Mar 2022 08:45:09 -0400 Subject: [PATCH] refactor newton's method --- src/function.rs | 6 ++--- src/misc.rs | 63 +++++++++++++++++++++++++++++-------------------- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/function.rs b/src/function.rs index 3908fac..0f6d417 100644 --- a/src/function.rs +++ b/src/function.rs @@ -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> { let newtons_method_output: Vec = 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(), diff --git a/src/misc.rs b/src/misc.rs index a3368e5..403d430 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -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, data: Vec, f: &dyn Fn(f64) -> f64, f_1: &dyn Fn(f64) -> f64, ) -> Vec { @@ -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` 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, + threshold: f64, +) -> Option { + 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 {