nth derivative

This commit is contained in:
Simon Gardling 2022-04-12 12:47:36 -04:00
parent 927ca265e6
commit e46040d616
5 changed files with 123 additions and 8 deletions

11
TODO.md
View File

@ -9,9 +9,8 @@
- Display of intersections between functions - Display of intersections between functions
4. Allow constants in min/max integral input (like pi or euler's number) 4. Allow constants in min/max integral input (like pi or euler's number)
5. Sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos) 5. Sliding values for functions (like a user-interactable slider that adjusts a variable in the function, like desmos)
6. nth derivative support (again) 6. Threading (Partially implemented when running natively)
7. Threading (Partially mplemented when running natively) 7. Fix integral display
8. Fix integral display 8. Look into other, better methods of compression that would be faster
9. Look into other, better methods of compression that would be faster 9. Better handling of panics and errors to display to the user
10. Better handling of panics and errors to display to the user 10. Turn Dynamic Iterator functions into traits
11. Turn Dynamic Iterator functions into traits

View File

@ -50,7 +50,7 @@ pub const DEFAULT_INTEGRAL_NUM: usize = 100;
/// Colors used for plotting /// Colors used for plotting
// Colors commented out are used elsewhere and are not included here for better user experience // Colors commented out are used elsewhere and are not included here for better user experience
pub const COLORS: &[Color32; 14] = &[ pub const COLORS: &[Color32; 13] = &[
Color32::RED, Color32::RED,
// Color32::GREEN, // Color32::GREEN,
// Color32::YELLOW, // Color32::YELLOW,
@ -65,7 +65,7 @@ pub const COLORS: &[Color32; 14] = &[
Color32::LIGHT_GRAY, Color32::LIGHT_GRAY,
Color32::LIGHT_RED, Color32::LIGHT_RED,
Color32::DARK_GRAY, Color32::DARK_GRAY,
Color32::DARK_RED, // Color32::DARK_RED,
Color32::KHAKI, Color32::KHAKI,
Color32::DARK_GREEN, Color32::DARK_GREEN,
Color32::DARK_BLUE, Color32::DARK_BLUE,

View File

@ -8,6 +8,7 @@ use eframe::{egui, epaint};
use egui::{ use egui::{
plot::{BarChart, PlotUi, Value}, plot::{BarChart, PlotUi, Value},
widgets::plot::Bar, widgets::plot::Bar,
Checkbox, Context,
}; };
use epaint::Color32; use epaint::Color32;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
@ -52,15 +53,21 @@ pub struct FunctionEntry {
/// If displaying derivatives are enabled (note, they are still calculated for other purposes) /// If displaying derivatives are enabled (note, they are still calculated for other purposes)
pub derivative: bool, pub derivative: bool,
pub nth_derviative: bool,
back_data: Vec<Value>, back_data: Vec<Value>,
integral_data: Option<(Vec<Bar>, f64)>, integral_data: Option<(Vec<Bar>, f64)>,
derivative_data: Vec<Value>, derivative_data: Vec<Value>,
extrema_data: Vec<Value>, extrema_data: Vec<Value>,
root_data: Vec<Value>, root_data: Vec<Value>,
nth_derivative_data: Option<Vec<Value>>,
autocomplete: AutoComplete<'static>, autocomplete: AutoComplete<'static>,
test_result: Option<String>, test_result: Option<String>,
curr_nth: usize,
pub settings_opened: bool,
} }
impl Default for FunctionEntry { impl Default for FunctionEntry {
@ -73,13 +80,17 @@ impl Default for FunctionEntry {
max_x: 1.0, max_x: 1.0,
integral: false, integral: false,
derivative: false, derivative: false,
nth_derviative: false,
back_data: Vec::new(), back_data: Vec::new(),
integral_data: None, integral_data: None,
derivative_data: Vec::new(), derivative_data: Vec::new(),
extrema_data: Vec::new(), extrema_data: Vec::new(),
root_data: Vec::new(), root_data: Vec::new(),
nth_derivative_data: None,
autocomplete: AutoComplete::default(), autocomplete: AutoComplete::default(),
test_result: None, test_result: None,
curr_nth: 3,
settings_opened: false,
} }
} }
} }
@ -94,6 +105,32 @@ impl FunctionEntry {
self.update_string(&output_string); self.update_string(&output_string);
} }
pub fn settings(&mut self, ctx: &Context) {
let mut invalidate_nth = false;
egui::Window::new(format!("Settings: {}", self.raw_func_str))
.open(&mut self.settings_opened)
.default_pos([200.0, 200.0])
.resizable(false)
.collapsible(false)
.show(ctx, |ui| {
ui.add(Checkbox::new(
&mut self.nth_derviative,
"Display Nth Derivative",
));
if ui
.add(egui::Slider::new(&mut self.curr_nth, 3..=5).text("Nth Derivative"))
.changed()
{
invalidate_nth = true;
}
});
if invalidate_nth {
self.invalidate_nth();
}
}
/// Get function's cached test result /// Get function's cached test result
pub fn get_test_result(&self) -> &Option<String> { &self.test_result } pub fn get_test_result(&self) -> &Option<String> { &self.test_result }
@ -263,6 +300,24 @@ impl FunctionEntry {
} else { } else {
self.invalidate_derivative(); self.invalidate_derivative();
} }
if self.nth_derviative && let Some(nth_derivative_data) = &self.nth_derivative_data {
let new_nth_derivative_data: Vec<Value> = dyn_iter(&resolution_iter)
.map(|x| {
if let Some(i) = x_data.get_index(x) {
(*nth_derivative_data)[i]
} else {
Value::new(*x, self.function.get_nth_derivative(self.curr_nth, *x))
}
})
.collect();
debug_assert_eq!(new_nth_derivative_data.len(), settings.plot_width + 1);
self.nth_derivative_data = Some(new_nth_derivative_data);
} else {
self.invalidate_nth();
}
} else { } else {
self.invalidate_back(); self.invalidate_back();
self.invalidate_derivative(); self.invalidate_derivative();
@ -287,6 +342,14 @@ impl FunctionEntry {
debug_assert_eq!(data.len(), settings.plot_width + 1); debug_assert_eq!(data.len(), settings.plot_width + 1);
self.derivative_data = data; self.derivative_data = data;
} }
if self.nth_derviative && self.nth_derivative_data.is_none() {
let data: Vec<Value> = dyn_iter(&resolution_iter)
.map(|x| Value::new(*x, self.function.get_nth_derivative(self.curr_nth, *x)))
.collect();
debug_assert_eq!(data.len(), settings.plot_width + 1);
self.nth_derivative_data = Some(data);
}
} }
if self.integral { if self.integral {
@ -370,6 +433,15 @@ impl FunctionEntry {
); );
} }
if self.nth_derviative && let Some(nth_derviative) = &self.nth_derivative_data {
plot_ui.line(
(*nth_derviative)
.to_line()
.color(Color32::DARK_RED)
.name(self.function.get_nth_derivative_str()),
);
}
// Plot integral data // Plot integral data
match &self.integral_data { match &self.integral_data {
Some(integral_data) => { Some(integral_data) => {
@ -391,6 +463,7 @@ impl FunctionEntry {
self.invalidate_back(); self.invalidate_back();
self.invalidate_integral(); self.invalidate_integral();
self.invalidate_derivative(); self.invalidate_derivative();
self.invalidate_nth();
self.extrema_data.clear(); self.extrema_data.clear();
self.root_data.clear(); self.root_data.clear();
} }
@ -404,6 +477,8 @@ impl FunctionEntry {
/// Invalidate Derivative data /// Invalidate Derivative data
pub fn invalidate_derivative(&mut self) { self.derivative_data.clear(); } pub fn invalidate_derivative(&mut self) { self.derivative_data.clear(); }
pub fn invalidate_nth(&mut self) { self.nth_derivative_data = None }
/// Runs asserts to make sure everything is the expected value /// Runs asserts to make sure everything is the expected value
#[cfg(test)] #[cfg(test)]
pub fn tests( pub fn tests(

View File

@ -464,9 +464,19 @@ impl MathApp {
.clicked(), .clicked(),
); );
function.settings_opened.bitxor_assign(
ui.add(Button::new(""))
.on_hover_text(match function.settings_opened {
true => "Close Settings",
false => "Open Settings",
})
.clicked(),
);
// Contains the function string in a text box that the user can edit // Contains the function string in a text box that the user can edit
function.auto_complete(ui, i as i32) function.auto_complete(ui, i as i32)
}); });
function.settings(ctx);
} }
// Remove function if the user requests it // Remove function if the user requests it

