number_diff/utils/
parse.rs

1use crate::Error;
2use std::{
3    f64::consts::{E, PI},
4    sync::Arc,
5};
6
7use crate::Elementary::{self, *};
8
9impl<'a> From<&'a str> for Elementary {
10    fn from(value: &'a str) -> Self {
11        let value: String = value.split_whitespace().collect();
12        Self::to_elementary(&value).unwrap()
13    }
14}
15impl Elementary {
16    fn split_function(value: &str) -> Vec<&str> {
17        let mut interp_slice: Vec<&str> = value.split("").collect();
18        // remove the first and last element because they are just empty string slices
19        interp_slice.remove(0);
20        interp_slice.pop();
21
22        let mut chunks: Vec<&str> = Vec::new();
23        let mut open_parenthesis = -1;
24
25        let mut cut_index = 0;
26
27        let mut skip = 0;
28        for i in 0..interp_slice.len() {
29            // if items need to be skipped (because of the implementation of constants)
30            if skip > 0 {
31                skip -= 1;
32                continue;
33            }
34
35            if interp_slice[i] == "(" {
36                // this is for the first case of an opening parenthesis. Note that we cannot start
37                // at 0 since that would match the case for closing an outer parenthesis
38                if open_parenthesis == -1 {
39                    open_parenthesis = 1;
40                } else {
41                    // for all other cases, however, the number of open parentheses just goes up by
42                    // one
43                    open_parenthesis += 1;
44                }
45            } else if interp_slice[i] == ")" {
46                open_parenthesis -= 1
47            }
48
49            // check if outer parenthesis has been closed
50            if open_parenthesis == 0 {
51                chunks.push(&value[cut_index..=i]);
52
53                // set new cut index
54                cut_index = i + 1;
55
56                // reset parenthesis
57                open_parenthesis = -1;
58            }
59
60            // detect operations and constants
61            if open_parenthesis == -1 {
62                if interp_slice[i] == "+"
63                    || interp_slice[i] == "-"
64                    || interp_slice[i] == "*"
65                    || interp_slice[i] == "/"
66                    || interp_slice[i] == "^"
67                {
68                    chunks.push(interp_slice[i]);
69                    cut_index = i + 1;
70                } else if interp_slice[i] == "!" {
71                    chunks.push(&value[cut_index..=i]);
72                    cut_index = i + 1;
73                } else if interp_slice[i] == "x" {
74                    chunks.push(&value[cut_index..=i]);
75                    cut_index = i + 1;
76                } else if interp_slice[i] == "e" {
77                    chunks.push(&value[cut_index..=i]);
78                    cut_index = i + 1;
79                } else if interp_slice[i] == "pi" || interp_slice[i] == "π" {
80                    chunks.push("pi");
81                    cut_index = i + 1;
82                } else {
83                    // checking for numbers
84                    if let Ok(_) = &value[cut_index..=i].parse::<f64>() {
85                        // find the index at which the number ends
86                        let mut last_index = i;
87                        'index: for j in i + 1..=interp_slice.len() {
88                            if let Ok(_) = &value[cut_index..j].parse::<f64>() {
89                                last_index = j - 1;
90                            } else {
91                                break 'index;
92                            }
93                        }
94
95                        // push the whole number
96                        chunks.push(&value[cut_index..=last_index]);
97
98                        // the next couple of indexes must be skipped in order to avoid parsing of
99                        // individual digits
100                        skip = last_index - i;
101                        // by setting skip to the number of difference between the current index
102                        // and the index at which the number ends
103                        cut_index = last_index + 1;
104                    }
105                }
106            }
107        }
108
109        if chunks.is_empty() {
110            chunks.push(value);
111        }
112
113        chunks
114    }
115
116    fn to_elementary(string: &str) -> Result<Self, Error> {
117        let strings = Self::split_function(string);
118
119        let mut functions: Vec<ElemRef> = strings
120            .clone()
121            .iter()
122            .map(|s| Self::parse_function(s).unwrap())
123            .collect();
124
125        let mut iteration = 0;
126
127        // order of operations
128        while functions.len() != 1 {
129            if iteration >= 10000 {
130                return Err(Error::ParseError(String::from(format!(
131                    "Iteration limit reached while parsing function: {string}",
132                ))));
133            } else {
134                iteration += 1;
135            }
136            // first in the order of operations is powers (seeing as parentheses are handled as a
137            // separate case)
138            if functions.contains(&ElemRef::Pow) {
139                for i in (0..functions.len()).rev() {
140                    // find the index of the last power (because we treat this case from right to
141                    // left)
142                    if i >= functions.len() {
143                        continue;
144                    }
145                    if functions[i] == ElemRef::Pow {
146                        let replacement_func = ElemRef::Function(Pow(
147                            Arc::new(functions[i - 1].clone().convert()?),
148                            Arc::new(functions[i + 1].clone().convert()?),
149                        ));
150                        functions.remove(i + 1);
151                        functions.remove(i);
152                        functions.remove(i - 1);
153                        functions.insert(i - 1, replacement_func);
154                    }
155                }
156
157                continue;
158            }
159
160            // the factorial function (x ↦ x!) is by convention written in "postfix" notation (i.e.
161            // it taeks precedence over normal operations)
162            if functions.contains(&ElemRef::Factorial) {
163                iterate_operation(&mut functions, ElemRef::Factorial)?;
164                continue;
165            }
166
167            // next up in the order of operations is multiplication
168            if functions.contains(&ElemRef::Mul) {
169                iterate_operation(&mut functions, ElemRef::Mul)?;
170                continue;
171            }
172
173            // we also have to handle implied multiplication. Weather this is handled before or
174            // after the explicit multiplication doesn't matter since multiplication is commutative
175            // i.e. a*b = b*a
176
177            // check if there is there are any instances of implied multiplication
178            for i in 0..functions.len() {
179                if i < functions.len() - 1 {
180                    if let (ElemRef::Function(func1), ElemRef::Function(func2)) =
181                        (&functions[i], &functions[i + 1])
182                    {
183                        // multiply the two together
184                        let replacement_func = ElemRef::Function(Mul(
185                            Arc::new(func1.to_owned()),
186                            Arc::new(func2.to_owned()),
187                        ));
188
189                        // remove the functions and replace them with the multiplied function
190                        functions.remove(i + 1);
191                        functions.remove(i);
192                        functions.insert(i, replacement_func);
193                    }
194                }
195            }
196            // next up is division
197            if functions.contains(&ElemRef::Div) {
198                iterate_operation(&mut functions, ElemRef::Div)?;
199                continue;
200            }
201
202            // then addition
203            if functions.contains(&ElemRef::Add) {
204                iterate_operation(&mut functions, ElemRef::Add)?;
205                continue;
206            }
207
208            // and lastly subtracion
209            if functions.contains(&ElemRef::Sub) {
210                iterate_operation(&mut functions, ElemRef::Sub)?;
211                continue;
212            }
213        }
214
215        functions
216            .pop()
217            .expect("Couldn't find a function to parse")
218            .convert()
219    }
220
221    fn parse_function(string: &str) -> Result<ElemRef, Error> {
222        let mut string = string.to_lowercase();
223
224        // unwrap potential parentheses
225        if &string[..1] == "(" {
226            while &string[..1] == "(" {
227                string = string[1..string.len() - 1].to_string();
228            }
229            return Ok(ElemRef::Function(Self::to_elementary(&string)?));
230        }
231
232        // check for special function (independent variable) x, and then check for constants
233        if string == "x" {
234            return Ok(ElemRef::Function(X));
235        } else if let Ok(number) = string.parse::<f64>() {
236            return Ok(ElemRef::Function(Con(number)));
237        }
238
239        match &string[..] {
240            // check in order of operations
241            "^" => Ok(ElemRef::Pow),
242            "*" => Ok(ElemRef::Mul),
243            "/" => Ok(ElemRef::Div),
244            "+" => Ok(ElemRef::Add),
245            "-" => Ok(ElemRef::Sub),
246            // also constants
247            "e" => Ok(ElemRef::Function(Con(E))),
248            "pi" => Ok(ElemRef::Function(Con(PI))),
249            "!" => Ok(ElemRef::Factorial),
250            _ => {
251                // if we do not have an operation, we must have a function consisting of a function
252                // identifier and its contents
253                let (func, cont) = split_first(&string, "(");
254
255                // remove outer parenthesis
256                let mut graphemes: Vec<&str> = cont.split("").collect();
257                // remove the first and last character because they should be parentheses
258                graphemes.remove(0);
259                graphemes.pop();
260                let cont: String = graphemes.iter().map(|x| *x).collect();
261
262                match func {
263                    "sin" => Ok(ElemRef::Function(Sin(Arc::new(Self::to_elementary(
264                        &cont,
265                    )?)))),
266                    "cos" => Ok(ElemRef::Function(Cos(Arc::new(Self::to_elementary(
267                        &cont,
268                    )?)))),
269                    "tan" => Ok(ElemRef::Function(Tan(Arc::new(Self::to_elementary(
270                        &cont,
271                    )?)))),
272                    "sec" => Ok(ElemRef::Function(Sec(Arc::new(Self::to_elementary(
273                        &cont,
274                    )?)))),
275                    "csc" => Ok(ElemRef::Function(Csc(Arc::new(Self::to_elementary(
276                        &cont,
277                    )?)))),
278                    "cot" => Ok(ElemRef::Function(Cot(Arc::new(Self::to_elementary(
279                        &cont,
280                    )?)))),
281                    "asin" => Ok(ElemRef::Function(Asin(Arc::new(Self::to_elementary(
282                        &cont,
283                    )?)))),
284                    "acos" => Ok(ElemRef::Function(Acos(Arc::new(Self::to_elementary(
285                        &cont,
286                    )?)))),
287                    "atan" => Ok(ElemRef::Function(Atan(Arc::new(Self::to_elementary(
288                        &cont,
289                    )?)))),
290                    "sinh" => Ok(ElemRef::Function(Sinh(Arc::new(Self::to_elementary(
291                        &cont,
292                    )?)))),
293                    "cosh" => Ok(ElemRef::Function(Cosh(Arc::new(Self::to_elementary(
294                        &cont,
295                    )?)))),
296                    "tanh" => Ok(ElemRef::Function(Tanh(Arc::new(Self::to_elementary(
297                        &cont,
298                    )?)))),
299                    "ln" => Ok(ElemRef::Function(Log(
300                        Arc::new(Con(E)), //ln is equivalent to log base e of its contents
301                        Arc::new(Self::to_elementary(&cont)?),
302                    ))),
303                    "abs" => Ok(ElemRef::Function(Abs(Arc::new(Self::to_elementary(
304                        &cont,
305                    )?)))),
306                    "sqrt" => Ok(ElemRef::Function(Pow(
307                        Arc::new(Self::to_elementary(&cont)?),
308                        Arc::new(Con(0.5)),
309                    ))),
310                    "d" => Ok(ElemRef::Function(
311                        Self::to_elementary(&cont)?.derivative_unsimplified(),
312                    )),
313                    _ => Err(Error::ParseError(format!(
314                        "Function identifier '{func}' not recognized"
315                    ))),
316                }
317            }
318        }
319    }
320}
321
322// all instances of an operation must be handled before the parsing method can move on to the next.
323// This is to ensure that the order of operations is being upheld
324fn iterate_operation(functions: &mut Vec<ElemRef>, operation: ElemRef) -> Result<(), Error> {
325    if functions.contains(&operation) {
326        for i in 0..functions.len() {
327            if i >= functions.len() {
328                continue;
329            }
330
331            if functions[i] == operation {
332                let replacement_func = match operation {
333                    ElemRef::Mul => ElemRef::Function(Mul(
334                        Arc::new(functions[i - 1].clone().convert()?),
335                        Arc::new(functions[i + 1].clone().convert()?),
336                    )),
337                    ElemRef::Div => ElemRef::Function(Div(
338                        Arc::new(functions[i - 1].clone().convert()?),
339                        Arc::new(functions[i + 1].clone().convert()?),
340                    )),
341                    ElemRef::Add => ElemRef::Function(Add(
342                        Arc::new(functions[i + 1].clone().convert()?),
343                        Arc::new(functions[i - 1].clone().convert()?),
344                    )),
345                    ElemRef::Sub => {
346                        if i > 0 {
347                            ElemRef::Function(Sub(
348                                Arc::new(functions[i - 1].clone().convert()?),
349                                Arc::new(functions[i + 1].clone().convert()?),
350                            ))
351                        } else {
352                            ElemRef::Function(functions[i + 1].clone().convert()? * (-1 as f64))
353                        }
354                    }
355                    ElemRef::Factorial => {
356                        if i > 0 {
357                            ElemRef::Function(Factorial(functions[i - 1].clone().convert()?.into()))
358                        } else {
359                            return Err(Error::ParseError(String::from(
360                                "Factorial function must be applied to another function",
361                            )));
362                        }
363                    }
364                    _ => unimplemented!("No such operation"), // this point shouldn't be reached
365                };
366
367                if operation == ElemRef::Factorial {
368                    println!("testing - {replacement_func:?}");
369                    // the factorial notation is rather unique, because it always follows directly
370                    // after the function upon which it acts.
371                    functions.remove(i);
372                    functions.remove(i - 1);
373                    functions.insert(i - 1, replacement_func)
374                } else {
375                    // the operation itself as well as the functions surrounding it must be removed
376                    functions.remove(i + 1);
377                    functions.remove(i);
378                    if i > 0 {
379                        functions.remove(i - 1);
380                        // the combined new function is inserted in the place of the previous functions
381                        functions.insert(i - 1, replacement_func);
382                    } else {
383                        // this is strictly for when a negative number is implied as seen above
384                        functions.insert(i, replacement_func)
385                    }
386                }
387            }
388        }
389    }
390    Ok(())
391}
392
393// enum to allow operations to be described as the same type without carrying two functions
394#[derive(Debug, Clone, PartialEq)]
395enum ElemRef {
396    Function(Elementary),
397    Pow,
398    Mul,
399    Div,
400    Add,
401    Sub,
402    Factorial,
403}
404impl ElemRef {
405    fn convert(self) -> Result<Elementary, Error> {
406        match self {
407            Self::Function(elem) => Ok(elem),
408            _ => Err(Error::ParseError(String::from(
409                "Cannot convert operation to elementary function",
410            ))),
411        }
412    }
413}
414
415// splits the provided string at the first index where the specified identifier is found.
416// if the identifier is not found, the string will be split at index 0
417fn split_first<'a>(string: &'a String, indentifier: &'a str) -> (&'a str, &'a str) {
418    let slice: Vec<&str> = string.split("").collect();
419
420    let mut index = 0;
421    // find index of first insance of the identifier
422
423    for (i, s) in slice.iter().enumerate().take(string.len()) {
424        if *s == indentifier {
425            index = i;
426            break;
427        }
428    }
429
430    string.split_at(index - 1)
431}