cleanup + further optimize split_function_chars

This commit is contained in:
Simon Gardling 2022-05-07 02:19:52 -04:00
parent bf58e82c80
commit 79782d84b1
8 changed files with 89 additions and 82 deletions

View File

@ -20,3 +20,4 @@
10. Create actual icon(s) for PWA/favicon (using placeholder from eframe_template)
11. Fix mobile text input
12. Don't use json for text storage
13. Write custom plotter

View File

@ -1,7 +1,7 @@
use exmex::prelude::*;
#[derive(Clone)]
pub struct FlatExWrapper {
pub(crate) struct FlatExWrapper {
func: Option<FlatEx<f64>>,
}
@ -172,10 +172,18 @@ fn prettyify_function_str(func: &str) -> String {
}
}
pub const VALID_VARIABLES: [char; 3] = ['x', 'e', 'π'];
// pub const VALID_VARIABLES: [char; 3] = ['x', 'e', 'π'];
/// Case insensitive checks for if `c` is a character used to represent a variable
#[inline]
pub fn is_variable(c: &char) -> bool { VALID_VARIABLES.contains(&c.to_ascii_lowercase()) }
pub const fn is_variable(c: &char) -> bool {
match c.to_ascii_lowercase() {
'x' => true,
'e' => true,
'π' => true,
_ => false,
}
}
/// Adds asterisks where needed in a function
pub fn process_func_str(function_in: &str) -> String {

View File

@ -1,4 +1,4 @@
use std::{intrinsics::assume, mem};
use std::intrinsics::assume;
use crate::parsing::is_variable;
@ -28,7 +28,6 @@ pub fn split_function(input: &str) -> Vec<String> {
.collect::<Vec<String>>()
}
// __REVIEW__
pub fn split_function_chars(chars: &[char]) -> Vec<String> {
if chars.is_empty() {
return Vec::new();
@ -45,11 +44,12 @@ pub fn split_function_chars(chars: &[char]) -> Vec<String> {
}
// Resulting split-up data
let mut data: Vec<Vec<&char>> = Vec::with_capacity(chars.len());
let mut data: Vec<String> = Vec::with_capacity(chars.len());
// Buffer used to store data ready to be appended
let mut buffer: Vec<&char> = Vec::with_capacity(chars.len());
// Need to start out with an empty string
data.push(String::new());
/// Used to store info about a character
struct BoolSlice {
closing_parens: bool,
number: bool,
@ -60,7 +60,7 @@ pub fn split_function_chars(chars: &[char]) -> Vec<String> {
}
impl BoolSlice {
fn from_char(c: &char, prev_masked_num: bool, prev_masked_var: bool) -> Self {
const fn from_char(c: &char, prev_masked_num: bool, prev_masked_var: bool) -> Self {
let isnumber = c.is_ascii_digit();
let isvariable = is_variable(c);
Self {
@ -81,11 +81,42 @@ pub fn split_function_chars(chars: &[char]) -> Vec<String> {
const fn is_variable(&self) -> bool { self.variable && !self.masked_var }
const fn is_number(&self) -> bool { self.number && !self.masked_num }
const fn splitable(&self, c: &char, other: &BoolSlice) -> bool {
if other.closing_parens {
// Cases like `)x`, `)2`, and `)(`
return (*c == '(')
| (self.letter && !self.is_variable())
| self.is_variable() | self.is_number();
} else if *c == '(' {
// Cases like `x(` and `2(`
return (other.is_variable() | other.is_number()) && !other.letter;
} else if other.is_number() {
// Cases like `2x` and `2sin(x)`
return self.is_variable() | self.letter;
} else if self.is_variable() | self.letter {
// Cases like `e2` and `xx`
return other.is_number()
| (other.is_variable() && self.is_variable())
| other.is_variable();
} else if (self.is_number() | self.letter | self.is_variable())
&& (other.is_number() | other.letter)
{
return true;
} else if self.is_number() && other.is_variable() {
// Cases like `x2`
return true;
} else {
return false;
}
}
}
// Setup first char here
let mut prev_char: BoolSlice = BoolSlice::from_char(&chars[0], false, false);
buffer.push(&chars[0]);
let mut last = unsafe { data.last_mut().unwrap_unchecked() };
last.push(chars[0]);
// Iterate through all chars excluding the first one
for c in chars.iter().skip(1) {
@ -115,59 +146,19 @@ pub fn split_function_chars(chars: &[char]) -> Vec<String> {
}
}
let mut do_split = false;
if prev_char.closing_parens {
// Cases like `)x`, `)2`, and `)(`
do_split = (c == &'(')
| (curr_c.letter && !curr_c.is_variable())
| curr_c.is_variable()
| curr_c.is_number();
} else if c == &'(' {
// Cases like `x(` and `2(`
do_split = (prev_char.is_variable() | prev_char.is_number()) && !prev_char.letter;
} else if prev_char.is_number() {
// Cases like `2x` and `2sin(x)`
do_split = curr_c.is_variable() | curr_c.letter;
} else if curr_c.is_variable() | curr_c.letter {
// Cases like `e2` and `xx`
do_split = prev_char.is_number()
| (prev_char.is_variable() && curr_c.is_variable())
| prev_char.is_variable()
} else if (curr_c.is_number() | curr_c.letter | curr_c.is_variable())
&& (prev_char.is_number() | prev_char.letter)
{
do_split = true;
} else if curr_c.is_number() && prev_char.is_variable() {
// Cases like `x2`
do_split = true;
}
// Append split
if do_split {
data.push(Vec::new());
// Don't deinitialize `buffer`, simply swap data between the new last element of `data` with buffer!
unsafe {
mem::swap(data.last_mut().unwrap_unchecked(), &mut buffer);
}
if curr_c.splitable(c, &prev_char) {
data.push(String::new());
last = unsafe { data.last_mut().unwrap_unchecked() };
}
// Add character to buffer
buffer.push(c);
last.push(*c);
// Move current character data to `prev_char`
prev_char = curr_c;
}
// If there is still data in the buffer, append it
if !buffer.is_empty() {
data.push(buffer);
}
data.into_iter()
.map(|e| e.iter().map(|c| *c).collect::<String>())
.collect::<Vec<String>>()
data
}
/// Generate a hint based on the input `input`, returns an `Option<String>`

View File

@ -68,30 +68,30 @@ pub struct FunctionEntry {
impl const Default for FunctionEntry {
/// Creates default FunctionEntry instance (which is empty)
fn default() -> FunctionEntry {
FunctionEntry {
function: BackingFunction::EMPTY,
raw_func_str: String::new(),
min_x: -1.0,
max_x: 1.0,
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::default(),
test_result: None,
curr_nth: 3,
settings_opened: false,
}
}
fn default() -> FunctionEntry { FunctionEntry::EMPTY }
}
impl FunctionEntry {
pub const EMPTY: FunctionEntry = FunctionEntry {
function: BackingFunction::EMPTY,
raw_func_str: String::new(),
min_x: -1.0,
max_x: 1.0,
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::default(),
test_result: None,
curr_nth: 3,
settings_opened: false,
};
pub fn settings_window(&mut self, ctx: &Context) {
let mut invalidate_nth = false;
egui::Window::new(format!("Settings: {}", self.raw_func_str))
@ -605,7 +605,7 @@ mod tests {
fn do_test(sum: Riemann, area_target: f64) {
let settings = app_settings_constructor(sum, -1.0, 1.0, 10, 10);
let mut function = FunctionEntry::default();
let mut function = FunctionEntry::EMPTY;
function.update_string("x^2");
function.integral = true;
function.derivative = true;

View File

@ -14,7 +14,10 @@ pub struct FunctionManager {
impl Default for FunctionManager {
fn default() -> Self {
Self {
functions: vec![(Uuid::new_v4(), FunctionEntry::default())],
functions: vec![(
uuid!("684fc8be-4ba0-408d-96ef-480b0642126f"), // Random uuid here to avoid call to `Uuid::new_v4()`
FunctionEntry::EMPTY,
)],
}
}
}
@ -196,10 +199,7 @@ impl FunctionManager {
}
}
pub fn new_function(&mut self) {
self.functions
.push((Uuid::new_v4(), FunctionEntry::default()));
}
pub fn new_function(&mut self) { self.functions.push((Uuid::new_v4(), FunctionEntry::EMPTY)); }
pub fn any_using_integral(&self) -> bool {
self.functions.iter().any(|(_, func)| func.integral)

View File

@ -9,6 +9,9 @@
#[macro_use]
extern crate static_assertions;
#[macro_use]
extern crate uuid;
mod consts;
mod function_entry;
mod function_manager;

View File

@ -7,6 +7,9 @@
#[macro_use]
extern crate static_assertions;
#[macro_use]
extern crate uuid;
mod consts;
mod function_entry;
mod function_manager;

View File

@ -107,6 +107,7 @@ pub struct MathApp {
/// Contains the list of Areas calculated (the vector of f64) and time it took for the last frame (the Duration). Stored in a Tuple.
last_info: (Vec<Option<f64>>, Option<Duration>),
/// Whether or not dark mode is enabled
dark_mode: bool,
/// Stores opened windows/elements for later reference