View File

@ -16,6 +16,8 @@ pub struct BackingFunction {
derivative_1_str: String, derivative_1_str: String,
/// f''(x) /// f''(x)
derivative_2: FlatEx<f64>, derivative_2: FlatEx<f64>,
nth_derivative: Option<(usize, FlatEx<f64>, String)>,
} }
impl BackingFunction { impl BackingFunction {
@ -66,6 +68,7 @@ impl BackingFunction {
derivative_1, derivative_1,
derivative_1_str, derivative_1_str,
derivative_2, derivative_2,
nth_derivative: None,
}) })
} }
@ -84,6 +87,34 @@ impl BackingFunction {
pub fn get_derivative_2(&self, x: f64) -> f64 { pub fn get_derivative_2(&self, x: f64) -> f64 {
self.derivative_2.eval(&[x]).unwrap_or(f64::NAN) self.derivative_2.eval(&[x]).unwrap_or(f64::NAN)
} }
pub fn get_nth_derivative_str(&self) -> &str { &self.nth_derivative.as_ref().unwrap().2 }
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]).unwrap_or(f64::NAN);
}
}
let new_func = self
.function
.partial_iter((1..=n).map(|_| 0).collect::<Vec<usize>>().iter())
.unwrap_or_else(|_| EMPTY_FUNCTION.clone());
self.nth_derivative = Some((
n,
new_func.clone(),
new_func.unparse().to_owned().replace("{x}", "x"),
));
return new_func.eval(&[x]).unwrap_or(f64::NAN);
}
}
}
} }
const VALID_VARIABLES: [char; 5] = ['x', 'X', 'e', 'E', 'π']; const VALID_VARIABLES: [char; 5] = ['x', 'X', 'e', 'E', 'π'];