refactoring of function and derivative code

This commit is contained in:
Simon Gardling 2022-03-04 13:54:25 -05:00
parent d16b5c882a
commit 2e55768972
2 changed files with 83 additions and 66 deletions

View File

@ -1,7 +1,7 @@
#![allow(clippy::too_many_arguments)] // Clippy, shut
#[allow(unused_imports)]
use crate::misc::{debug_log, SteppedVector};
use crate::misc::{debug_log, BackingFunction, BoxFunction, SteppedVector, EPSILON};
use eframe::egui::{
plot::{BarChart, Line, Value, Values},
@ -22,7 +22,7 @@ impl fmt::Display for RiemannSum {
}
pub struct Function {
function: Box<dyn Fn(f64) -> f64>,
function: BackingFunction,
func_str: String,
min_x: f64,
max_x: f64,
@ -34,6 +34,7 @@ pub struct Function {
pub(crate) integral: bool,
pub(crate) derivative: bool,
pub(crate) nth_derivative: u64,
integral_min_x: f64,
integral_max_x: f64,
integral_num: usize,
@ -43,13 +44,11 @@ pub struct Function {
// x^2 function, set here so we don't have to regenerate it every time a new function is made
fn default_function(x: f64) -> f64 { x.powi(2) }
const EPSILON: f64 = 5.0e-7;
impl Function {
// Creates Empty Function instance
pub fn empty() -> Self {
Self {
function: Box::new(default_function),
function: BackingFunction::new(Box::new(default_function)),
func_str: String::new(),
min_x: -1.0,
max_x: 1.0,
@ -59,6 +58,7 @@ impl Function {
derivative_cache: None,
integral: false,
derivative: false,
nth_derivative: 1,
integral_min_x: f64::NAN,
integral_max_x: f64::NAN,
integral_num: 0,
@ -67,7 +67,7 @@ impl Function {
}
// Runs the internal function to get values
fn run_func(&self, x: f64) -> f64 { (self.function)(x) }
fn run_func(&self, x: f64) -> f64 { self.function.get(x) }
pub fn update(
&mut self, func_str: String, integral: bool, derivative: bool, integral_min_x: Option<f64>,
@ -76,10 +76,10 @@ impl Function {
// If the function string changes, just wipe and restart from scratch
if func_str != self.func_str {
self.func_str = func_str.clone();
self.function = Box::new({
self.function = BackingFunction::new(Box::new({
let expr: Expr = func_str.parse().unwrap();
expr.bind("x").unwrap()
});
}));
self.back_cache = None;
self.front_cache = None;
self.derivative_cache = None;
@ -143,10 +143,7 @@ impl Function {
if let Some(i) = x_data.get_index(x) {
derivative_cache[i]
} else {
let (x1, x2) = (x - EPSILON, x + EPSILON);
let (y1, y2) = (self.run_func(x1), self.run_func(x2));
let slope = (y2 - y1) / (EPSILON * 2.0);
Value::new(x, slope)
Value::new(x, self.function.derivative(x, self.nth_derivative))
}
})
.collect(),
@ -299,29 +296,39 @@ impl Function {
self.integral = integral;
self
}
// Toggles integral
pub fn integral_num(mut self, integral_num: usize) -> Self {
self.integral_num = integral_num;
self
}
pub fn pixel_width(mut self, pixel_width: usize) -> Self {
self.pixel_width = pixel_width;
self
}
pub fn integral_bounds(mut self, min_x: f64, max_x: f64) -> Self {
if min_x >= max_x {
panic!("integral_bounds: min_x is larger than max_x");
}
self.integral_min_x = min_x;
self.integral_max_x = max_x;
self
}
}
#[test]
fn left_function_test() {
let pixel_width = 10;
let integral_num = 10;
let pixel_width = 10;
let mut function = Function {
function: Box::new(default_function),
func_str: String::from("x^2"),
min_x: -1.0,
max_x: 1.0,
pixel_width,
back_cache: None,
front_cache: None,
derivative_cache: None,
integral: false,
derivative: false,
integral_min_x: -1.0,
integral_max_x: 1.0,
integral_num,
sum: RiemannSum::Left,
};
let mut function = Function::empty()
.update_riemann(RiemannSum::Left)
.pixel_width(pixel_width)
.integral_num(integral_num)
.integral_bounds(-1.0, 1.0);
let back_values_target = vec![
(-1.0, 1.0),
@ -437,25 +444,14 @@ fn left_function_test() {
#[test]
fn middle_function_test() {
let pixel_width = 10;
let integral_num = 10;
let pixel_width = 10;
let mut function = Function {
function: Box::new(default_function),
func_str: String::from("x^2"),
min_x: -1.0,
max_x: 1.0,
pixel_width,
back_cache: None,
front_cache: None,
derivative_cache: None,
integral: false,
derivative: false,
integral_min_x: -1.0,
integral_max_x: 1.0,
integral_num,
sum: RiemannSum::Middle,
};
let mut function = Function::empty()
.update_riemann(RiemannSum::Middle)
.pixel_width(pixel_width)
.integral_num(integral_num)
.integral_bounds(-1.0, 1.0);
let back_values_target = vec![
(-1.0, 1.0),
@ -571,25 +567,14 @@ fn middle_function_test() {
#[test]
fn right_function_test() {
let pixel_width = 10;
let integral_num = 10;
let pixel_width = 10;
let mut function = Function {
function: Box::new(default_function),
func_str: String::from("x^2"),
min_x: -1.0,
max_x: 1.0,
pixel_width,
back_cache: None,
front_cache: None,
derivative_cache: None,
integral: false,
derivative: false,
integral_min_x: -1.0,
integral_max_x: 1.0,
integral_num,
sum: RiemannSum::Right,
};
let mut function = Function::empty()
.update_riemann(RiemannSum::Right)
.pixel_width(pixel_width)
.integral_num(integral_num)
.integral_bounds(-1.0, 1.0);
let back_values_target = vec![
(-1.0, 1.0),

View File

@ -34,6 +34,38 @@ pub fn debug_log(s: &str) {
#[allow(dead_code)]
pub fn debug_log(_s: &str) {}
pub const EPSILON: f64 = 5.0e-7;
pub type BoxFunction = Box<dyn Fn(f64) -> f64>;
pub struct BackingFunction {
function: BoxFunction,
}
impl BackingFunction {
pub fn new(function: BoxFunction) -> BackingFunction { BackingFunction { function } }
pub fn get(&self, x: f64) -> f64 { (self.function)(x) }
pub fn derivative(&self, x: f64, n: u64) -> f64 {
if n == 0 {
return self.get(x);
}
let (x1, x2) = (x - EPSILON, x + EPSILON);
let (y1, y2) = (self.derivative(x1, n - 1), (self.derivative(x2, n - 1)));
(y2 - y1) / (EPSILON * 2.0)
}
}
impl From<BoxFunction> for BackingFunction {
fn from(boxfunction: BoxFunction) -> BackingFunction {
BackingFunction {
function: boxfunction,
}
}
}
/*
EXTREMELY Janky function that tries to put asterisks in the proper places to be parsed. This is so cursed. But it works, and I hopefully won't ever have to touch it again.
One limitation though, variables with multiple characters like `pi` cannot be multiplied (like `pipipipi` won't result in `pi*pi*pi*pi`). But that's such a niche use case (and that same thing could be done by using exponents) that it doesn't really matter.