From c4c29328b6b123c4380bfaf0ad1c5fc58c947441 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Wed, 23 Mar 2022 20:56:04 -0400 Subject: [PATCH] refactor newtons_method --- Cargo.toml | 1 + src/function.rs | 7 ++--- src/misc.rs | 80 ++++++++++++++++++++----------------------------- 3 files changed, 35 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 69b5a59..24cddf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ tar = "0.4.38" ruzstd = { git = "https://github.com/KillingSpark/zstd-rs.git" } serde_json = "1.0.79" tracing = "0.1.32" +itertools = "0.10.3" [build-dependencies] shadow-rs = "0.11.0" diff --git a/src/function.rs b/src/function.rs index f8726bf..4c820f6 100644 --- a/src/function.rs +++ b/src/function.rs @@ -226,9 +226,6 @@ impl FunctionEntry { self.output.invalidate_derivative(); } - let do_extrema = settings.extrema && (min_max_changed | self.output.extrema.is_none()); - let do_roots = settings.roots && (min_max_changed | self.output.roots.is_none()); - self.min_x = min_x; self.max_x = max_x; @@ -281,12 +278,12 @@ impl FunctionEntry { }; // Calculates extrema - if do_extrema { + if settings.extrema && (min_max_changed | self.output.extrema.is_none()) { self.output.extrema = self.newtons_method_helper(threshold, 1); } // Calculates roots - if do_roots { + if settings.roots && (min_max_changed | self.output.roots.is_none()) { self.output.roots = self.newtons_method_helper(threshold, 0); } diff --git a/src/misc.rs b/src/misc.rs index 1f48998..e6adfdf 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -1,4 +1,5 @@ use eframe::egui::plot::Value as EguiValue; +use itertools::Itertools; use serde_json::Value as JsonValue; #[cfg(not(target_arch = "wasm32"))] @@ -183,61 +184,44 @@ pub fn newtons_method( threshold: f64, range: std::ops::Range, data: Vec, f: &dyn Fn(f64) -> f64, f_1: &dyn Fn(f64) -> f64, ) -> Vec { - let mut output_list: Vec = Vec::new(); - let mut last_ele_option: Option = None; - for ele in data.iter() { - if last_ele_option.is_none() { - last_ele_option = Some(*ele); - continue; - } + data.iter() + .tuples() + .filter(|(prev, curr)| !(prev.y.is_nan() | curr.y.is_nan())) + .map(|(prev, curr)| { + if prev.y.signum() != curr.y.signum() { + // actual start of newton's method + let x = { + let mut x1: f64 = prev.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 `ele.y` is NaN, just continue iterating - if ele.y.is_nan() { - continue; - } + // If below threshold, break + if (x2 - x1).abs() < threshold { + break; + } - let last_ele_y = last_ele_option.unwrap().y; // store this here as it's used multiple times - - // if `last_ele.y` is NaN, continue iterating - if last_ele_y.is_nan() { - continue; - } - - if last_ele_y.signum() != ele.y.signum() { - // actual start of newton's method - let x = { - let mut x1: f64 = last_ele_option.unwrap().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; + x1 = x2; } - // If below threshold, break - if (x2 - x1).abs() < threshold { - break; + // If failed, return NaN, which is then filtered out + match fail { + true => f64::NAN, + false => x1, } + }; - x1 = x2; - } - - // If failed, return NaN, which is then filtered out - match fail { - true => f64::NAN, - false => x1, - } - }; - - if !x.is_nan() { - output_list.push(x); + return x; } - } - last_ele_option = Some(*ele); - } - output_list + f64::NAN + }) + .filter(|x| !x.is_nan()) + .collect() } // Returns a vector of length `max_i` starting at value `min_x` with resolution