refactoring of function and derivative code
This commit is contained in:
parent
d16b5c882a
commit
2e55768972
117
src/function.rs
117
src/function.rs
@ -1,7 +1,7 @@
|
|||||||
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
#![allow(clippy::too_many_arguments)] // Clippy, shut
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use crate::misc::{debug_log, SteppedVector};
|
use crate::misc::{debug_log, BackingFunction, BoxFunction, SteppedVector, EPSILON};
|
||||||
|
|
||||||
use eframe::egui::{
|
use eframe::egui::{
|
||||||
plot::{BarChart, Line, Value, Values},
|
plot::{BarChart, Line, Value, Values},
|
||||||
@ -22,7 +22,7 @@ impl fmt::Display for RiemannSum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
function: Box<dyn Fn(f64) -> f64>,
|
function: BackingFunction,
|
||||||
func_str: String,
|
func_str: String,
|
||||||
min_x: f64,
|
min_x: f64,
|
||||||
max_x: f64,
|
max_x: f64,
|
||||||
@ -34,6 +34,7 @@ pub struct Function {
|
|||||||
|
|
||||||
pub(crate) integral: bool,
|
pub(crate) integral: bool,
|
||||||
pub(crate) derivative: bool,
|
pub(crate) derivative: bool,
|
||||||
|
pub(crate) nth_derivative: u64,
|
||||||
integral_min_x: f64,
|
integral_min_x: f64,
|
||||||
integral_max_x: f64,
|
integral_max_x: f64,
|
||||||
integral_num: usize,
|
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
|
// 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) }
|
fn default_function(x: f64) -> f64 { x.powi(2) }
|
||||||
|
|
||||||
const EPSILON: f64 = 5.0e-7;
|
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
// Creates Empty Function instance
|
// Creates Empty Function instance
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
function: Box::new(default_function),
|
function: BackingFunction::new(Box::new(default_function)),
|
||||||
func_str: String::new(),
|
func_str: String::new(),
|
||||||
min_x: -1.0,
|
min_x: -1.0,
|
||||||
max_x: 1.0,
|
max_x: 1.0,
|
||||||
@ -59,6 +58,7 @@ impl Function {
|
|||||||
derivative_cache: None,
|
derivative_cache: None,
|
||||||
integral: false,
|
integral: false,
|
||||||
derivative: false,
|
derivative: false,
|
||||||
|
nth_derivative: 1,
|
||||||
integral_min_x: f64::NAN,
|
integral_min_x: f64::NAN,
|
||||||
integral_max_x: f64::NAN,
|
integral_max_x: f64::NAN,
|
||||||
integral_num: 0,
|
integral_num: 0,
|
||||||
@ -67,7 +67,7 @@ impl Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Runs the internal function to get values
|
// 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(
|
pub fn update(
|
||||||
&mut self, func_str: String, integral: bool, derivative: bool, integral_min_x: Option<f64>,
|
&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 the function string changes, just wipe and restart from scratch
|
||||||
if func_str != self.func_str {
|
if func_str != self.func_str {
|
||||||
self.func_str = func_str.clone();
|
self.func_str = func_str.clone();
|
||||||
self.function = Box::new({
|
self.function = BackingFunction::new(Box::new({
|
||||||
let expr: Expr = func_str.parse().unwrap();
|
let expr: Expr = func_str.parse().unwrap();
|
||||||
expr.bind("x").unwrap()
|
expr.bind("x").unwrap()
|
||||||
});
|
}));
|
||||||
self.back_cache = None;
|
self.back_cache = None;
|
||||||
self.front_cache = None;
|
self.front_cache = None;
|
||||||
self.derivative_cache = None;
|
self.derivative_cache = None;
|
||||||
@ -143,10 +143,7 @@ impl Function {
|
|||||||
if let Some(i) = x_data.get_index(x) {
|
if let Some(i) = x_data.get_index(x) {
|
||||||
derivative_cache[i]
|
derivative_cache[i]
|
||||||
} else {
|
} else {
|
||||||
let (x1, x2) = (x - EPSILON, x + EPSILON);
|
Value::new(x, self.function.derivative(x, self.nth_derivative))
|
||||||
let (y1, y2) = (self.run_func(x1), self.run_func(x2));
|
|
||||||
let slope = (y2 - y1) / (EPSILON * 2.0);
|
|
||||||
Value::new(x, slope)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
@ -299,29 +296,39 @@ impl Function {
|
|||||||
self.integral = integral;
|
self.integral = integral;
|
||||||
self
|
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]
|
#[test]
|
||||||
fn left_function_test() {
|
fn left_function_test() {
|
||||||
let pixel_width = 10;
|
|
||||||
let integral_num = 10;
|
let integral_num = 10;
|
||||||
|
let pixel_width = 10;
|
||||||
|
|
||||||
let mut function = Function {
|
let mut function = Function::empty()
|
||||||
function: Box::new(default_function),
|
.update_riemann(RiemannSum::Left)
|
||||||
func_str: String::from("x^2"),
|
.pixel_width(pixel_width)
|
||||||
min_x: -1.0,
|
.integral_num(integral_num)
|
||||||
max_x: 1.0,
|
.integral_bounds(-1.0, 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 back_values_target = vec![
|
let back_values_target = vec![
|
||||||
(-1.0, 1.0),
|
(-1.0, 1.0),
|
||||||
@ -431,31 +438,20 @@ fn left_function_test() {
|
|||||||
assert_eq!(vec_integral.len(), integral_num);
|
assert_eq!(vec_integral.len(), integral_num);
|
||||||
|
|
||||||
assert_eq!(vec_integral, vec_integral_target);
|
assert_eq!(vec_integral, vec_integral_target);
|
||||||
assert_eq!(vec_integral[vec_integral.len()-1].1, area_target);
|
assert_eq!(vec_integral[vec_integral.len() - 1].1, area_target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn middle_function_test() {
|
fn middle_function_test() {
|
||||||
let pixel_width = 10;
|
|
||||||
let integral_num = 10;
|
let integral_num = 10;
|
||||||
|
let pixel_width = 10;
|
||||||
|
|
||||||
let mut function = Function {
|
let mut function = Function::empty()
|
||||||
function: Box::new(default_function),
|
.update_riemann(RiemannSum::Middle)
|
||||||
func_str: String::from("x^2"),
|
.pixel_width(pixel_width)
|
||||||
min_x: -1.0,
|
.integral_num(integral_num)
|
||||||
max_x: 1.0,
|
.integral_bounds(-1.0, 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 back_values_target = vec![
|
let back_values_target = vec![
|
||||||
(-1.0, 1.0),
|
(-1.0, 1.0),
|
||||||
@ -565,31 +561,20 @@ fn middle_function_test() {
|
|||||||
assert_eq!(vec_integral.len(), integral_num);
|
assert_eq!(vec_integral.len(), integral_num);
|
||||||
|
|
||||||
assert_eq!(vec_integral, vec_integral_target);
|
assert_eq!(vec_integral, vec_integral_target);
|
||||||
assert_eq!(vec_integral[vec_integral.len()-1].1, area_target);
|
assert_eq!(vec_integral[vec_integral.len() - 1].1, area_target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn right_function_test() {
|
fn right_function_test() {
|
||||||
let pixel_width = 10;
|
|
||||||
let integral_num = 10;
|
let integral_num = 10;
|
||||||
|
let pixel_width = 10;
|
||||||
|
|
||||||
let mut function = Function {
|
let mut function = Function::empty()
|
||||||
function: Box::new(default_function),
|
.update_riemann(RiemannSum::Right)
|
||||||
func_str: String::from("x^2"),
|
.pixel_width(pixel_width)
|
||||||
min_x: -1.0,
|
.integral_num(integral_num)
|
||||||
max_x: 1.0,
|
.integral_bounds(-1.0, 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 back_values_target = vec![
|
let back_values_target = vec![
|
||||||
(-1.0, 1.0),
|
(-1.0, 1.0),
|
||||||
@ -699,6 +684,6 @@ fn right_function_test() {
|
|||||||
assert_eq!(vec_integral.len(), integral_num);
|
assert_eq!(vec_integral.len(), integral_num);
|
||||||
|
|
||||||
assert_eq!(vec_integral, vec_integral_target);
|
assert_eq!(vec_integral, vec_integral_target);
|
||||||
assert_eq!(vec_integral[vec_integral.len()-1].1, area_target);
|
assert_eq!(vec_integral[vec_integral.len() - 1].1, area_target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
src/misc.rs
32
src/misc.rs
@ -34,6 +34,38 @@ pub fn debug_log(s: &str) {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn debug_log(_s: &str) {}
|
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.
|
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.
|
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.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user