From 2d63c1b5f7f63c5dc50fd3f0c6ab6905bc1a4e82 Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Fri, 13 May 2022 11:38:00 -0400 Subject: [PATCH] cleanup and improved function tests --- Cargo.lock | 19 ++++++--- src/function_entry.rs | 92 ++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 4 +- src/main.rs | 6 +++ src/math_app.rs | 2 +- src/misc.rs | 88 +++++++++++++++++++++++------------------ tests/function.rs | 12 +++--- tests/misc.rs | 26 ++++++++---- 8 files changed, 183 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc740ff..f624fe1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -662,7 +662,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "eframe" version = "0.18.0" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "bytemuck", "egui", @@ -682,7 +682,7 @@ dependencies = [ [[package]] name = "egui" version = "0.18.1" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "ahash", "epaint", @@ -693,7 +693,7 @@ dependencies = [ [[package]] name = "egui-winit" version = "0.18.0" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "arboard", "egui", @@ -706,7 +706,7 @@ dependencies = [ [[package]] name = "egui_glow" version = "0.18.1" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "bytemuck", "egui", @@ -726,22 +726,24 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "emath" version = "0.18.0" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "bytemuck", + "libm", "serde", ] [[package]] name = "epaint" version = "0.18.1" -source = "git+https://github.com/Titaniumtown/egui.git#8e62055679973f91ee58fe91cf6495ad28fae2f7" +source = "git+https://github.com/Titaniumtown/egui.git#1797df1670ae95b5da81fb99324531f6737826e9" dependencies = [ "ab_glyph", "ahash", "atomic_refcell", "bytemuck", "emath", + "libm", "nohash-hasher", "parking_lot 0.12.0", "serde", @@ -1146,6 +1148,11 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.2" +source = "git+https://github.com/phip1611/libm.git?branch=const-functions#d447daef3b9a4de4b1aaf6b87f09fbe20f5ded09" + [[package]] name = "libz-sys" version = "1.1.6" diff --git a/src/function_entry.rs b/src/function_entry.rs index f3ca8fc..b50677d 100644 --- a/src/function_entry.rs +++ b/src/function_entry.rs @@ -318,23 +318,23 @@ impl FunctionEntry { Vec>, ) = dyn_iter(&resolution_iter) .map(|x| { - if let Some(i) = x_data.get_index(x) { - return ( + if let Some(i) = x_data.get_index(*x) { + ( self.back_data[i], derivative_required.then(|| self.derivative_data[i]), do_nth_derivative.then(|| unsafe { nth_derivative_data.map(|data| data[i]).unwrap_unchecked() }), - ); + ) } else { - return ( + ( Value::new(*x, self.function.get(*x)), derivative_required .then(|| Value::new(*x, self.function.get_derivative_1(*x))), do_nth_derivative.then(|| { Value::new(*x, self.function.get_nth_derivative(self.curr_nth, *x)) }), - ); + ) } }) .collect::, Option)>>() @@ -556,6 +556,7 @@ impl FunctionEntry { /// Invalidate Derivative data pub fn invalidate_derivative(&mut self) { self.derivative_data.clear(); } + /// Invalidates `n`th derivative data pub fn invalidate_nth(&mut self) { self.nth_derivative_data = None } /// Runs asserts to make sure everything is the expected value @@ -566,7 +567,6 @@ impl FunctionEntry { ) { { self.calculate(&min_x, &max_x, true, &settings); - let back_target = back_target; assert!(!self.back_data.is_empty()); assert_eq!(self.back_data.len(), settings.plot_width + 1); let back_vec_tuple = self.back_data.to_tuple(); @@ -585,6 +585,86 @@ impl FunctionEntry { assert_eq!(self.integral_data.clone().unwrap().1, area_target); } + { + self.calculate(&(min_x + 1.0), &(max_x + 1.0), true, &settings); + + assert_eq!( + self.derivative_data + .to_tuple() + .iter() + .take(6) + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>(), + derivative_target + .iter() + .rev() + .take(6) + .rev() + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>() + ); + + assert_eq!( + self.back_data + .to_tuple() + .iter() + .take(6) + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>(), + back_target + .iter() + .rev() + .take(6) + .rev() + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>() + ); + } + + { + self.calculate(&(min_x - 1.0), &(max_x - 1.0), true, &settings); + + assert_eq!( + self.derivative_data + .to_tuple() + .iter() + .rev() + .take(6) + .rev() + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>(), + derivative_target + .iter() + .take(6) + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>() + ); + + assert_eq!( + self.back_data + .to_tuple() + .iter() + .rev() + .take(6) + .rev() + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>(), + back_target + .iter() + .take(6) + .cloned() + .map(|(a, b)| (a.round(), b.round())) // round to account for floating point issues + .collect::>() + ); + } + { self.update_string("sin(x)"); assert!(self.get_test_result().is_none()); diff --git a/src/lib.rs b/src/lib.rs index 6e51e1d..9ddba95 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ #![feature(const_default_impls)] #![feature(const_fn_floating_point_arithmetic)] #![feature(const_assume)] +#![feature(const_option_ext)] +#![feature(const_slice_index)] #[macro_use] extern crate static_assertions; @@ -27,7 +29,7 @@ pub use crate::{ math_app::AppSettings, misc::{ decimal_round, format_bytes, hashed_storage_create, hashed_storage_read, - option_vec_printer, resolution_helper, SteppedVector, + option_vec_printer, resolution_helper, step_helper, SteppedVector, }, }; diff --git a/src/main.rs b/src/main.rs index abc20a5..e28b930 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,12 @@ #![feature(stmt_expr_attributes)] #![feature(const_trait_impl)] #![feature(core_intrinsics)] +#![feature(const_convert)] +#![feature(const_default_impls)] +#![feature(const_fn_floating_point_arithmetic)] +#![feature(const_assume)] +#![feature(const_option_ext)] +#![feature(const_slice_index)] #[macro_use] extern crate static_assertions; diff --git a/src/math_app.rs b/src/math_app.rs index 15b8f16..0d17098 100644 --- a/src/math_app.rs +++ b/src/math_app.rs @@ -240,7 +240,7 @@ impl MathApp { loading_element.remove(); Self { - functions: get_functions().unwrap_or(FunctionManager::default()), + functions: get_functions().unwrap_or_default(), last_info: (vec![None], None), dark_mode: true, // dark mode is default and is previously set text: data.text, diff --git a/src/misc.rs b/src/misc.rs index c763c55..4999f1d 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -3,9 +3,6 @@ use std::intrinsics::assume; use eframe::egui::plot::{Line, Points, Value, Values}; use itertools::Itertools; -#[cfg(threading)] -use rayon::prelude::*; - #[cfg(not(threading))] #[inline] pub fn dyn_iter<'a, T>(input: &'a [T]) -> impl Iterator @@ -21,6 +18,8 @@ pub fn dyn_iter<'a, I>(input: &'a I) -> <&'a I as IntoParallelIterator>::Iter where &'a I: IntoParallelIterator, { + use rayon::prelude::*; + input.par_iter() } @@ -39,6 +38,7 @@ pub fn dyn_mut_iter<'a, I>(input: &'a mut I) -> <&'a mut I as IntoParallelIterat where &'a mut I: IntoParallelIterator, { + use rayon::prelude::*; input.par_iter_mut() } @@ -79,37 +79,40 @@ pub struct SteppedVector<'a> { /// Actual data being referenced. HAS to be sorted from minimum to maximum data: &'a [f64], - /// Minimum value - min: f64, - - /// Maximum value - max: f64, - /// Since all entries in `data` are evenly spaced, this field stores the step between 2 adjacent elements step: f64, } impl<'a> SteppedVector<'a> { /// 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 { - // 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) { + pub fn get_index(&self, x: f64) -> Option { + unsafe { + assume(!self.step.is_nan()); + assume(self.step > 0.0); + assume(self.step.is_sign_positive()); + } + + let max = self.get_max(); + if &x > max { return None; } - if x == &self.min { - return Some(0); + let min = self.get_min(); + if min > &x { + return None; } - if x == &self.max { + if &x == min { + return Some(0); + } else if &x == max { return Some(self.data.len() - 1); } // Do some math in order to calculate the expected index value - let possible_i = ((x - self.min) / self.step) as usize; + let possible_i = ((x - min).abs() / self.step) as usize; // 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 { @@ -118,14 +121,26 @@ impl<'a> SteppedVector<'a> { } } - #[allow(dead_code)] - pub const fn get_min(&self) -> f64 { self.min } + #[inline] + pub const fn get_min(&self) -> &f64 { + unsafe { + assume(!self.data.is_empty()); + assume(!self.data.is_empty()); + self.data.get_unchecked(0) + } + } + + #[inline] + pub const fn get_max(&self) -> &f64 { + unsafe { + assume(!self.data.is_empty()); + assume(!self.data.is_empty()); + self.data.last().unwrap_unchecked() + } + } #[allow(dead_code)] - pub const fn get_max(&self) -> f64 { self.max } - - #[allow(dead_code)] - pub fn get_data(&self) -> &'a [f64] { &self.data } + pub fn get_data(&self) -> &'a [f64] { self.data } } // Convert `&[f64]` into [`SteppedVector`] @@ -140,8 +155,8 @@ impl<'a> From<&'a [f64]> for SteppedVector<'a> { } // length of data subtracted by 1 (represents the maximum index value) - let max: f64 = data[data.len() - 1]; // The max value should be the first element - let min: f64 = data[0]; // The minimum value should be the last element + let max: f64 = data[data.len() - 1]; // The max value should be the last element + let min: f64 = data[0]; // The minimum value should be the first element debug_assert!(max > min); @@ -149,12 +164,7 @@ impl<'a> From<&'a [f64]> for SteppedVector<'a> { let step = (max - min).abs() / (data.len() as f64); // Create and return the struct - SteppedVector { - data, - min, - max, - step, - } + SteppedVector { data, step } } } @@ -200,7 +210,7 @@ pub fn newtons_method_helper( unsafe { assume(!data.is_empty()); - assume(data.len() > 0); + assume(!data.is_empty()); } data.iter() @@ -255,7 +265,7 @@ where unsafe { assume(!data.is_empty()); - assume(data.len() > 0); + assume(!data.is_empty()); } let max_i: i32 = (data.len() as i32) - 1; @@ -300,10 +310,10 @@ const HASH_LENGTH: usize = 8; #[allow(dead_code)] pub fn hashed_storage_create(hash: &[u8], data: &[u8]) -> String { debug_assert_eq!(hash.len(), HASH_LENGTH); - debug_assert!(data.len() > 0); + debug_assert!(!data.is_empty()); unsafe { - assume(data.len() > 0); + assume(!data.is_empty()); assume(!data.is_empty()); assume(hash.len() == HASH_LENGTH); assume(!hash.is_empty()); @@ -322,7 +332,7 @@ pub fn hashed_storage_read(data: String) -> (String, Vec) { debug_assert!(data.len() > HASH_LENGTH); unsafe { assume(!data.is_empty()); - assume(data.len() > 0); + assume(!data.is_empty()); } // can't use data.as_bytes() here for some reason, seems to break on wasm? @@ -330,14 +340,14 @@ pub fn hashed_storage_read(data: String) -> (String, Vec) { let (hash, cached_data) = decoded_1.split_at(8); debug_assert_eq!(hash.len(), HASH_LENGTH); - debug_assert!(cached_data.len() > 0); + debug_assert!(!cached_data.is_empty()); unsafe { assume(!cached_data.is_empty()); - assume(cached_data.len() > 0); + assume(!cached_data.is_empty()); assume(!hash.is_empty()); - assume(hash.len() > 0); + assume(!hash.is_empty()); } ( diff --git a/tests/function.rs b/tests/function.rs index dc6d593..bd03b63 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -62,8 +62,10 @@ fn do_test(sum: Riemann, area_target: f64) { } #[test] -fn function_entries() { - do_test(Riemann::Left, 0.9600000000000001); - do_test(Riemann::Middle, 0.92); - do_test(Riemann::Right, 0.8800000000000001); -} +fn left_function() { do_test(Riemann::Left, 0.9600000000000001); } + +#[test] +fn middle_function() { do_test(Riemann::Middle, 0.92); } + +#[test] +fn right_function() { do_test(Riemann::Right, 0.8800000000000001); } diff --git a/tests/misc.rs b/tests/misc.rs index 3a63451..4de7e66 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -7,23 +7,23 @@ fn stepped_vector() { let max: i32 = 1000; let data: Vec = (min..=max).map(|x| x as f64).collect(); let len_data = data.len(); - let stepped_vector: SteppedVector = data.as_slice().into(); + let stepped_vector: SteppedVector = SteppedVector::from(data.as_slice()); - assert_eq!(stepped_vector.get_min(), min as f64); - assert_eq!(stepped_vector.get_max(), max as f64); + 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 @@ -65,6 +65,16 @@ fn resolution_helper() { assert_eq!(resolution_helper(3, &-2.0, &1.0), vec![-2.0, -1.0, 0.0]); } +#[test] +fn step_helper() { + use ytbn_graphing_software::step_helper; + + assert_eq!( + step_helper(10, &2.0, &3.0), + vec![2.0, 5.0, 8.0, 11.0, 14.0, 17.0, 20.0, 23.0, 26.0, 29.0] + ); +} + /// Tests [`option_vec_printer`] #[test] fn option_vec_printer() {