diff --git a/parsing/build.rs b/parsing/build.rs index 915e854..7eef5f5 100644 --- a/parsing/build.rs +++ b/parsing/build.rs @@ -17,7 +17,7 @@ fn main() { fn generate_hashmap() { let path = Path::new(&env::var("OUT_DIR").unwrap()).join("codegen.rs"); - let mut file = BufWriter::new(File::create(&path).expect("Could not create file")); + let mut file = BufWriter::new(File::create(path).expect("Could not create file")); let string_hashmap = compile_hashmap(SUPPORTED_FUNCTIONS.iter().map(|a| a.to_string()).collect()); diff --git a/parsing/src/parsing.rs b/parsing/src/parsing.rs index 6f31d0a..4941868 100644 --- a/parsing/src/parsing.rs +++ b/parsing/src/parsing.rs @@ -1,21 +1,31 @@ use exmex::prelude::*; +use std::collections::HashMap; #[derive(Clone, PartialEq)] -pub(crate) struct FlatExWrapper { +pub(crate) struct FlatExWrapper<'a> { func: Option>, + func_str: Option<&'a str>, } -impl FlatExWrapper { - const EMPTY: FlatExWrapper = FlatExWrapper { func: None }; +impl<'a> FlatExWrapper<'a> { + const EMPTY: FlatExWrapper<'a> = FlatExWrapper { + func: None, + func_str: None, + }; #[inline] - const fn new(f: FlatEx) -> Self { Self { func: Some(f) } } + const fn new(f: FlatEx) -> Self { + Self { + func: Some(f), + func_str: None, + } + } #[inline] const fn is_none(&self) -> bool { self.func.is_none() } #[inline] - fn eval(&self, x: &[f64]) -> f64 { + fn eval(&'a self, x: &[f64]) -> f64 { self.func .as_ref() .map(|f| f.eval(x).unwrap_or(f64::NAN)) @@ -23,7 +33,7 @@ impl FlatExWrapper { } #[inline] - fn partial(&self, x: usize) -> Self { + fn partial(&'a self, x: usize) -> Self { self.func .as_ref() .map(|f| f.clone().partial(x).map(Self::new).unwrap_or(Self::EMPTY)) @@ -31,10 +41,17 @@ impl FlatExWrapper { } #[inline] - fn get_string(&self) -> &str { self.func.as_ref().map(|f| f.unparse()).unwrap_or("") } + fn get_string(&'a mut self) -> &'a str { + if let Some(func_str) = self.func_str { + return func_str; + } + let calculated = self.func.as_ref().map(|f| f.unparse()).unwrap_or(""); + self.func_str = Some(calculated); + return calculated; + } #[inline] - fn partial_iter(&self, n: usize) -> Self { + fn partial_iter(&'a self, n: usize) -> Self { self.func .as_ref() .map(|f| { @@ -47,45 +64,29 @@ impl FlatExWrapper { } } -impl const Default for FlatExWrapper { - fn default() -> FlatExWrapper { FlatExWrapper::EMPTY } +impl<'a> const Default for FlatExWrapper<'a> { + fn default() -> FlatExWrapper<'a> { FlatExWrapper::EMPTY } } - /// Function that includes f(x), f'(x), f'(x)'s string representation, and f''(x) #[derive(Clone, PartialEq)] -pub struct BackingFunction { +pub struct BackingFunction<'a> { /// f(x) - function: FlatExWrapper, - - /// f'(x) - derivative_1: FlatExWrapper, - - /// Mathematical representation of f'(x) - derivative_1_str: String, - - /// f''(x) - derivative_2: FlatExWrapper, + function: FlatExWrapper<'a>, /// Temporary cache for nth derivative - nth_derivative: Option<(usize, FlatExWrapper, String)>, + nth_derivative: HashMap>, } -impl BackingFunction { - /// Empty [`BackingFunction`] instance - pub const EMPTY: BackingFunction = BackingFunction { - function: FlatExWrapper::EMPTY, - derivative_1: FlatExWrapper::EMPTY, - derivative_1_str: String::new(), - derivative_2: FlatExWrapper::EMPTY, - nth_derivative: None, - }; - +impl<'a> BackingFunction<'a> { pub const fn is_none(&self) -> bool { self.function.is_none() } /// Create new [`BackingFunction`] instance pub fn new(func_str: &str) -> Result { if func_str.is_empty() { - return Ok(Self::EMPTY); + return Ok(Self { + function: FlatExWrapper::EMPTY, + nth_derivative: HashMap::new(), + }); } let function = FlatExWrapper::new({ @@ -93,10 +94,8 @@ impl BackingFunction { match &parse_result { Err(e) => return Err(e.to_string()), - Ok(_) => { - let var_names = unsafe { parse_result.as_ref().unwrap_unchecked() } - .var_names() - .to_vec(); + Ok(ok_result) => { + let var_names = ok_result.var_names().to_vec(); if var_names != ["x"] { let var_names_not_x: Vec<&String> = var_names @@ -117,61 +116,25 @@ impl BackingFunction { unsafe { parse_result.unwrap_unchecked() } }); - let derivative_1 = function.partial(0); - - let derivative_1_str = prettyify_function_str(derivative_1.get_string()); - - let derivative_2 = derivative_1.partial(0); - Ok(Self { function, - derivative_1, - derivative_1_str, - derivative_2, - nth_derivative: None, + + nth_derivative: HashMap::new(), }) } - /// Returns Mathematical representation of the function's derivative - pub fn get_derivative_str(&self) -> &str { &self.derivative_1_str } + pub fn get(&'a mut self, derivative: usize, x: f64) -> f64 { + match derivative { + 0 => self.function.eval(&[x]), - /// Calculate f(x) - pub fn get(&self, x: f64) -> f64 { self.function.eval(&[x]) } - - /// Calculate f'(x) - pub fn get_derivative_1(&self, x: f64) -> f64 { self.derivative_1.eval(&[x]) } - - /// Calculate f''(x) - pub fn get_derivative_2(&self, x: f64) -> f64 { self.derivative_2.eval(&[x]) } - - /// Get string relating to the nth derivative - pub fn get_nth_derivative_str(&self) -> &str { - self.nth_derivative - .as_ref() - .map(|a| a.2.as_str()) - .unwrap_or("") - } - - pub fn get_nth_derivative(&mut self, n: usize, x: f64) -> f64 { - match n { - 0 => self.get(x), - 1 => self.get_derivative_1(x), - 2 => self.get_derivative_2(x), - _ => { - if let Some((curr_n, curr_n_func, _)) = &self.nth_derivative { - if curr_n == &n { - return curr_n_func.eval(&[x]); - } + _ => match self.nth_derivative.get(&derivative) { + Some(func) => func.eval(&[x]), + None => { + let new_func = self.function.partial_iter(derivative); + self.nth_derivative.insert(derivative, new_func.clone()); + new_func.eval(&[x]) } - let new_func = self.function.partial_iter(n); - - self.nth_derivative = Some(( - n, - new_func.clone(), - prettyify_function_str(new_func.get_string()), - )); - new_func.eval(&[x]) - } + }, } } } diff --git a/parsing/src/splitting.rs b/parsing/src/splitting.rs index e6e4766..0663833 100644 --- a/parsing/src/splitting.rs +++ b/parsing/src/splitting.rs @@ -15,6 +15,7 @@ pub fn split_function(input: &str, split: SplitType) -> Vec { .collect::>() } +// Specifies how to split a function #[derive(PartialEq, Debug, Copy, Clone)] pub enum SplitType { Multiplication, diff --git a/src/function_entry.rs b/src/function_entry.rs index 989ec47..b65d9d0 100644 --- a/src/function_entry.rs +++ b/src/function_entry.rs @@ -31,7 +31,7 @@ impl fmt::Display for Riemann { #[derive(Clone)] pub struct FunctionEntry { /// The `BackingFunction` instance that is used to generate `f(x)`, `f'(x)`, and `f''(x)` - function: BackingFunction, + function: BackingFunction<'static>, /// Stores a function string (that hasn't been processed via `process_func_str`) to display to the user pub raw_func_str: String, @@ -121,28 +121,10 @@ impl<'de> Deserialize<'de> for FunctionEntry { impl const Default for FunctionEntry { /// Creates default FunctionEntry instance (which is empty) - fn default() -> FunctionEntry { FunctionEntry::EMPTY } + fn default() -> FunctionEntry {} } impl FunctionEntry { - pub const EMPTY: FunctionEntry = FunctionEntry { - function: BackingFunction::EMPTY, - raw_func_str: String::new(), - integral: false, - derivative: false, - nth_derviative: false, - back_data: Vec::new(), - integral_data: None, - derivative_data: Vec::new(), - extrema_data: Vec::new(), - root_data: Vec::new(), - nth_derivative_data: None, - autocomplete: AutoComplete::EMPTY, - test_result: None, - curr_nth: 3, - settings_opened: false, - }; - pub const fn is_some(&self) -> bool { !self.function.is_none() } pub fn settings_window(&mut self, ctx: &Context) { @@ -216,10 +198,10 @@ impl FunctionEntry { }; let y = match sum { - Riemann::Left => self.function.get(left_x), - Riemann::Right => self.function.get(right_x), + Riemann::Left => self.function.get(0, left_x), + Riemann::Right => self.function.get(0, right_x), Riemann::Middle => { - (self.function.get(left_x) + self.function.get(right_x)) / 2.0 + (self.function.get(0, left_x) + self.function.get(0, right_x)) / 2.0 } }; @@ -242,22 +224,22 @@ impl FunctionEntry { threshold, range, self.back_data.as_slice(), - &|x: f64| self.function.get(x), - &|x: f64| self.function.get_derivative_1(x), + &|x: f64| self.function.get(0, x), + &|x: f64| self.function.get(1, x), ), 1 => newtons_method_helper( threshold, range, self.derivative_data.as_slice(), - &|x: f64| self.function.get_derivative_1(x), - &|x: f64| self.function.get_derivative_2(x), + &|x: f64| self.function.get(1, x), + &|x: f64| self.function.get(2, x), ), _ => unreachable!(), }; newtons_method_output .into_iter() - .map(|x| PlotPoint::new(x, self.function.get(x))) + .map(|x| PlotPoint::new(x, self.function.get(0, x))) .collect() } @@ -291,7 +273,7 @@ impl FunctionEntry { let data: Vec = resolution_iter .clone() .into_iter() - .map(|x| PlotPoint::new(x, self.function.get(x))) + .map(|x| PlotPoint::new(x, self.function.get(0, x))) .collect(); debug_assert_eq!(data.len(), settings.plot_width + 1); @@ -302,7 +284,7 @@ impl FunctionEntry { let data: Vec = resolution_iter .clone() .into_iter() - .map(|x| PlotPoint::new(x, self.function.get_derivative_1(x))) + .map(|x| PlotPoint::new(x, self.function.get(1, x))) .collect(); debug_assert_eq!(data.len(), settings.plot_width + 1); self.derivative_data = data; @@ -311,7 +293,7 @@ impl FunctionEntry { if self.nth_derviative && self.nth_derivative_data.is_none() { let data: Vec = resolution_iter .into_iter() - .map(|x| PlotPoint::new(x, self.function.get_nth_derivative(self.curr_nth, x))) + .map(|x| PlotPoint::new(x, self.function.get(self.curr_nth, x))) .collect(); debug_assert_eq!(data.len(), settings.plot_width + 1); self.nth_derivative_data = Some(data);