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

@@ -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>`