diff --git a/src/function.rs b/src/function.rs index 7b62951..cded261 100644 --- a/src/function.rs +++ b/src/function.rs @@ -91,7 +91,7 @@ impl FunctionEntry { /// Creates and does the math for creating all the rectangles under the /// graph fn integral_rectangles( - &self, integral_min_x: f64, integral_max_x: f64, sum: Riemann, integral_num: usize, + &self, integral_min_x: &f64, integral_max_x: &f64, sum: &Riemann, integral_num: &usize, ) -> (Vec<(f64, f64)>, f64) { if integral_min_x.is_nan() { panic!("integral_min_x is NaN") @@ -99,9 +99,9 @@ impl FunctionEntry { panic!("integral_max_x is NaN") } - let step = (integral_min_x - integral_max_x).abs() / (integral_num as f64); + let step = (integral_min_x - integral_max_x).abs() / (*integral_num as f64); - let data2: Vec<(f64, f64)> = dyn_iter(&step_helper(integral_num, integral_min_x, step)) + let data2: Vec<(f64, f64)> = dyn_iter(&step_helper(*integral_num, &integral_min_x, &step)) .map(|x| { let step_offset = step * x.signum(); // store the offset here so it doesn't have to be calculated multiple times let x2: f64 = x + step_offset; @@ -132,20 +132,22 @@ impl FunctionEntry { pub fn get_func_str(&self) -> &str { &self.func_str } /// Helps with processing newton's method depending on level of derivative - fn newtons_method_helper(&self, threshold: f64, derivative_level: usize) -> Option> { + fn newtons_method_helper( + &self, threshold: &f64, derivative_level: usize, + ) -> Option> { let range = self.min_x..self.max_x; let newtons_method_output: Vec = match derivative_level { 0 => newtons_method_helper( - threshold, - range, - self.output.back.to_owned().unwrap(), + &threshold, + &range, + &self.output.back.to_owned().unwrap(), &|x: f64| self.function.get(x), &|x: f64| self.function.get_derivative_1(x), ), 1 => newtons_method_helper( - threshold, - range, - self.output.derivative.to_owned().unwrap(), + &threshold, + &range, + &self.output.derivative.to_owned().unwrap(), &|x: f64| self.function.get_derivative_1(x), &|x: f64| self.function.get_derivative_2(x), ), @@ -157,8 +159,7 @@ impl FunctionEntry { } else { Some( dyn_iter(&newtons_method_output) - .map(|x| (*x, self.function.get(*x))) - .map(|(x, y)| Value::new(x, y)) + .map(|x| Value::new(*x, self.function.get(*x))) .collect(), ) } @@ -169,7 +170,7 @@ impl FunctionEntry { &mut self, min_x: &f64, max_x: &f64, width_changed: bool, settings: &AppSettings, ) { let resolution: f64 = settings.plot_width as f64 / (max_x.abs() + min_x.abs()); - let resolution_iter = resolution_helper(settings.plot_width + 1, *min_x, resolution); + let resolution_iter = resolution_helper(&settings.plot_width + 1, &min_x, &resolution); // Makes sure proper arguments are passed when integral is enabled if self.integral && settings.integral_changed { @@ -179,11 +180,11 @@ impl FunctionEntry { let mut partial_regen = false; let min_max_changed = (min_x != &self.min_x) | (max_x != &self.max_x); + self.min_x = *min_x; + self.max_x = *max_x; if width_changed { self.output.invalidate_back(); self.output.invalidate_derivative(); - self.min_x = *min_x; - self.max_x = *max_x; } else if min_max_changed && self.output.back.is_some() { partial_regen = true; @@ -196,22 +197,21 @@ impl FunctionEntry { .into(); let back_data: Vec = dyn_iter(&resolution_iter) - .cloned() .map(|x| { if let Some(i) = x_data.get_index(x) { back_cache[i] } else { - Value::new(x, self.function.get(x)) + Value::new(*x, self.function.get(*x)) } }) .collect(); - assert_eq!(back_data.len(), settings.plot_width + 1); + // assert_eq!(back_data.len(), settings.plot_width + 1); self.output.back = Some(back_data); let derivative_cache = self.output.derivative.as_ref().unwrap(); let new_derivative_data: Vec = dyn_iter(&resolution_iter) .map(|x| { - if let Some(i) = x_data.get_index(*x) { + if let Some(i) = x_data.get_index(x) { derivative_cache[i] } else { Value::new(*x, self.function.get_derivative_1(*x)) @@ -219,7 +219,7 @@ impl FunctionEntry { }) .collect(); - assert_eq!(new_derivative_data.len(), settings.plot_width + 1); + // assert_eq!(new_derivative_data.len(), settings.plot_width + 1); self.output.derivative = Some(new_derivative_data); } else { @@ -227,65 +227,50 @@ impl FunctionEntry { self.output.invalidate_derivative(); } - self.min_x = *min_x; - self.max_x = *max_x; - let threshold: f64 = resolution / 2.0; if !partial_regen { - self.output.back = Some({ - if self.output.back.is_none() { - let data: Vec = dyn_iter(&resolution_iter) - .map(|x| Value::new(*x, self.function.get(*x))) - .collect(); - assert_eq!(data.len(), settings.plot_width + 1); + if self.output.back.is_none() { + let data: Vec = dyn_iter(&resolution_iter) + .map(|x| Value::new(*x, self.function.get(*x))) + .collect(); + assert_eq!(data.len(), settings.plot_width + 1); - self.output.back = Some(data); - } + self.output.back = Some(data); + } - self.output.back.as_ref().unwrap().clone() - }); - - self.output.derivative = { - if self.output.derivative.is_none() { - let data: Vec = dyn_iter(&resolution_iter) - .map(|x| Value::new(*x, self.function.get_derivative_1(*x))) - .collect(); - assert_eq!(data.len(), settings.plot_width + 1); - self.output.derivative = Some(data); - } - - Some(self.output.derivative.as_ref().unwrap().clone()) - }; + if self.output.derivative.is_none() { + let data: Vec = dyn_iter(&resolution_iter) + .map(|x| Value::new(*x, self.function.get_derivative_1(*x))) + .collect(); + assert_eq!(data.len(), settings.plot_width + 1); + self.output.derivative = Some(data); + } } - self.output.integral = match self.integral { - true => { - if self.output.integral.is_none() { - let (data, area) = self.integral_rectangles( - settings.integral_min_x, - settings.integral_max_x, - settings.riemann_sum, - settings.integral_num, - ); - self.output.integral = - Some((data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(), area)); - } - - let cache = self.output.integral.as_ref().unwrap(); - Some((cache.0.clone(), cache.1)) + if self.integral { + if self.output.integral.is_none() { + let (data, area) = self.integral_rectangles( + &settings.integral_min_x, + &settings.integral_max_x, + &settings.riemann_sum, + &settings.integral_num, + ); + self.output.integral = + Some((data.iter().map(|(x, y)| Bar::new(*x, *y)).collect(), area)); } - false => None, - }; + } else { + self.output.integral = None; + } // Calculates extrema if settings.do_extrema && (min_max_changed | self.output.extrema.is_none()) { - self.output.extrema = self.newtons_method_helper(threshold, 1); + self.output.extrema = self.newtons_method_helper(&threshold, 1); } // Calculates roots if settings.do_roots && (min_max_changed | self.output.roots.is_none()) { - self.output.roots = self.newtons_method_helper(threshold, 0); + self.output.roots = self.newtons_method_helper(&threshold, 0); } } diff --git a/src/misc.rs b/src/misc.rs index 940757d..3e58490 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -71,18 +71,18 @@ pub struct SteppedVector { impl SteppedVector { /// Returns `Option` with index of element with value `x`. and `None` /// if `x` does not exist in `data` - pub fn get_index(&self, x: f64) -> Option { + pub fn get_index(&self, x: &f64) -> Option { // if `x` is outside range, just go ahead and return `None` as it *shouldn't* be // in `data` - if (x > self.max) | (self.min > x) { + if (x > &self.max) | (&self.min > x) { return None; } - if x == self.min { + if x == &self.min { return Some(0); } - if x == self.max { + if x == &self.max { return Some(self.data.len() - 1); } @@ -91,7 +91,7 @@ impl SteppedVector { // Make sure that the index is valid by checking the data returned vs the actual // data (just in case) - if self.data[possible_i] == x { + if &self.data[possible_i] == x { // It is valid! Some(possible_i) } else { @@ -221,7 +221,7 @@ pub fn decimal_round(x: f64, n: usize) -> f64 { /// `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_helper( - threshold: f64, range: std::ops::Range, data: Vec, f: &dyn Fn(f64) -> f64, + threshold: &f64, range: &std::ops::Range, data: &Vec, f: &dyn Fn(f64) -> f64, f_1: &dyn Fn(f64) -> f64, ) -> Vec { data.iter() @@ -229,9 +229,7 @@ pub fn newtons_method_helper( .filter(|(prev, curr)| !prev.y.is_nan() && !curr.y.is_nan()) .filter(|(prev, curr)| prev.y.signum() != curr.y.signum()) .map(|(prev, _)| prev.x) - .map(|start_x| { - newtons_method(f, f_1, start_x, range.clone(), threshold).unwrap_or(f64::NAN) - }) + .map(|start_x| newtons_method(f, f_1, &start_x, &range, &threshold).unwrap_or(f64::NAN)) .filter(|x| !x.is_nan()) .collect() } @@ -241,21 +239,21 @@ pub fn newtons_method_helper( /// `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, + 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 x1: f64 = *start_x; let mut x2: f64; let mut fail: bool = false; loop { - x2 = x1 - (f(x1) / f_1(x1)); + x2 = &x1 - (f(x1) / f_1(x1)); if !range.contains(&x2) { fail = true; break; } // If below threshold, break - if (x2 - x1).abs() < threshold { + if (x2 - x1).abs() < *threshold { break; } @@ -299,18 +297,16 @@ where } // 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 { +pub fn resolution_helper(max_i: usize, min_x: &f64, resolution: &f64) -> Vec { (0..max_i) - .map(|x| (x as f64 / resolution as f64) + min_x) + .map(|x| (x as f64 / resolution) + min_x) .collect() } // Returns a vector of length `max_i` starting at value `min_x` with step of // `step` -pub fn step_helper(max_i: usize, min_x: f64, step: f64) -> Vec { - (0..max_i) - .map(|x| (x as f64 * step as f64) + min_x) - .collect() +pub fn step_helper(max_i: usize, min_x: &f64, step: &f64) -> Vec { + (0..max_i).map(|x| (x as f64 * step) + min_x).collect() } pub fn chars_take(chars: &[char], i: usize) -> String { @@ -339,18 +335,18 @@ mod tests { assert_eq!(stepped_vector.get_min(), min as f64); assert_eq!(stepped_vector.get_max(), max as f64); - assert_eq!(stepped_vector.get_index(min as f64), Some(0)); - assert_eq!(stepped_vector.get_index(max as f64), Some(len_data - 1)); + assert_eq!(stepped_vector.get_index(&(min as f64)), Some(0)); + assert_eq!(stepped_vector.get_index(&(max as f64)), Some(len_data - 1)); for i in min..=max { assert_eq!( - stepped_vector.get_index(i as f64), + stepped_vector.get_index(&(i as f64)), Some((i + min.abs()) as usize) ); } - assert_eq!(stepped_vector.get_index((min - 1) as f64), None); - assert_eq!(stepped_vector.get_index((max + 1) as f64), None); + assert_eq!(stepped_vector.get_index(&((min - 1) as f64)), None); + assert_eq!(stepped_vector.get_index(&((max + 1) as f64)), None); } /// Ensures [`decimal_round`] returns correct values @@ -376,16 +372,16 @@ mod tests { #[test] fn resolution_helper_test() { assert_eq!( - resolution_helper(10, 1.0, 1.0), + resolution_helper(10, &1.0, &1.0), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] ); assert_eq!( - resolution_helper(5, -2.0, 1.0), + resolution_helper(5, &-2.0, &1.0), vec![-2.0, -1.0, 0.0, 1.0, 2.0] ); - assert_eq!(resolution_helper(3, -2.0, 1.0), vec![-2.0, -1.0, 0.0]); + assert_eq!(resolution_helper(3, &-2.0, &1.0), vec![-2.0, -1.0, 0.0]); } /// Tests [`option_vec_printer`]