1use std::collections::HashMap;
19
20#[derive(Debug, Clone, Copy)]
22pub enum MathNum {
23 Integer(i64),
24 Float(f64),
25 Unset,
26}
27
28impl Default for MathNum {
29 fn default() -> Self {
30 MathNum::Integer(0)
31 }
32}
33
34impl MathNum {
35 pub fn is_zero(&self) -> bool {
36 match self {
37 MathNum::Integer(n) => *n == 0,
38 MathNum::Float(f) => *f == 0.0,
39 MathNum::Unset => true,
40 }
41 }
42
43 pub fn to_int(&self) -> i64 {
44 match self {
45 MathNum::Integer(n) => *n,
46 MathNum::Float(f) => *f as i64,
47 MathNum::Unset => 0,
48 }
49 }
50
51 pub fn to_float(&self) -> f64 {
52 match self {
53 MathNum::Integer(n) => *n as f64,
54 MathNum::Float(f) => *f,
55 MathNum::Unset => 0.0,
56 }
57 }
58
59 pub fn is_float(&self) -> bool {
60 matches!(self, MathNum::Float(_))
61 }
62
63 pub fn is_integer(&self) -> bool {
64 matches!(self, MathNum::Integer(_))
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[repr(u8)]
71enum MathTok {
72 InPar = 0, OutPar = 1, Not = 2, Comp = 3, PostPlus = 4, PostMinus = 5, UPlus = 6, UMinus = 7, And = 8, Xor = 9, Or = 10, Mul = 11, Div = 12, Mod = 13, Plus = 14, Minus = 15, ShLeft = 16, ShRight = 17, Les = 18, Leq = 19, Gre = 20, Geq = 21, Deq = 22, Neq = 23, DAnd = 24, DOr = 25, DXor = 26, Quest = 27, Colon = 28, Eq = 29, PlusEq = 30, MinusEq = 31, MulEq = 32, DivEq = 33, ModEq = 34, AndEq = 35, XorEq = 36, OrEq = 37, ShLeftEq = 38, ShRightEq = 39, DAndEq = 40, DOrEq = 41, DXorEq = 42, Comma = 43, Eoi = 44, PrePlus = 45, PreMinus = 46, Num = 47, Id = 48, Power = 49, CId = 50, PowerEq = 51, Func = 52, }
126
127const TOKCOUNT: usize = 53;
128
129const LR: u16 = 0x0000; const RL: u16 = 0x0001; const BOOL: u16 = 0x0002; const OP_A2: u16 = 0x0004; const OP_A2IR: u16 = 0x0008; const OP_A2IO: u16 = 0x0010; const OP_E2: u16 = 0x0020; const OP_E2IO: u16 = 0x0040; const OP_OP: u16 = 0x0080; const OP_OPF: u16 = 0x0100; static Z_PREC: [u8; TOKCOUNT] = [
144 1, 137, 2, 2, 2, 2, 2, 2, 4, 5, 6, 8, 8, 8, 9, 9, 3, 3, 10, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 200, 2, 2, 0, 0, 7, 0, 16, 0, ];
156
157static C_PREC: [u8; TOKCOUNT] = [
159 1, 137, 2, 2, 2, 2, 2, 2, 9, 10, 11, 4, 4, 4, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 12, 14, 13, 15, 16,
160 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 200, 2, 2, 0, 0, 3, 0, 17, 0,
161];
162
163static OP_TYPE: [u16; TOKCOUNT] = [
165 LR,
167 LR | OP_OP | OP_OPF,
168 RL,
169 RL,
170 RL | OP_OP | OP_OPF,
171 RL | OP_OP | OP_OPF,
173 RL,
174 RL,
175 LR | OP_A2IO,
176 LR | OP_A2IO,
177 LR | OP_A2IO,
179 LR | OP_A2,
180 LR | OP_A2,
181 LR | OP_A2,
182 LR | OP_A2,
183 LR | OP_A2,
185 LR | OP_A2IO,
186 LR | OP_A2IO,
187 LR | OP_A2IR,
188 LR | OP_A2IR,
189 LR | OP_A2IR,
191 LR | OP_A2IR,
192 LR | OP_A2IR,
193 LR | OP_A2IR,
194 BOOL | OP_A2IO,
195 BOOL | OP_A2IO,
197 LR | OP_A2IO,
198 RL | OP_OP,
199 RL | OP_OP,
200 RL | OP_E2,
201 RL | OP_E2,
203 RL | OP_E2,
204 RL | OP_E2,
205 RL | OP_E2,
206 RL | OP_E2,
207 RL | OP_E2IO,
209 RL | OP_E2IO,
210 RL | OP_E2IO,
211 RL | OP_E2IO,
212 RL | OP_E2IO,
213 BOOL | OP_E2IO,
215 BOOL | OP_E2IO,
216 RL | OP_A2IO,
217 RL | OP_A2,
218 RL | OP_OP,
219 RL,
221 RL,
222 LR | OP_OPF,
223 LR | OP_OPF,
224 RL | OP_A2,
225 LR | OP_OPF,
227 RL | OP_E2,
228 LR | OP_OPF,
229];
230
231#[derive(Clone)]
233struct MathValue {
234 val: MathNum,
235 lval: Option<String>,
236}
237
238impl Default for MathValue {
239 fn default() -> Self {
240 MathValue {
241 val: MathNum::Integer(0),
242 lval: None,
243 }
244 }
245}
246
247pub struct MathEval<'a> {
249 input: &'a str,
250 pos: usize,
251 yyval: MathNum,
252 yylval: String,
253 stack: Vec<MathValue>,
254 mtok: MathTok,
255 unary: bool,
256 noeval: i32,
257 lastbase: i32,
258 prec: &'static [u8; TOKCOUNT],
259 c_precedences: bool,
260 force_float: bool,
261 octal_zeroes: bool,
262 variables: HashMap<String, MathNum>,
263 lastval: i32,
264 pid: i64,
265 error: Option<String>,
266}
267
268impl<'a> MathEval<'a> {
269 pub fn new(input: &'a str) -> Self {
270 MathEval {
271 input,
272 pos: 0,
273 yyval: MathNum::Integer(0),
274 yylval: String::new(),
275 stack: Vec::with_capacity(100),
276 mtok: MathTok::Eoi,
277 unary: true,
278 noeval: 0,
279 lastbase: -1,
280 prec: &Z_PREC,
281 c_precedences: false,
282 force_float: false,
283 octal_zeroes: false,
284 variables: HashMap::new(),
285 lastval: 0,
286 pid: std::process::id() as i64,
287 error: None,
288 }
289 }
290
291 pub fn with_variables(mut self, vars: HashMap<String, MathNum>) -> Self {
292 self.variables = vars;
293 self
294 }
295
296 pub fn with_string_variables(mut self, vars: &HashMap<String, String>) -> Self {
298 for (k, v) in vars {
299 if let Ok(i) = v.parse::<i64>() {
300 self.variables.insert(k.clone(), MathNum::Integer(i));
301 } else if let Ok(f) = v.parse::<f64>() {
302 self.variables.insert(k.clone(), MathNum::Float(f));
303 }
304 }
305 self
306 }
307
308 pub fn extract_string_variables(&self) -> HashMap<String, String> {
310 self.variables
311 .iter()
312 .map(|(k, v)| {
313 let s = match v {
314 MathNum::Integer(i) => i.to_string(),
315 MathNum::Float(f) => {
316 if f.fract() == 0.0 && f.abs() < i64::MAX as f64 {
317 (*f as i64).to_string()
318 } else {
319 f.to_string()
320 }
321 }
322 MathNum::Unset => "0".to_string(),
323 };
324 (k.clone(), s)
325 })
326 .collect()
327 }
328
329 pub fn with_c_precedences(mut self, enable: bool) -> Self {
330 self.c_precedences = enable;
331 self.prec = if enable { &C_PREC } else { &Z_PREC };
332 self
333 }
334
335 pub fn with_force_float(mut self, enable: bool) -> Self {
336 self.force_float = enable;
337 self
338 }
339
340 pub fn with_octal_zeroes(mut self, enable: bool) -> Self {
341 self.octal_zeroes = enable;
342 self
343 }
344
345 pub fn with_lastval(mut self, val: i32) -> Self {
346 self.lastval = val;
347 self
348 }
349
350 fn peek(&self) -> Option<char> {
351 self.input[self.pos..].chars().next()
352 }
353
354 fn advance(&mut self) -> Option<char> {
355 let c = self.peek()?;
356 self.pos += c.len_utf8();
357 Some(c)
358 }
359
360 fn is_digit(c: char) -> bool {
361 c.is_ascii_digit()
362 }
363
364 fn is_ident_start(c: char) -> bool {
365 c.is_ascii_alphabetic() || c == '_'
366 }
367
368 fn is_ident(c: char) -> bool {
369 c.is_ascii_alphanumeric() || c == '_'
370 }
371
372 fn lex_constant(&mut self) -> MathTok {
374 let _start = self.pos;
375 let mut is_neg = false;
376
377 if self.peek() == Some('-') {
379 is_neg = true;
380 self.advance();
381 }
382
383 if self.peek() == Some('0') {
385 self.advance();
386 match self.peek().map(|c| c.to_ascii_lowercase()) {
387 Some('x') => {
388 self.advance();
390 let hex_start = self.pos;
391 while let Some(c) = self.peek() {
392 if c.is_ascii_hexdigit() || c == '_' {
393 self.advance();
394 } else {
395 break;
396 }
397 }
398 let hex_str: String = self.input[hex_start..self.pos]
399 .chars()
400 .filter(|&c| c != '_')
401 .collect();
402 let val = i64::from_str_radix(&hex_str, 16).unwrap_or(0);
403 self.lastbase = 16;
404 self.yyval = if self.force_float {
405 MathNum::Float(if is_neg { -(val as f64) } else { val as f64 })
406 } else {
407 MathNum::Integer(if is_neg { -val } else { val })
408 };
409 return MathTok::Num;
410 }
411 Some('b') => {
412 self.advance();
414 let bin_start = self.pos;
415 while let Some(c) = self.peek() {
416 if c == '0' || c == '1' || c == '_' {
417 self.advance();
418 } else {
419 break;
420 }
421 }
422 let bin_str: String = self.input[bin_start..self.pos]
423 .chars()
424 .filter(|&c| c != '_')
425 .collect();
426 let val = i64::from_str_radix(&bin_str, 2).unwrap_or(0);
427 self.lastbase = 2;
428 self.yyval = if self.force_float {
429 MathNum::Float(if is_neg { -(val as f64) } else { val as f64 })
430 } else {
431 MathNum::Integer(if is_neg { -val } else { val })
432 };
433 return MathTok::Num;
434 }
435 _ => {
436 if self.octal_zeroes {
438 let oct_start = self.pos;
440 let mut is_octal = true;
441 while let Some(c) = self.peek() {
442 if c.is_ascii_digit() || c == '_' {
443 if c >= '8' && c <= '9' {
444 is_octal = false;
445 }
446 self.advance();
447 } else if c == '.' || c == 'e' || c == 'E' || c == '#' {
448 is_octal = false;
449 break;
450 } else {
451 break;
452 }
453 }
454 if is_octal && self.pos > oct_start {
455 let oct_str: String = self.input[oct_start..self.pos]
456 .chars()
457 .filter(|&c| c != '_')
458 .collect();
459 let val = i64::from_str_radix(&oct_str, 8).unwrap_or(0);
460 self.lastbase = 8;
461 self.yyval = if self.force_float {
462 MathNum::Float(if is_neg { -(val as f64) } else { val as f64 })
463 } else {
464 MathNum::Integer(if is_neg { -val } else { val })
465 };
466 return MathTok::Num;
467 }
468 self.pos = oct_start;
469 }
470 self.pos -= 1;
472 }
473 }
474 }
475
476 let num_start = self.pos;
478 while let Some(c) = self.peek() {
479 if Self::is_digit(c) || c == '_' {
480 self.advance();
481 } else {
482 break;
483 }
484 }
485
486 if self.peek() == Some('.') || self.peek() == Some('e') || self.peek() == Some('E') {
488 if self.peek() == Some('.') {
490 self.advance();
491 while let Some(c) = self.peek() {
492 if Self::is_digit(c) || c == '_' {
493 self.advance();
494 } else {
495 break;
496 }
497 }
498 }
499 if self.peek() == Some('e') || self.peek() == Some('E') {
500 self.advance();
501 if self.peek() == Some('+') || self.peek() == Some('-') {
502 self.advance();
503 }
504 while let Some(c) = self.peek() {
505 if Self::is_digit(c) || c == '_' {
506 self.advance();
507 } else {
508 break;
509 }
510 }
511 }
512 let float_str: String = self.input[num_start..self.pos]
513 .chars()
514 .filter(|&c| c != '_')
515 .collect();
516 let val: f64 = float_str.parse().unwrap_or(0.0);
517 self.yyval = MathNum::Float(if is_neg { -val } else { val });
518 return MathTok::Num;
519 }
520
521 if self.peek() == Some('#') {
523 self.advance();
524 let base_str: String = self.input[num_start..self.pos - 1]
525 .chars()
526 .filter(|&c| c != '_')
527 .collect();
528 let base: u32 = base_str.parse().unwrap_or(10);
529 self.lastbase = base as i32;
530
531 let val_start = self.pos;
532 while let Some(c) = self.peek() {
533 if c.is_ascii_alphanumeric() || c == '_' {
534 self.advance();
535 } else {
536 break;
537 }
538 }
539 let val_str: String = self.input[val_start..self.pos]
540 .chars()
541 .filter(|&c| c != '_')
542 .collect();
543 let val = i64::from_str_radix(&val_str, base).unwrap_or(0);
544 self.yyval = if self.force_float {
545 MathNum::Float(if is_neg { -(val as f64) } else { val as f64 })
546 } else {
547 MathNum::Integer(if is_neg { -val } else { val })
548 };
549 return MathTok::Num;
550 }
551
552 let int_str: String = self.input[num_start..self.pos]
554 .chars()
555 .filter(|&c| c != '_')
556 .collect();
557 let val: i64 = int_str.parse().unwrap_or(0);
558 self.yyval = if self.force_float {
559 MathNum::Float(if is_neg { -(val as f64) } else { val as f64 })
560 } else {
561 MathNum::Integer(if is_neg { -val } else { val })
562 };
563 MathTok::Num
564 }
565
566 fn zzlex(&mut self) -> MathTok {
568 self.yyval = MathNum::Integer(0);
569
570 loop {
571 let c = match self.advance() {
572 Some(c) => c,
573 None => return MathTok::Eoi,
574 };
575
576 match c {
577 ' ' | '\t' | '\n' | '"' => continue,
578
579 '+' => {
580 if self.peek() == Some('+') {
581 self.advance();
582 return if self.unary {
583 MathTok::PrePlus
584 } else {
585 MathTok::PostPlus
586 };
587 }
588 if self.peek() == Some('=') {
589 self.advance();
590 return MathTok::PlusEq;
591 }
592 return if self.unary {
593 MathTok::UPlus
594 } else {
595 MathTok::Plus
596 };
597 }
598
599 '-' => {
600 if self.peek() == Some('-') {
601 self.advance();
602 return if self.unary {
603 MathTok::PreMinus
604 } else {
605 MathTok::PostMinus
606 };
607 }
608 if self.peek() == Some('=') {
609 self.advance();
610 return MathTok::MinusEq;
611 }
612 if self.unary {
613 if let Some(next) = self.peek() {
615 if Self::is_digit(next) || next == '.' {
616 self.pos -= 1; return self.lex_constant();
618 }
619 }
620 return MathTok::UMinus;
621 }
622 return MathTok::Minus;
623 }
624
625 '(' => return MathTok::InPar,
626 ')' => return MathTok::OutPar,
627
628 '!' => {
629 if self.peek() == Some('=') {
630 self.advance();
631 return MathTok::Neq;
632 }
633 return MathTok::Not;
634 }
635
636 '~' => return MathTok::Comp,
637
638 '&' => {
639 if self.peek() == Some('&') {
640 self.advance();
641 if self.peek() == Some('=') {
642 self.advance();
643 return MathTok::DAndEq;
644 }
645 return MathTok::DAnd;
646 }
647 if self.peek() == Some('=') {
648 self.advance();
649 return MathTok::AndEq;
650 }
651 return MathTok::And;
652 }
653
654 '|' => {
655 if self.peek() == Some('|') {
656 self.advance();
657 if self.peek() == Some('=') {
658 self.advance();
659 return MathTok::DOrEq;
660 }
661 return MathTok::DOr;
662 }
663 if self.peek() == Some('=') {
664 self.advance();
665 return MathTok::OrEq;
666 }
667 return MathTok::Or;
668 }
669
670 '^' => {
671 if self.peek() == Some('^') {
672 self.advance();
673 if self.peek() == Some('=') {
674 self.advance();
675 return MathTok::DXorEq;
676 }
677 return MathTok::DXor;
678 }
679 if self.peek() == Some('=') {
680 self.advance();
681 return MathTok::XorEq;
682 }
683 return MathTok::Xor;
684 }
685
686 '*' => {
687 if self.peek() == Some('*') {
688 self.advance();
689 if self.peek() == Some('=') {
690 self.advance();
691 return MathTok::PowerEq;
692 }
693 return MathTok::Power;
694 }
695 if self.peek() == Some('=') {
696 self.advance();
697 return MathTok::MulEq;
698 }
699 return MathTok::Mul;
700 }
701
702 '/' => {
703 if self.peek() == Some('=') {
704 self.advance();
705 return MathTok::DivEq;
706 }
707 return MathTok::Div;
708 }
709
710 '%' => {
711 if self.peek() == Some('=') {
712 self.advance();
713 return MathTok::ModEq;
714 }
715 return MathTok::Mod;
716 }
717
718 '<' => {
719 if self.peek() == Some('<') {
720 self.advance();
721 if self.peek() == Some('=') {
722 self.advance();
723 return MathTok::ShLeftEq;
724 }
725 return MathTok::ShLeft;
726 }
727 if self.peek() == Some('=') {
728 self.advance();
729 return MathTok::Leq;
730 }
731 return MathTok::Les;
732 }
733
734 '>' => {
735 if self.peek() == Some('>') {
736 self.advance();
737 if self.peek() == Some('=') {
738 self.advance();
739 return MathTok::ShRightEq;
740 }
741 return MathTok::ShRight;
742 }
743 if self.peek() == Some('=') {
744 self.advance();
745 return MathTok::Geq;
746 }
747 return MathTok::Gre;
748 }
749
750 '=' => {
751 if self.peek() == Some('=') {
752 self.advance();
753 return MathTok::Deq;
754 }
755 return MathTok::Eq;
756 }
757
758 '$' => {
759 self.yyval = MathNum::Integer(self.pid);
761 return MathTok::Num;
762 }
763
764 '?' => {
765 if self.unary {
766 self.yyval = MathNum::Integer(self.lastval as i64);
768 return MathTok::Num;
769 }
770 return MathTok::Quest;
771 }
772
773 ':' => return MathTok::Colon,
774 ',' => return MathTok::Comma,
775
776 '[' => {
777 if Self::is_digit(self.peek().unwrap_or('\0')) {
779 let base_start = self.pos;
781 while let Some(c) = self.peek() {
782 if Self::is_digit(c) {
783 self.advance();
784 } else {
785 break;
786 }
787 }
788 if self.peek() != Some(']') {
789 self.error = Some("bad base syntax".to_string());
790 return MathTok::Eoi;
791 }
792 let base_str: String = self.input[base_start..self.pos].to_string();
793 let base: u32 = base_str.parse().unwrap_or(10);
794 self.advance(); if !Self::is_digit(self.peek().unwrap_or('\0'))
797 && !Self::is_ident_start(self.peek().unwrap_or('\0'))
798 {
799 self.error = Some("bad base syntax".to_string());
800 return MathTok::Eoi;
801 }
802
803 let val_start = self.pos;
804 while let Some(c) = self.peek() {
805 if c.is_ascii_alphanumeric() {
806 self.advance();
807 } else {
808 break;
809 }
810 }
811 let val_str = &self.input[val_start..self.pos];
812 let val = i64::from_str_radix(val_str, base).unwrap_or(0);
813 self.lastbase = base as i32;
814 self.yyval = MathNum::Integer(val);
815 return MathTok::Num;
816 }
817 if self.peek() == Some('#') {
819 while let Some(c) = self.peek() {
820 if c == ']' {
821 self.advance();
822 break;
823 }
824 self.advance();
825 }
826 continue;
827 }
828 self.error = Some("bad output format specification".to_string());
829 return MathTok::Eoi;
830 }
831
832 '#' => {
833 if self.peek() == Some('\\') || self.peek() == Some('#') {
835 self.advance();
836 if let Some(ch) = self.advance() {
837 self.yyval = MathNum::Integer(ch as i64);
838 return MathTok::Num;
839 }
840 }
841 let id_start = self.pos;
843 while let Some(c) = self.peek() {
844 if Self::is_ident(c) {
845 self.advance();
846 } else {
847 break;
848 }
849 }
850 if self.pos > id_start {
851 self.yylval = self.input[id_start..self.pos].to_string();
852 return MathTok::CId;
853 }
854 continue;
855 }
856
857 _ => {
858 if Self::is_digit(c)
859 || (c == '.' && Self::is_digit(self.peek().unwrap_or('\0')))
860 {
861 self.pos -= c.len_utf8();
862 return self.lex_constant();
863 }
864
865 if Self::is_ident_start(c) {
866 let id_start = self.pos - c.len_utf8();
867 while let Some(c) = self.peek() {
868 if Self::is_ident(c) {
869 self.advance();
870 } else {
871 break;
872 }
873 }
874
875 let id = &self.input[id_start..self.pos];
876
877 let id_lower = id.to_lowercase();
879 if id_lower == "nan" {
880 self.yyval = MathNum::Float(f64::NAN);
881 return MathTok::Num;
882 }
883 if id_lower == "inf" {
884 self.yyval = MathNum::Float(f64::INFINITY);
885 return MathTok::Num;
886 }
887
888 if self.peek() == Some('(') {
890 let func_start = id_start;
892 self.advance(); let mut depth = 1;
894 while let Some(c) = self.peek() {
895 self.advance();
896 if c == '(' {
897 depth += 1;
898 } else if c == ')' {
899 depth -= 1;
900 if depth == 0 {
901 break;
902 }
903 }
904 }
905 self.yylval = self.input[func_start..self.pos].to_string();
906 return MathTok::Func;
907 }
908
909 if self.peek() == Some('[') {
911 self.advance(); let mut depth = 1;
913 while let Some(c) = self.peek() {
914 self.advance();
915 if c == '[' {
916 depth += 1;
917 } else if c == ']' {
918 depth -= 1;
919 if depth == 0 {
920 break;
921 }
922 }
923 }
924 }
925
926 self.yylval = self.input[id_start..self.pos].to_string();
927 return MathTok::Id;
928 }
929
930 return MathTok::Eoi;
931 }
932 }
933 }
934 }
935
936 fn push(&mut self, val: MathNum, lval: Option<String>) {
937 self.stack.push(MathValue { val, lval });
938 }
939
940 fn pop(&mut self) -> MathNum {
941 if let Some(mv) = self.stack.pop() {
942 if matches!(mv.val, MathNum::Unset) {
943 if let Some(ref name) = mv.lval {
944 return self.get_variable(name);
945 }
946 }
947 mv.val
948 } else {
949 self.error = Some("stack underflow".to_string());
950 MathNum::Integer(0)
951 }
952 }
953
954 fn pop_with_lval(&mut self) -> MathValue {
955 self.stack.pop().unwrap_or_default()
956 }
957
958 fn get_value(&self, mv: &MathValue) -> MathNum {
959 if matches!(mv.val, MathNum::Unset) {
960 if let Some(ref name) = mv.lval {
961 return self.get_variable(name);
962 }
963 }
964 mv.val
965 }
966
967 fn get_variable(&self, name: &str) -> MathNum {
968 let base_name = if let Some(bracket) = name.find('[') {
970 &name[..bracket]
971 } else {
972 name
973 };
974 self.variables
975 .get(base_name)
976 .copied()
977 .unwrap_or(MathNum::Integer(0))
978 }
979
980 fn set_variable(&mut self, name: &str, val: MathNum) -> MathNum {
981 let base_name = if let Some(bracket) = name.find('[') {
982 &name[..bracket]
983 } else {
984 name
985 };
986 self.variables.insert(base_name.to_string(), val);
987 val
988 }
989
990 fn op(&mut self, what: MathTok) {
992 if self.error.is_some() {
993 return;
994 }
995
996 let tp = OP_TYPE[what as usize];
997
998 if (tp & (OP_A2 | OP_A2IR | OP_A2IO | OP_E2 | OP_E2IO)) != 0 {
1000 if self.stack.len() < 2 {
1001 self.error = Some("not enough operands".to_string());
1002 return;
1003 }
1004
1005 let b = self.pop();
1006 let mv_a = self.pop_with_lval();
1007 let a = if matches!(mv_a.val, MathNum::Unset) {
1008 if let Some(ref name) = mv_a.lval {
1009 self.get_variable(name)
1010 } else {
1011 MathNum::Integer(0)
1012 }
1013 } else {
1014 mv_a.val
1015 };
1016
1017 let (a, b) = if (tp & (OP_A2IO | OP_E2IO)) != 0 {
1019 (MathNum::Integer(a.to_int()), MathNum::Integer(b.to_int()))
1021 } else if a.is_float() != b.is_float() && what != MathTok::Comma {
1022 (MathNum::Float(a.to_float()), MathNum::Float(b.to_float()))
1024 } else {
1025 (a, b)
1026 };
1027
1028 let result = if self.noeval > 0 {
1029 MathNum::Integer(0)
1030 } else {
1031 let is_float = a.is_float();
1032 match what {
1033 MathTok::And | MathTok::AndEq => MathNum::Integer(a.to_int() & b.to_int()),
1034 MathTok::Xor | MathTok::XorEq => MathNum::Integer(a.to_int() ^ b.to_int()),
1035 MathTok::Or | MathTok::OrEq => MathNum::Integer(a.to_int() | b.to_int()),
1036
1037 MathTok::Mul | MathTok::MulEq => {
1038 if is_float {
1039 MathNum::Float(a.to_float() * b.to_float())
1040 } else {
1041 MathNum::Integer(a.to_int().wrapping_mul(b.to_int()))
1042 }
1043 }
1044
1045 MathTok::Div | MathTok::DivEq => {
1046 if b.is_zero() {
1047 self.error = Some("division by zero".to_string());
1048 return;
1049 }
1050 if is_float {
1051 MathNum::Float(a.to_float() / b.to_float())
1052 } else {
1053 let bi = b.to_int();
1054 if bi == -1 {
1055 MathNum::Integer(a.to_int().wrapping_neg())
1056 } else {
1057 MathNum::Integer(a.to_int() / bi)
1058 }
1059 }
1060 }
1061
1062 MathTok::Mod | MathTok::ModEq => {
1063 if b.is_zero() {
1064 self.error = Some("division by zero".to_string());
1065 return;
1066 }
1067 if is_float {
1068 MathNum::Float(a.to_float() % b.to_float())
1069 } else {
1070 let bi = b.to_int();
1071 if bi == -1 {
1072 MathNum::Integer(0)
1073 } else {
1074 MathNum::Integer(a.to_int() % bi)
1075 }
1076 }
1077 }
1078
1079 MathTok::Plus | MathTok::PlusEq => {
1080 if is_float {
1081 MathNum::Float(a.to_float() + b.to_float())
1082 } else {
1083 MathNum::Integer(a.to_int().wrapping_add(b.to_int()))
1084 }
1085 }
1086
1087 MathTok::Minus | MathTok::MinusEq => {
1088 if is_float {
1089 MathNum::Float(a.to_float() - b.to_float())
1090 } else {
1091 MathNum::Integer(a.to_int().wrapping_sub(b.to_int()))
1092 }
1093 }
1094
1095 MathTok::ShLeft | MathTok::ShLeftEq => {
1096 MathNum::Integer(a.to_int() << (b.to_int() as u32 & 63))
1097 }
1098 MathTok::ShRight | MathTok::ShRightEq => {
1099 MathNum::Integer(a.to_int() >> (b.to_int() as u32 & 63))
1100 }
1101
1102 MathTok::Les => MathNum::Integer(if is_float {
1103 (a.to_float() < b.to_float()) as i64
1104 } else {
1105 (a.to_int() < b.to_int()) as i64
1106 }),
1107 MathTok::Leq => MathNum::Integer(if is_float {
1108 (a.to_float() <= b.to_float()) as i64
1109 } else {
1110 (a.to_int() <= b.to_int()) as i64
1111 }),
1112 MathTok::Gre => MathNum::Integer(if is_float {
1113 (a.to_float() > b.to_float()) as i64
1114 } else {
1115 (a.to_int() > b.to_int()) as i64
1116 }),
1117 MathTok::Geq => MathNum::Integer(if is_float {
1118 (a.to_float() >= b.to_float()) as i64
1119 } else {
1120 (a.to_int() >= b.to_int()) as i64
1121 }),
1122 MathTok::Deq => MathNum::Integer(if is_float {
1123 (a.to_float() == b.to_float()) as i64
1124 } else {
1125 (a.to_int() == b.to_int()) as i64
1126 }),
1127 MathTok::Neq => MathNum::Integer(if is_float {
1128 (a.to_float() != b.to_float()) as i64
1129 } else {
1130 (a.to_int() != b.to_int()) as i64
1131 }),
1132
1133 MathTok::DAnd | MathTok::DAndEq => {
1134 MathNum::Integer((a.to_int() != 0 && b.to_int() != 0) as i64)
1135 }
1136 MathTok::DOr | MathTok::DOrEq => {
1137 MathNum::Integer((a.to_int() != 0 || b.to_int() != 0) as i64)
1138 }
1139 MathTok::DXor | MathTok::DXorEq => {
1140 let ai = a.to_int() != 0;
1141 let bi = b.to_int() != 0;
1142 MathNum::Integer((ai != bi) as i64)
1143 }
1144
1145 MathTok::Power | MathTok::PowerEq => {
1146 let bi = b.to_int();
1147 if !is_float && bi >= 0 {
1148 let mut result = 1i64;
1149 let base = a.to_int();
1150 for _ in 0..bi {
1151 result = result.wrapping_mul(base);
1152 }
1153 MathNum::Integer(result)
1154 } else {
1155 let af = a.to_float();
1156 let bf = b.to_float();
1157 if bf <= 0.0 && af == 0.0 {
1158 self.error = Some("division by zero".to_string());
1159 return;
1160 }
1161 if af < 0.0 && bf != bf.trunc() {
1162 self.error = Some("imaginary power".to_string());
1163 return;
1164 }
1165 MathNum::Float(af.powf(bf))
1166 }
1167 }
1168
1169 MathTok::Comma => b,
1170 MathTok::Eq => b,
1171
1172 _ => MathNum::Integer(0),
1173 }
1174 };
1175
1176 if (tp & (OP_E2 | OP_E2IO)) != 0 {
1178 if let Some(ref name) = mv_a.lval {
1179 let final_val = self.set_variable(name, result);
1180 self.push(final_val, Some(name.clone()));
1181 } else {
1182 self.error = Some("lvalue required".to_string());
1183 self.push(MathNum::Integer(0), None);
1184 }
1185 } else {
1186 self.push(result, None);
1187 }
1188 return;
1189 }
1190
1191 if self.stack.is_empty() {
1193 self.error = Some("stack empty".to_string());
1194 return;
1195 }
1196
1197 let mv = self.pop_with_lval();
1198 let val = if matches!(mv.val, MathNum::Unset) {
1199 if let Some(ref name) = mv.lval {
1200 self.get_variable(name)
1201 } else {
1202 MathNum::Integer(0)
1203 }
1204 } else {
1205 mv.val
1206 };
1207
1208 match what {
1209 MathTok::Not => {
1210 let result = MathNum::Integer(if val.is_zero() { 1 } else { 0 });
1211 self.push(result, None);
1212 }
1213 MathTok::Comp => {
1214 let result = MathNum::Integer(!val.to_int());
1215 self.push(result, None);
1216 }
1217 MathTok::UPlus => {
1218 self.push(val, None);
1219 }
1220 MathTok::UMinus => {
1221 let result = if val.is_float() {
1222 MathNum::Float(-val.to_float())
1223 } else {
1224 MathNum::Integer(-val.to_int())
1225 };
1226 self.push(result, None);
1227 }
1228 MathTok::PostPlus => {
1229 if let Some(ref name) = mv.lval {
1230 let new_val = if val.is_float() {
1231 MathNum::Float(val.to_float() + 1.0)
1232 } else {
1233 MathNum::Integer(val.to_int() + 1)
1234 };
1235 self.set_variable(name, new_val);
1236 }
1237 self.push(val, None); }
1239 MathTok::PostMinus => {
1240 if let Some(ref name) = mv.lval {
1241 let new_val = if val.is_float() {
1242 MathNum::Float(val.to_float() - 1.0)
1243 } else {
1244 MathNum::Integer(val.to_int() - 1)
1245 };
1246 self.set_variable(name, new_val);
1247 }
1248 self.push(val, None);
1249 }
1250 MathTok::PrePlus => {
1251 let new_val = if val.is_float() {
1252 MathNum::Float(val.to_float() + 1.0)
1253 } else {
1254 MathNum::Integer(val.to_int() + 1)
1255 };
1256 if let Some(ref name) = mv.lval {
1257 self.set_variable(name, new_val);
1258 }
1259 self.push(new_val, mv.lval);
1260 }
1261 MathTok::PreMinus => {
1262 let new_val = if val.is_float() {
1263 MathNum::Float(val.to_float() - 1.0)
1264 } else {
1265 MathNum::Integer(val.to_int() - 1)
1266 };
1267 if let Some(ref name) = mv.lval {
1268 self.set_variable(name, new_val);
1269 }
1270 self.push(new_val, mv.lval);
1271 }
1272 MathTok::Quest => {
1273 if self.stack.len() < 2 {
1277 self.error = Some("?: needs 3 operands".to_string());
1278 return;
1279 }
1280 let false_val = val;
1281 let true_val = self.pop();
1282 let cond = self.pop();
1283 let result = if !cond.is_zero() { true_val } else { false_val };
1284 self.push(result, None);
1285 }
1286 MathTok::Colon => {
1287 self.error = Some("':' without '?'".to_string());
1288 }
1289 _ => {
1290 self.error = Some("unknown operator".to_string());
1291 }
1292 }
1293 }
1294
1295 fn bop(&mut self, tk: MathTok) {
1297 if self.stack.is_empty() {
1298 return;
1299 }
1300 let mv = &self.stack[self.stack.len() - 1];
1301 let val = if matches!(mv.val, MathNum::Unset) {
1302 if let Some(ref name) = mv.lval {
1303 self.get_variable(name)
1304 } else {
1305 MathNum::Integer(0)
1306 }
1307 } else {
1308 mv.val
1309 };
1310
1311 let tst = !val.is_zero();
1312 match tk {
1313 MathTok::DAnd | MathTok::DAndEq => {
1314 if !tst {
1315 self.noeval += 1;
1316 }
1317 }
1318 MathTok::DOr | MathTok::DOrEq => {
1319 if tst {
1320 self.noeval += 1;
1321 }
1322 }
1323 _ => {}
1324 }
1325 }
1326
1327 fn top_prec(&self) -> u8 {
1328 self.prec[MathTok::Comma as usize] + 1
1329 }
1330
1331 fn check_unary(&mut self) {
1332 let tp = OP_TYPE[self.mtok as usize];
1333 self.unary = (tp & OP_OPF) == 0;
1336 }
1337
1338 fn mathparse(&mut self, pc: u8) {
1340 if self.error.is_some() {
1341 return;
1342 }
1343
1344 self.mtok = self.zzlex();
1345
1346 if pc == self.top_prec() && self.mtok == MathTok::Eoi {
1348 return;
1349 }
1350
1351 self.check_unary();
1352
1353 while self.prec[self.mtok as usize] <= pc {
1354 if self.error.is_some() {
1355 return;
1356 }
1357
1358 match self.mtok {
1359 MathTok::Num => {
1360 self.push(self.yyval, None);
1361 }
1362 MathTok::Id => {
1363 let lval = self.yylval.clone();
1364 if self.noeval > 0 {
1365 self.push(MathNum::Integer(0), Some(lval));
1366 } else {
1367 self.push(MathNum::Unset, Some(lval));
1368 }
1369 }
1370 MathTok::CId => {
1371 let lval = self.yylval.clone();
1372 let val = if self.noeval > 0 {
1373 MathNum::Integer(0)
1374 } else {
1375 self.get_variable(&lval)
1376 };
1377 self.push(val, Some(lval));
1378 }
1379 MathTok::Func => {
1380 let func_call = self.yylval.clone();
1381 let val = if self.noeval > 0 {
1382 MathNum::Integer(0)
1383 } else {
1384 self.call_math_func(&func_call)
1385 };
1386 self.push(val, None);
1387 }
1388 MathTok::InPar => {
1389 self.mathparse(self.top_prec());
1390 if self.mtok != MathTok::OutPar {
1391 if self.error.is_none() {
1392 self.error = Some("')' expected".to_string());
1393 }
1394 return;
1395 }
1396 }
1397 MathTok::Quest => {
1398 if self.stack.is_empty() {
1400 self.error = Some("bad math expression".to_string());
1401 return;
1402 }
1403 let mv = &self.stack[self.stack.len() - 1];
1404 let cond = self.get_value(mv);
1405
1406 let q = !cond.is_zero();
1407 if !q {
1408 self.noeval += 1;
1409 }
1410 let colon_prec = self.prec[MathTok::Colon as usize];
1411 self.mathparse(colon_prec - 1);
1412 if !q {
1413 self.noeval -= 1;
1414 }
1415
1416 if self.mtok != MathTok::Colon {
1417 if self.error.is_none() {
1418 self.error = Some("':' expected".to_string());
1419 }
1420 return;
1421 }
1422
1423 if q {
1424 self.noeval += 1;
1425 }
1426 let quest_prec = self.prec[MathTok::Quest as usize];
1427 self.mathparse(quest_prec);
1428 if q {
1429 self.noeval -= 1;
1430 }
1431
1432 self.op(MathTok::Quest);
1433 continue;
1434 }
1435 _ => {
1436 let otok = self.mtok;
1438 let onoeval = self.noeval;
1439 let tp = OP_TYPE[otok as usize];
1440 if (tp & 0x03) == BOOL {
1441 self.bop(otok);
1442 }
1443 let otok_prec = self.prec[otok as usize];
1444 let adjust = if (tp & 0x01) != RL { 1 } else { 0 };
1446 self.mathparse(otok_prec - adjust);
1447 self.noeval = onoeval;
1448 self.op(otok);
1449 continue;
1450 }
1451 }
1452
1453 self.mtok = self.zzlex();
1455 self.check_unary();
1456 }
1457 }
1458
1459 fn call_math_func(&mut self, call: &str) -> MathNum {
1461 let paren = call.find('(').unwrap_or(call.len());
1463 let name = &call[..paren];
1464 let args_str = if paren < call.len() {
1465 &call[paren + 1..call.len() - 1]
1466 } else {
1467 ""
1468 };
1469
1470 let args: Vec<f64> = if args_str.is_empty() {
1472 vec![]
1473 } else {
1474 args_str
1475 .split(',')
1476 .filter_map(|s| {
1477 let mut eval = MathEval::new(s.trim());
1478 eval.variables = self.variables.clone();
1479 match eval.evaluate() {
1480 Ok(n) => Some(n.to_float()),
1481 Err(_) => None,
1482 }
1483 })
1484 .collect()
1485 };
1486
1487 let result = match name {
1489 "abs" => args.get(0).map(|x| x.abs()).unwrap_or(0.0),
1490 "acos" => args.get(0).map(|x| x.acos()).unwrap_or(0.0),
1491 "asin" => args.get(0).map(|x| x.asin()).unwrap_or(0.0),
1492 "atan" => args.get(0).map(|x| x.atan()).unwrap_or(0.0),
1493 "atan2" => {
1494 let y = args.get(0).copied().unwrap_or(0.0);
1495 let x = args.get(1).copied().unwrap_or(1.0);
1496 y.atan2(x)
1497 }
1498 "ceil" => args.get(0).map(|x| x.ceil()).unwrap_or(0.0),
1499 "cos" => args.get(0).map(|x| x.cos()).unwrap_or(1.0),
1500 "cosh" => args.get(0).map(|x| x.cosh()).unwrap_or(1.0),
1501 "exp" => args.get(0).map(|x| x.exp()).unwrap_or(1.0),
1502 "floor" => args.get(0).map(|x| x.floor()).unwrap_or(0.0),
1503 "hypot" => {
1504 let x = args.get(0).copied().unwrap_or(0.0);
1505 let y = args.get(1).copied().unwrap_or(0.0);
1506 x.hypot(y)
1507 }
1508 "int" => args.get(0).map(|x| x.trunc()).unwrap_or(0.0),
1509 "log" => args.get(0).map(|x| x.ln()).unwrap_or(0.0),
1510 "log10" => args.get(0).map(|x| x.log10()).unwrap_or(0.0),
1511 "log2" => args.get(0).map(|x| x.log2()).unwrap_or(0.0),
1512 "max" => args.iter().copied().fold(f64::NEG_INFINITY, f64::max),
1513 "min" => args.iter().copied().fold(f64::INFINITY, f64::min),
1514 "pow" => {
1515 let base = args.get(0).copied().unwrap_or(0.0);
1516 let exp = args.get(1).copied().unwrap_or(1.0);
1517 base.powf(exp)
1518 }
1519 "rand" => rand::random::<f64>(),
1520 "round" => args.get(0).map(|x| x.round()).unwrap_or(0.0),
1521 "sin" => args.get(0).map(|x| x.sin()).unwrap_or(0.0),
1522 "sinh" => args.get(0).map(|x| x.sinh()).unwrap_or(0.0),
1523 "sqrt" => args.get(0).map(|x| x.sqrt()).unwrap_or(0.0),
1524 "tan" => args.get(0).map(|x| x.tan()).unwrap_or(0.0),
1525 "tanh" => args.get(0).map(|x| x.tanh()).unwrap_or(0.0),
1526 "trunc" => args.get(0).map(|x| x.trunc()).unwrap_or(0.0),
1527 _ => {
1528 self.error = Some(format!("unknown function: {}", name));
1529 0.0
1530 }
1531 };
1532
1533 MathNum::Float(result)
1534 }
1535
1536 pub fn evaluate(&mut self) -> Result<MathNum, String> {
1538 self.prec = if self.c_precedences { &C_PREC } else { &Z_PREC };
1539
1540 while let Some(c) = self.peek() {
1542 if c.is_whitespace() || c == '\u{a1}' {
1543 self.advance();
1544 } else {
1545 break;
1546 }
1547 }
1548
1549 if self.pos >= self.input.len() {
1550 return Ok(MathNum::Integer(0));
1551 }
1552
1553 self.mathparse(self.top_prec());
1554
1555 if let Some(ref err) = self.error {
1556 return Err(err.clone());
1557 }
1558
1559 while let Some(c) = self.peek() {
1561 if c.is_whitespace() {
1562 self.advance();
1563 } else {
1564 return Err(format!("illegal character: {}", c));
1565 }
1566 }
1567
1568 if self.stack.is_empty() {
1569 return Ok(MathNum::Integer(0));
1570 }
1571
1572 let mv = self.stack.pop().unwrap();
1573 let result = if matches!(mv.val, MathNum::Unset) {
1574 if let Some(ref name) = mv.lval {
1575 self.get_variable(name)
1576 } else {
1577 MathNum::Integer(0)
1578 }
1579 } else {
1580 mv.val
1581 };
1582
1583 Ok(result)
1584 }
1585
1586 pub fn get_variables(&self) -> &HashMap<String, MathNum> {
1588 &self.variables
1589 }
1590}
1591
1592pub fn matheval(expr: &str) -> Result<MathNum, String> {
1594 let mut eval = MathEval::new(expr);
1595 eval.evaluate()
1596}
1597
1598pub fn mathevali(expr: &str) -> Result<i64, String> {
1600 matheval(expr).map(|n| n.to_int())
1601}
1602
1603pub fn mathevalf(expr: &str) -> Result<f64, String> {
1605 matheval(expr).map(|n| n.to_float())
1606}
1607
1608#[cfg(test)]
1609mod tests {
1610 use super::*;
1611
1612 #[test]
1613 fn test_basic_arithmetic() {
1614 assert_eq!(mathevali("1 + 2").unwrap(), 3);
1615 assert_eq!(mathevali("10 - 3").unwrap(), 7);
1616 assert_eq!(mathevali("4 * 5").unwrap(), 20);
1617 assert_eq!(mathevali("20 / 4").unwrap(), 5);
1618 assert_eq!(mathevali("17 % 5").unwrap(), 2);
1619 }
1620
1621 #[test]
1622 fn test_precedence() {
1623 assert_eq!(mathevali("2 + 3 * 4").unwrap(), 14);
1624 assert_eq!(mathevali("(2 + 3) * 4").unwrap(), 20);
1625 assert_eq!(mathevali("2 ** 3 ** 2").unwrap(), 512); }
1627
1628 #[test]
1629 fn test_comparison() {
1630 assert_eq!(mathevali("5 > 3").unwrap(), 1);
1631 assert_eq!(mathevali("5 < 3").unwrap(), 0);
1632 assert_eq!(mathevali("5 == 5").unwrap(), 1);
1633 assert_eq!(mathevali("5 != 3").unwrap(), 1);
1634 assert_eq!(mathevali("5 >= 5").unwrap(), 1);
1635 assert_eq!(mathevali("5 <= 5").unwrap(), 1);
1636 }
1637
1638 #[test]
1639 fn test_logical() {
1640 assert_eq!(mathevali("1 && 1").unwrap(), 1);
1641 assert_eq!(mathevali("1 && 0").unwrap(), 0);
1642 assert_eq!(mathevali("1 || 0").unwrap(), 1);
1643 assert_eq!(mathevali("0 || 0").unwrap(), 0);
1644 assert_eq!(mathevali("!0").unwrap(), 1);
1645 assert_eq!(mathevali("!1").unwrap(), 0);
1646 }
1647
1648 #[test]
1649 fn test_bitwise() {
1650 assert_eq!(mathevali("5 & 3").unwrap(), 1);
1651 assert_eq!(mathevali("5 | 3").unwrap(), 7);
1652 assert_eq!(mathevali("5 ^ 3").unwrap(), 6);
1653 assert_eq!(mathevali("~0").unwrap(), -1);
1654 assert_eq!(mathevali("1 << 4").unwrap(), 16);
1655 assert_eq!(mathevali("16 >> 2").unwrap(), 4);
1656 }
1657
1658 #[test]
1659 fn test_ternary() {
1660 assert_eq!(mathevali("1 ? 10 : 20").unwrap(), 10);
1661 assert_eq!(mathevali("0 ? 10 : 20").unwrap(), 20);
1662 assert_eq!(mathevali("(5 > 3) ? 100 : 200").unwrap(), 100);
1663 }
1664
1665 #[test]
1666 fn test_power() {
1667 assert_eq!(mathevali("2 ** 10").unwrap(), 1024);
1668 assert_eq!(mathevali("3 ** 3").unwrap(), 27);
1669 assert!((mathevalf("2.0 ** 0.5").unwrap() - std::f64::consts::SQRT_2).abs() < 0.0001);
1670 }
1671
1672 #[test]
1673 fn test_float() {
1674 assert!((mathevalf("3.14 + 0.01").unwrap() - 3.15).abs() < 0.0001);
1675 assert!((mathevalf("1.5 * 2.0").unwrap() - 3.0).abs() < 0.0001);
1676 }
1677
1678 #[test]
1679 fn test_unary() {
1680 assert_eq!(mathevali("-5").unwrap(), -5);
1681 assert_eq!(mathevali("- -5").unwrap(), 5); assert_eq!(mathevali("+5").unwrap(), 5);
1683 assert_eq!(mathevali("-(-5)").unwrap(), 5);
1684 }
1685
1686 #[test]
1687 fn test_base() {
1688 assert_eq!(mathevali("0xFF").unwrap(), 255);
1689 assert_eq!(mathevali("0b1010").unwrap(), 10);
1690 assert_eq!(mathevali("16#FF").unwrap(), 255);
1691 assert_eq!(mathevali("2#1010").unwrap(), 10);
1692 assert_eq!(mathevali("[16]FF").unwrap(), 255);
1693 }
1694
1695 #[test]
1696 fn test_variables() {
1697 let mut vars = HashMap::new();
1698 vars.insert("x".to_string(), MathNum::Integer(10));
1699 vars.insert("y".to_string(), MathNum::Integer(20));
1700
1701 let mut eval = MathEval::new("x + y").with_variables(vars);
1702 assert_eq!(eval.evaluate().unwrap().to_int(), 30);
1703 }
1704
1705 #[test]
1706 fn test_assignment() {
1707 let mut eval = MathEval::new("x = 5");
1708 eval.evaluate().unwrap();
1709 assert_eq!(eval.variables.get("x").unwrap().to_int(), 5);
1710
1711 let mut eval2 = MathEval::new("x = 5, x += 3");
1712 let result = eval2.evaluate().unwrap();
1713 assert_eq!(result.to_int(), 8);
1714 }
1715
1716 #[test]
1717 fn test_increment() {
1718 let mut vars = HashMap::new();
1719 vars.insert("x".to_string(), MathNum::Integer(5));
1720
1721 let mut eval = MathEval::new("++x").with_variables(vars.clone());
1722 assert_eq!(eval.evaluate().unwrap().to_int(), 6);
1723 assert_eq!(eval.variables.get("x").unwrap().to_int(), 6);
1724
1725 let mut eval2 = MathEval::new("x++").with_variables(vars.clone());
1726 assert_eq!(eval2.evaluate().unwrap().to_int(), 5);
1727 assert_eq!(eval2.variables.get("x").unwrap().to_int(), 6);
1728 }
1729
1730 #[test]
1731 fn test_functions() {
1732 assert!((mathevalf("sqrt(4)").unwrap() - 2.0).abs() < 0.0001);
1733 assert!((mathevalf("sin(0)").unwrap()).abs() < 0.0001);
1734 assert!((mathevalf("cos(0)").unwrap() - 1.0).abs() < 0.0001);
1735 assert!((mathevalf("abs(-5)").unwrap() - 5.0).abs() < 0.0001);
1736 assert!((mathevalf("floor(3.7)").unwrap() - 3.0).abs() < 0.0001);
1737 assert!((mathevalf("ceil(3.2)").unwrap() - 4.0).abs() < 0.0001);
1738 }
1739
1740 #[test]
1741 fn test_special_values() {
1742 assert!(mathevalf("Inf").unwrap().is_infinite());
1743 assert!(mathevalf("NaN").unwrap().is_nan());
1744 }
1745
1746 #[test]
1747 fn test_errors() {
1748 assert!(matheval("1 / 0").is_err());
1749 assert!(matheval("1 +").is_err());
1750 assert!(matheval("()").is_ok()); }
1752
1753 #[test]
1754 fn test_underscore_in_numbers() {
1755 assert_eq!(mathevali("1_000_000").unwrap(), 1000000);
1756 assert_eq!(mathevali("0xFF_FF").unwrap(), 65535);
1757 }
1758
1759 #[test]
1760 fn test_comma_operator() {
1761 assert_eq!(mathevali("1, 2, 3").unwrap(), 3);
1762 assert_eq!(mathevali("(x = 1, y = 2, x + y)").unwrap(), 3);
1763 }
1764}