calculator_backend/
lib.rs

1///./src/lib.rs 
2/// This file contains the main logic for a calculator library that supports Reverse Polish Notation (RPN) and infix expressions.
3/// It includes functions for tokenization, conversion between infix and RPN, and evaluation of expressions.
4/// The library is designed to be used with a C library for evaluation, and it provides a C-compatible interface for integration.
5
6//use serde::{Serialize, Deserialize};
7use std::ffi::{CString, CStr, c_char};
8use std::os::raw::{c_double, c_int};
9
10// Error codes matching C
11const SUCCESS: c_int = 0;
12const DIVISION_BY_ZERO: c_int = 1;
13const INVALID_OPERATOR: c_int = 2;
14const STACK_UNDERFLOW: c_int = 3;
15const MEMORY_ERROR: c_int = 4;
16const UNDEFINED_VARIABLE: c_int = 5;
17const STACK_MAXIMUM: c_int = 6;
18const EXPR_LENGHT_MAXIMUM: c_int = 7;
19const FACTORIAL_ERROR: c_int = 8;
20const SQUARE_ROOT_ERROR: c_int = 9;
21const LOG_ERROR: c_int = 10;
22const LN_ERROR: c_int = 11;
23const TAN_INVALID_OPERATOR: c_int = 12;
24const INVALID_TRIG_OPERATOR: c_int = 13;
25
26/// Stores the result of the calculation
27/// S
28/// # Fields
29/// 
30/// * `result_value`: The calculated result, a C-compatible double-precision floating-point number (`c_double`).
31/// * `error_code`: An integer error code (`c_int`) indicating the success or failure of the calculation.
32///     * `0` represents success.
33///     * Other values indicate specific errors.
34#[repr(C)]
35#[derive(Debug)]
36pub struct CCalculationResult {
37    pub result_value: c_double,
38    pub error_code: c_int,
39}
40
41/// Returns a human-readable error message based on the provided error code.
42/// 
43/// This function acts as a wrapper around the `get_error_message` function, providing
44/// a simpler interface for retrieving error messages.
45/// 
46/// # Arguments
47/// 
48/// * `error_code`: An integer (`c_int`) representing the error code.
49/// 
50/// # Returns
51/// 
52/// A static string slice (`&'static str`) containing a human-readable description of the error.
53/// 
54/// # Supported Error Codes
55/// 
56/// * `0` (`SUCCESS`): "Success"
57/// * `1` (`DIVISION_BY_ZERO`): "Division by zero"
58/// * `2` (`INVALID_OPERATOR`): "Invalid operator"
59/// * `3` (`STACK_UNDERFLOW`): "Stack underflow - invalid expression"
60/// * `4` (`MEMORY_ERROR`): "Memory error"
61/// * `5` (`UNDEFINED_VARIABLE`): "Undefined variable in expression"
62/// * `6` (`STACK_MAXIMUM`): "Stack maximum exceeded"
63/// * `7` (`EXPR_LENGHT_MAXIMUM`): "Expression length maximum exceeded"
64/// * `8` (`FACTORIAL_ERROR`): "Factorial error"
65/// * `9` (`SQUARE_ROOT_ERROR`): "Square root error"
66/// * `10` (`LOG_ERROR`): "Log error"
67/// * `11` (`LN_ERROR`): "Natural logarithm error"
68/// * Any other value: "Unknown error"
69/// 
70pub fn get_error_str(error_code: c_int) -> &'static str {
71    get_error_message(error_code)
72}
73
74/// Reverse Polish Notatio3n (RPN) as a C syle array
75/// 
76/// # Fields
77/// 
78/// * `crpn_expression`: A pointer (`*const *const c_char`) to a read-only array of C-style strings. Each string represents an element of the RPN expression.
79/// * `length`: The number of elements in the `expression` array.
80#[repr(C)]
81#[derive(Debug)]
82pub struct CReversePolishExpression {
83    pub crpn_expression: *const *const c_char,
84    pub length: usize,
85}
86
87/// Represents the result of converting an infix expression to Reverse Polish Notation (RPN) in a C-compatible format.
88/// 
89/// # Fields
90/// 
91/// * `result_expression`: A fixed-size array of C-style characters (`c_char`) representing the converted RPN expression.
92/// * `error_code`: An integer error code (`c_int`) indicating the success or failure of the conversion.
93///     * `0` represents success.
94///     * Other values indicate specific errors.
95#[repr(C)]
96pub struct CConversionResult {
97    result_expression: [c_char; 1000],  // MAX_EXPR_LENGTH from C
98    error_code: c_int,
99}
100
101// Externally defined C functions for Reverse Polish Notation (RPN) calculations and conversions.
102// 
103// These functions are implemented in the C library and are exposed to Rust using the `extern "C"` block.
104// 
105// # Functions
106// 
107// ## `calculate_rpn`
108// Evaluates a Reverse Polish Notation (RPN) expression and returns the result.
109// 
110// ### Arguments
111// * `expr`: A pointer to a `CReversePolishExpression` struct, which contains the RPN expression to be evaluated.
112// 
113// ### Returns
114// A `CCalculationResult` struct containing:
115// * `result_value`: The calculated result as a `c_double`.
116// * `error_code`: An integer (`c_int`) indicating the success or failure of the calculation.
117//     * `0` (`SUCCESS`) indicates success.
118//     * Other values indicate specific errors (e.g., `DIVISION_BY_ZERO`, `INVALID_OPERATOR`).
119// 
120// ## `convert_rpn_to_infix`
121// Converts a Reverse Polish Notation (RPN) expression to an infix expression.
122// 
123// ### Arguments
124// * `expr`: A pointer to a `CReversePolishExpression` struct, which contains the RPN expression to be converted.
125// 
126// ### Returns
127// A `CConversionResult` struct containing:
128// * `result_expression`: A fixed-size array of `c_char` representing the converted infix expression.
129// * `error_code`: An integer (`c_int`) indicating the success or failure of the conversion.
130//     * `0` (`SUCCESS`) indicates success.
131//     * Other values indicate specific errors.
132// 
133// # Safety
134// 
135// These functions are marked as `unsafe` because they involve raw pointers and interaction with a C library.
136// Ensure that the pointers passed to these functions are valid and properly aligned.
137extern "C" {
138    pub fn calculate_rpn(expr: *const CReversePolishExpression) -> CCalculationResult;
139    fn convert_rpn_to_infix(expr: *const CReversePolishExpression) -> CConversionResult;
140}
141
142/// Represents a history entry for a mathematical expression and its result.
143///  # Fields
144/// 
145/// * `input`: A string containing the original mathematical expression provided by the user. 
146/// * `result`: An optional floating-point number (`Option<f64>`) representing the calculated result.
147/// * `error_message`: An optional string (`Option<String>`) containing an error message if the calculation failed.
148/// * `None` if there was no error.
149/// 
150///  This struct is used to log the history of calculations performed by the calculator.
151#[derive(Debug, Clone)]
152pub struct HistoryEntry {
153    pub input: String,
154    pub result: Option<f64>, // `None` if there was an error
155    pub error_message: Option<String>, // `None` if there was no error
156}
157
158/// Represents the history of calculations performed by the calculator.
159/// 
160/// # Fields
161/// 
162/// * `entries`: A vector of `HistoryEntry` structs, each representing a single calculation.
163/// * `last_result`: An optional floating-point number (`Option<f64>`) representing the result of the most recent successful calculation.
164/// 
165/// # Methods
166/// 
167/// ## `new`
168/// Creates a new, empty `History` instance.
169/// 
170/// ## `add_entry`
171/// Adds a new entry to the history.
172/// 
173/// ### Arguments
174/// * `input`: A string representing the mathematical expression provided by the user.
175/// * `result`: An optional floating-point number representing the result of the calculation. Use `None` if the calculation failed.
176/// * `error_message`: An optional string containing an error message if the calculation failed. Use `None` if there was no error.
177/// 
178/// ## `get_history`
179/// Returns a reference to the vector of `HistoryEntry` structs.
180/// 
181/// ## `get_last_result`
182/// Returns the result of the most recent successful calculation, or `None` if no successful calculation has been performed.
183#[derive(Debug)]
184pub struct History {
185    pub entries: Vec<HistoryEntry>,
186    pub last_result: Option<f64>,
187}
188
189/// Represents the history of calculations performed by the calculator.
190/// 
191/// # Fields
192/// 
193/// * `entries`: A vector of `HistoryEntry` structs, each representing a single calculation.
194/// * `last_result`: An optional floating-point number (`Option<f64>`) representing the result of the most recent successful calculation.
195/// 
196/// # Methods
197/// 
198/// ## `new`
199/// Creates a new, empty `History` instance.
200/// 
201/// ## `add_entry`
202/// Adds a new entry to the history.
203/// 
204/// ### Arguments
205/// * `input`: A string representing the mathematical expression provided by the user.
206/// * `result`: An optional floating-point number representing the result of the calculation. Use `None` if the calculation failed.
207/// * `error_message`: An optional string containing an error message if the calculation failed. Use `None` if there was no error.
208/// 
209/// ## `get_history`
210/// Returns a reference to the vector of `HistoryEntry` structs.
211/// 
212/// ## `get_last_result`
213/// Returns the result of the most recent successful calculation, or `None` if no successful calculation has been performed.
214impl History {
215    pub fn new() -> Self {
216        History {
217            entries: Vec::new(),
218            last_result: None,
219        }
220    }
221
222    pub fn add_entry(&mut self, input: String, result: Option<f64>, error_message: Option<String>) {
223        if let Some(res) = result {
224            self.last_result = Some(res); // Update the last result
225        }
226        self.entries.push(HistoryEntry {
227            input,
228            result,
229            error_message,
230        });
231    }
232
233    pub fn get_history(&self) -> &Vec<HistoryEntry> {
234        &self.entries
235    }
236
237    pub fn get_last_result(&self) -> Option<f64> {
238        self.last_result
239    }
240}
241
242/// Displays the history of calculations in a human-readable format.
243/// 
244/// # Arguments
245/// 
246/// * `history`: A reference to the `History` instance to be displayed.
247pub fn display_history(history: &History) {
248    for (i, entry) in history.get_history().iter().enumerate() {
249        println!("Entry {}:", i + 1);
250        println!("  Input: {}", entry.input);
251        match &entry.result {
252            Some(result) => println!("  Result: {}", result),
253            None => println!("  Error: {}", entry.error_message.as_ref().unwrap()),
254        }
255    }
256}
257
258
259/// Reverse Polish Notation (RPN) expression represented as a vector of strings
260/// 
261/// # Fields
262/// 
263/// * `rp_expression`: A vector of strings (`Vec<String>`) representing the expression in Reverse Polish Notation (RPN).
264///     * Each string in the vector is a single token of the RPN expression (e.g., "2", "3", "+").
265pub struct ReversePolish {
266    rp_expression: Vec<String>
267}
268
269/// Result of the calculations done through the C Library.
270/// 
271/// # Fields
272/// 
273/// * `success`: A boolean indicating whether the calculation was successful.
274/// * `expression`: The original mathematical expression provided by the user.
275/// * `rpn_expression`: The corresponding expression in Reverse Polish Notation (RPN).
276/// * `result`: The numerical result of the calculation.
277/// * `message`: An additional message providing details about the calculation outcome, 
278///              such as errors or warnings.
279impl ReversePolish {
280    pub fn to_string(&self) -> String {
281        self.rp_expression.join(" ")
282    }
283
284    pub fn to_c_expr(&self) -> Result<(Vec<CString>, Vec<*const c_char>), String> {
285        let mut expr_cstrings = Vec::with_capacity(self.rp_expression.len());
286        let mut expr_ptrs = Vec::with_capacity(self.rp_expression.len());
287        
288        for s in &self.rp_expression {
289            let cstring = CString::new(s.as_str())
290                .map_err(|_| "Failed to convert expression to CString".to_string())?;
291            expr_ptrs.push(cstring.as_ptr());
292            expr_cstrings.push(cstring);
293        }
294
295        Ok((expr_cstrings, expr_ptrs))
296    }
297}
298
299/// Represents the result of a calculation performed using the C Library.
300///
301/// # Fields
302///
303/// * `success`: A boolean indicating whether the calculation was successful.
304/// * `expression`: The original mathematical expression provided by the user.
305/// * `rpn_expression`: The corresponding expression in Reverse Polish Notation (RPN).
306/// * `result`: The numerical result of the calculation.
307/// * `message`: A string containing additional details about the calculation outcome, 
308///              such as errors or warnings.
309pub struct CalculationResult {
310    pub success: bool,
311    pub expression: String,
312    pub rpn_expression: String,
313    pub result: f64,
314    pub message: String,
315}
316
317
318/// Returns an error message based on given error code.
319/// 
320/// # Arguments
321/// 
322/// * 'error_code': An error code represented by a C-style integer.
323/// 
324/// # Returns
325/// 
326/// A static string slice containing a human-readable message describing the error.
327pub fn get_error_message(error_code: c_int) -> &'static str {
328    match error_code {
329        SUCCESS => "Success",
330        DIVISION_BY_ZERO => "Division by zero",
331        INVALID_OPERATOR => "Invalid operator",
332        STACK_UNDERFLOW => "Stack underflow - invalid expression",
333        MEMORY_ERROR => "Memory error",
334        UNDEFINED_VARIABLE => "Undefined variable in expression",
335        STACK_MAXIMUM => "Stack maximum exceeded",
336        EXPR_LENGHT_MAXIMUM => "Exprestion lenght maximum exceeded",
337        FACTORIAL_ERROR => "Factorial error",
338        SQUARE_ROOT_ERROR => "Square root error",
339        LOG_ERROR => "Log error",
340        LN_ERROR => "Natural logarithm error",
341        TAN_INVALID_OPERATOR => "Invalid operator for tangent",
342        INVALID_TRIG_OPERATOR => "Invalid trigonometric operator",
343        _ => "Unknown error"
344    }
345}
346
347/// Token types for the tokenization process.
348///
349/// * `Operator`: Represents mathematical operators such as `+`, `-`, `*`, `/`, `^`, `!`, and `√`.
350///     - Trigonometric functions: "sin", "cos", "tan", "arcsin", "arccos", "arctan"
351///     - Square root: "sqrt", "√"
352///     - Logarithmic functions: "log" (base-10 logarithm), "ln" (natural logarithm)
353///     - Special constants or functions: "ans"
354/// * `Operand`: Represents numeric values, including integers and floating-point numbers.
355///     * Example values: "2", "3.14", "10", "0"
356/// * `Variable`: Represents alphanumeric symbols used as variables or identifiers.
357///     * Example values: "x", "y", "result", "my_variable"
358/// * `Bracket`: Represents parentheses used to group expressions.
359///     * Example values: "(", ")"
360#[derive(Debug, Clone, PartialEq)]
361pub enum TokenType {
362    Operator,
363    Operand,
364    Variable,
365    Bracket,
366    Function
367}
368
369/// Represents a single token produced during the tokenization process.
370///
371/// Each `Token` consists of a string `value` and its corresponding `TokenType`, which categorizes the token.
372///
373/// # Fields
374///
375/// * `token_value`: A string containing the actual content of the token.
376/// * `token_type`: The type of the token, as defined by the `TokenType` enum.
377#[derive(Debug, Clone)]
378pub struct Token {
379    token_value: String,
380    token_type: TokenType,
381}
382
383/// Classifies an identifier as either an operator or a variable.
384/// 
385/// # Arguments
386/// 
387/// * `ident` - A string slice representing the identifier to be classified.
388/// 
389/// # Returns
390/// 
391/// A `TokenType` enum value:
392/// - `TokenType::Operator` if the identifier matches a known operator name.
393/// - `TokenType::Variable` if the identifier does not match any known operator name.
394/// 
395/// # Known Operators
396/// 
397/// The function recognizes the following operators:
398/// - Trigonometric functions: `"sin"`, `"cos"`, `"tan"`, `"arcsin"`, `"arccos"`, `"arctan"`
399/// - Square root: `"sqrt"`
400/// - Logarithmic functions: `"log"` (base-10 logarithm), `"ln"` (natural logarithm)
401/// - Special constants or functions: `"ans"`
402fn classify_identifier(ident: &str, history: &History) -> TokenType {
403    if ident == "ans" {
404        // Always resolve `ans` to the last result
405        if let Some(_last_result) = history.get_last_result() {
406
407            TokenType::Operand
408        } else {
409            TokenType::Operand // Default to 0.0 if no previous result exists
410        }
411    } else {
412        // Known operators
413        const OPERATORS_NAMES: &[&str] = &[
414            "sin", "cos", "tan", "arcsin", "arccos", "arctan", "sqrt", "log", "ln",
415        ];
416        if OPERATORS_NAMES.contains(&ident) {
417            TokenType::Operator
418        } else {
419            TokenType::Variable
420        }
421    }
422}
423
424///Determines if token is a valid number. 
425/// 
426/// # Fields 
427/// 
428/// *`s`: A string slice representing the token
429/// 
430/// # Returns
431/// 
432/// A boolean:
433/// -'true' if valid number (e.g 123,1.23,-1.23,)
434/// -'false` otherwise
435fn is_numeric(s: &str) -> bool {
436    s.parse::<f64>().is_ok() //Standard rust parser that does a way better job than my implementation did :(
437}
438
439/// Tokenizes an input string into a vector of tokens.
440/// 
441/// # Arguments
442/// 
443/// * `input` - A string slice representing the input to be tokenized.
444/// 
445/// # Returns
446/// 
447/// A vector of `Token` structs, each representing a part of the input.
448/// 
449/// # Token Types
450///
451/// - `Operator`: Symbols representing mathematical operations (`+`, `-`, `*`, `/`, `^`, '!').
452///     - Trigonometric functions: "sin", "cos", "tan", "arcsin", "arccos", "arctan"
453///     - Square root: "sqrt"
454///     - Logarithmic functions: "log" (base-10 logarithm), "ln" (natural logarithm)
455///     - Special constants or functions: "ans"
456/// - `Operand`: Numeric values, which can include decimal points (`2`, `3`, `4`, `9.23`, `0`).
457/// - `Variable`: Alphanumeric symbols representing unknowns (`x`, `y`, `z`, `_`). (UNUSED CURRENTLY)
458/// - `Bracket`: Parentheses used in expressions (`(`, `)`).
459pub fn tokenize(input: &str, history: &History) -> Vec<Token> {
460    let mut tokens: Vec<Token> = Vec::new();
461    let binding = input.to_lowercase();
462    let mut chars = binding.chars().peekable();
463    let mut current_pos = 0; //Keeps track of where the tokenizer is in the expression
464    let _input = input.replace("√", "sqrt");
465
466    while let Some(&c) = chars.peek() {
467        current_pos += 1; // Increment position conceptually for error messages
468
469        match c {
470            // === Whitespace ===
471            w if w.is_whitespace() => {
472                chars.next(); // Consume whitespace
473            }
474
475            // === Brackets ==
476            '(' | ')' => {
477                tokens.push(Token {
478                    token_value: c.to_string(),
479                    token_type: TokenType::Bracket,
480                });
481                chars.next(); // Consume bracket
482            }
483
484            // === Operators (excluding '-') ===
485            '+' | '*' | '/' | '^' | '!' => {
486            
487                tokens.push(Token {
488                    token_value: c.to_string(),
489                    token_type: TokenType::Operator,
490                });
491                chars.next(); // Consume operator
492            }
493            '√' => {
494                // Handle square root as a 
495                tokens.push(Token {
496                    token_value: "sqrt".to_string(),
497                    token_type: TokenType::Operator,
498                });
499                chars.next(); // Consume '√'
500            }
501            // === Minus Sign or Negative Number(Operator or start of Number) ===
502            '-' => {
503                let is_unary = tokens.is_empty() || matches!(tokens.last(), Some(Token { token_type: TokenType::Operator | TokenType::Bracket, ..}));
504
505                if is_unary {
506                    // Potentially start of a new negative number
507                    let mut num_string = String::new();
508                    num_string.push(chars.next().unwrap()); // Consume '-'
509
510                    //Consume rest of digits and one optional '.'
511                    while let Some(&next_c) = chars.peek() {
512                         if next_c.is_digit(10) || (next_c == '.' && !num_string.contains('.')) {
513                            // Consume the character *only if* it's part of the number
514                            num_string.push(chars.next().unwrap());
515                         } else {
516                             break; // Not part of number
517                         }
518                    }
519
520                    //Validate if a negative number was formed
521                    if is_numeric(&num_string) {
522                        tokens.push(Token {
523                            token_value: num_string, // num_string owns the value now
524                            token_type: TokenType::Operand,
525                        });
526                    } else {
527                        //If just "-". gets treated as an operator
528                        if num_string == "-" {
529                             tokens.push(Token {
530                                token_value: "-".to_string(), 
531                                token_type: TokenType::Operator,
532                             });
533                        } else {
534                            eprintln!("Warning: Invalid sequence starting with '-' ('{}') at position {}", num_string, current_pos);
535                             if num_string != "-" { // Avoid double-pushing if it was exactly "-"
536                                tokens.push(Token {
537                                    token_value: "-".to_string(),
538                                    token_type: TokenType::Operator,
539                                 });
540                                 // The non-digit/dot character after '-' will be handled in the next loop iteration.
541                             }
542
543                        }
544                    }
545                } else {
546                    //Binary operator
547                    tokens.push(Token {
548                        token_value: c.to_string(),
549                        token_type: TokenType::Operator,
550                    });
551                    chars.next(); // Consume '-' operator
552                }
553            }
554
555            // === Numbers (starting with digit or '.') ===
556            d if d.is_digit(10) || d == '.' => {
557                let mut num_str = String::new();
558                let mut has_decimal = d == '.';
559                num_str.push(chars.next().unwrap()); // Consume first digit or '.'
560
561                while let Some(&next_c) = chars.peek() {
562                    if next_c.is_digit(10) {
563                        num_str.push(chars.next().unwrap());
564                    } else if next_c == '.' && !has_decimal {
565                        has_decimal = true;
566                        num_str.push(chars.next().unwrap());
567                    } else {
568                        break; // End of number
569                    }
570                }
571
572                // === Scientific Notation ===
573                if let Some(&'e') = chars.peek() {
574                    num_str.push(chars.next().unwrap()); // Consume e
575                    
576                    //Catch direction decimal is moving in
577                    if let Some(&next_c) = chars.peek() {
578                        if next_c == '-' || next_c == '+' {
579                            num_str.push(chars.next().unwrap());
580                        }
581
582                        //Consume the rest of the number
583                        while let Some(&next_c) = chars.peek() {
584                            if next_c.is_digit(10) {
585                                num_str.push(chars.next().unwrap());
586                            } else {
587                                break; 
588                            }
589
590                        }
591                    }
592                }
593
594                // Validate if number was formed
595                if is_numeric(&num_str) {
596                    tokens.push(Token {
597                        token_value: num_str,
598                        token_type: TokenType::Operand,
599                    });
600                } else {
601                    // Handle error: Invalid sequence like "." or "1.2.3"
602                    eprintln!("Warning: Invalid numeric sequence '{}' at position {}", num_str, current_pos);
603                    // How to recover? Skip? Push as unknown? For now, just warns.
604                }
605            }
606
607            // ==== Identifier (Variables or Functions) ===
608            // Original code only checked is_alphabetic(), let's add `|| a == '_'`
609             a if a.is_alphabetic() => {
610                let mut ident_str = String::new();
611                ident_str.push(chars.next().unwrap());
612
613                while let Some(&next_c) = chars.peek() {
614                    if next_c.is_alphanumeric() {
615                        ident_str.push(chars.next().unwrap());
616                    } else {
617                        break;
618                    }
619                }
620
621                if ident_str == "ans" {
622                    // Replace `ans` with the last result from history
623                    let last_result = history.get_last_result().unwrap_or(0.0);
624                    tokens.push(Token {
625                        token_value: last_result.to_string(),
626                        token_type: TokenType::Operand,
627                    });
628                } else {
629                    let token_type = classify_identifier(&ident_str, history);
630                    tokens.push(Token {
631                        token_value: ident_str,
632                        token_type,
633                    });
634                }
635            }
636
637
638            // == Unknown Character ===
639            _ => {
640                eprintln!("Warning: Skipping unknown character '{}' at position {}", c, current_pos);
641                chars.next(); // Consume the unknown character
642            }
643        }
644    }
645    println!("Tokens: {:?}", tokens);
646    tokens
647}
648
649/// Determines the precedence of an operator.
650/// 
651/// # Arguments
652/// 
653/// * `op`- A string slice representing the operator.
654/// 
655/// # Returns
656/// 
657/// An integer representing the precedence of the operator.
658/// Returns 0 for unsupported or invalid operators.
659pub fn get_precedence(op: &str) -> i32 {
660    match op {
661        "+" | "-" => 1,
662        "*" | "/" => 2,
663        "^" => 3,
664        "!" | "√" | "sqrt" | "log" | "ln" => 4, 
665        "sin"| "cos"| "tan" | "arctan"| "arcsin"| "arccos" => 5,
666        _ => 0
667    }
668}
669
670/// Determines if operator is a right associative.
671/// 
672/// #Arguments
673/// 
674/// * 'op': A string slice representing the operator being checked.
675/// 
676/// #Returns
677/// 
678/// A boolean value:
679/// - `true` if the operator is right-associative (e.g., `^`, '!').
680/// - `false` otherwise.
681pub fn is_right_associative(op: &str) -> bool {
682    op == "^" || op == "!" 
683}
684
685/// Takes an infix expression and converts it into a Reverse Polish Notation (RPN) expression.
686///
687/// # Arguments
688///
689/// * `input`: A string slice representing the infix expression to be converted to Reverse Polish Notation (RPN).
690///
691/// # Returns
692///
693/// * `Ok(ReversePolish)`: If the conversion is successful, where `ReversePolish` holds the Reverse Polish Notation (RPN) expression.
694/// * `Err(String)`: If the conversion fails.
695///
696/// # Errors
697///
698/// This function returns an `Err(String)` with a descriptive error message in the following cases:
699///
700/// * "Mismatched parentheses": If the input expression has an unbalanced number of opening and closing parentheses.
701/// * "Invalid token: ..." : If the tokenizer encounters an unexpected character or sequence of characters that cannot be recognized as a valid token.
702pub fn infix_to_rpn(input: &str, _history: &History) -> Result<ReversePolish, String> {
703    let tokens = tokenize(input, _history);
704    let mut output = Vec::new();
705    let mut op_stack: Vec<Token> = Vec::new();
706
707    fn is_prefix_unary(op: &str) -> bool {
708        op == "sqrt"
709    }
710
711    for token in tokens {
712        match token.token_type {
713            TokenType::Operand | TokenType::Variable => {
714                output.push(token.token_value.clone());
715                if let Some(last_op) = op_stack.last() {
716                    if is_prefix_unary(&last_op.token_value) {
717                        let op = op_stack.pop().unwrap();
718                        output.push(op.token_value);
719                    }
720                }
721            }
722            TokenType::Operator => {
723                if is_prefix_unary(&token.token_value) {
724                    // Delay adding to output until we see the operand
725                    op_stack.push(token);
726                }else {
727                    while let Some(top) = op_stack.last() {
728                    if top.token_value == "(" {
729                        break;
730                    }
731                    let curr_prec = get_precedence(&token.token_value);
732                    let top_prec = get_precedence(&top.token_value);
733
734                    if curr_prec > top_prec || (curr_prec == top_prec && is_right_associative(&token.token_value)) {
735                        break;
736                    }
737                    if let Some(op) = op_stack.pop() {
738                        output.push(op.token_value);
739                    }
740                    }
741                    op_stack.push(token);
742                }
743            }
744            TokenType::Bracket => {
745                if token.token_value == "(" {
746                    op_stack.push(token);
747                } else {
748                    while let Some(top) = op_stack.pop() {
749                        if top.token_value == "(" {
750                            break;
751                        }
752                        output.push(top.token_value);
753                    }
754                }
755            }
756            _ => {}
757        }
758    }
759
760    while let Some(op) = op_stack.pop() {
761        output.push(op.token_value);
762    }
763
764    Ok(ReversePolish {
765        rp_expression: output,
766    })
767}
768
769/// Endpoint to process a mathematical expression and return the result as a string.
770/// 
771/// #Arguments
772/// * 'input': A string representation of the infix expression to be processed.
773/// 
774/// #Returns
775/// 
776/// 'CalculationResult' structure containing:
777/// - `success`: A boolean indicating whether the calculation was successful.
778/// - `expression`: The original mathematical expression provided by the user.
779/// - `rpn_expression`: The corresponding expression in Reverse Polish Notation (RPN).
780/// - `result`: The numerical result of the calculation.
781/// - `message`: An additional message providing details about the calculation outcome, 
782///              such as errors or warnings.
783/// 
784/// #Errors
785/// 
786/// This function returns an error code in the following cases:
787/// - If the expression contains mismatched parentheses or cannot be tokenized, an error message is returned.
788/// - If the conversion to C-compatible format fails, an error message is returned.
789/// - If the C function for evaluation fails, an error message with details is included in the response.
790pub fn calculate_expression(input: &str, history: &mut History) -> CalculationResult {
791    let input = input.trim_matches('"');
792
793    match infix_to_rpn(input, history) {
794        Ok(rpn) => {
795            let rpn_str = rpn.to_string();
796
797            let (_expr_cstrings, expr_ptrs) = match rpn.to_c_expr() {
798                Ok(data) => data,
799                Err(e) => {
800                    history.add_entry(
801                        input.to_string(),
802                        None,
803                        Some(format!("Failed to convert to RPN: {}", e)),
804                    );
805                    return CalculationResult {
806                        success: false,
807                        expression: input.to_string(),
808                        rpn_expression: rpn_str,
809                        result: 0.0,
810                        message: e,
811                    };
812                }
813            };
814
815            let c_expr = CReversePolishExpression {
816                crpn_expression: expr_ptrs.as_ptr(),
817                length: expr_ptrs.len(),
818            };
819
820            let result = unsafe { calculate_rpn(&c_expr) };
821
822            let success = result.error_code == SUCCESS;
823            let message = get_error_message(result.error_code).to_string();
824
825            history.add_entry(
826                input.to_string(),
827                if success { Some(result.result_value) } else { None },
828                if success { None } else { Some(message.clone()) },
829            );
830
831            CalculationResult {
832                success,
833                expression: input.to_string(),
834                rpn_expression: rpn_str,
835                result: result.result_value,
836                message,
837            }
838        }
839        Err(e) => {
840            history.add_entry(
841                input.to_string(),
842                None,
843                Some(format!("Failed to parse expression: {}", e)),
844            );
845            CalculationResult {
846                success: false,
847                expression: input.to_string(),
848                rpn_expression: String::new(),
849                result: 0.0,
850                message: format!("Failed to parse expression: {}", e),
851            }
852        }
853    }
854}
855
856/// Represents the result of a conversion between Reverse Polish Notation (RPN) and infix notation.
857///
858/// # Fields
859///
860/// * `success` - A boolean indicating whether the conversion was successful.
861/// * `rpn_expression` - A string containing the RPN representation of the expression.
862/// * `infix_expression` - A string containing the infix representation of the expression.
863/// * `message` - A string with additional information, such as error messages or status notes.
864pub struct ConversionResult {
865    pub success: bool,
866    pub rpn_expression: String,
867    pub infix_expression: String,
868    pub message: String,
869}
870
871// Make it accessible in tests
872#[cfg(test)]
873impl CalculationResult {
874    pub fn success(&self) -> bool {
875        self.success
876    }
877
878    pub fn result(&self) -> f64 {
879        self.result
880    }
881}
882
883/// Endpoint to convert a Reverse Polish Notation (RPN) expression to an infix expression.
884/// 
885/// #Arguments
886/// 
887/// * 'input': A string containing the RPN expression to be converted
888/// 
889/// #Returns
890/// 
891/// A 'CalculationResult' structure containing:
892///  - `success`: A boolean indicating whether the calculation was successful.
893/// - `expression`: The original mathematical expression provided by the user.
894/// - `rpn_expression`: The corresponding expression in Reverse Polish Notation (RPN).
895/// - `result`: The numerical result of the calculation.
896/// - `message`: An additional message providing details about the calculation outcome, 
897///              such as errors or warnings.
898/// 
899/// #Errors
900/// 
901/// This function returns an error code in the following cases:
902/// - If the expression contains mismatched parentheses or cannot be tokenized, an error message is returned.
903/// - If the conversion to C-compatible format fails, an error message is returned.
904/// - If the C function for evaluation fails, an error message with details is included in the response.
905pub fn convert_rpn(input: String) -> ConversionResult {
906    let input = input.trim_matches('"');
907    let tokens: Vec<String> = input.split_whitespace().map(String::from).collect();
908    let rpn = ReversePolish { rp_expression: tokens };
909
910    let (_expr_cstrings, expr_ptrs) = match rpn.to_c_expr() {
911        Ok(data) => data,
912        Err(e) => {
913            return ConversionResult {
914                success: false,
915                rpn_expression: input.to_string(),
916                infix_expression: String::new(),
917                message: e
918            };
919        }
920    };
921
922    let c_expr = CReversePolishExpression {
923        crpn_expression: expr_ptrs.as_ptr(),
924        length: expr_ptrs.len(),
925    };
926
927    let result = unsafe {
928        let c_result = convert_rpn_to_infix(&c_expr);
929        let infix_expression = match CStr::from_ptr(c_result.result_expression.as_ptr()).to_str() {
930            Ok(s) => s.to_owned(),
931            Err(_) => return ConversionResult {
932                success: false,
933                rpn_expression: input.to_string(),
934                infix_expression: String::new(),
935                message: "Invalid UTF-8 in result".to_string()
936            }
937        };
938        (c_result.error_code, infix_expression)
939    };
940
941    let (error_code, infix_expression) = result;
942    let success = error_code == SUCCESS;
943    let message = get_error_message(error_code).to_string();
944
945    ConversionResult {
946        success,
947        rpn_expression: input.to_string(),
948        infix_expression,
949        message
950    }
951}