1use crate::error::PdfError;
7use std::collections::{HashMap, HashSet, VecDeque};
8use std::fmt;
9
10#[derive(Debug, Clone)]
12pub struct CalculationEngine {
13 field_values: HashMap<String, FieldValue>,
15 calculations: HashMap<String, Calculation>,
17 dependencies: HashMap<String, HashSet<String>>,
19 calculation_order: Vec<String>,
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub enum FieldValue {
26 Number(f64),
28 Text(String),
30 Boolean(bool),
32 Empty,
34}
35
36impl FieldValue {
37 pub fn to_number(&self) -> f64 {
39 match self {
40 FieldValue::Number(n) => *n,
41 FieldValue::Text(s) => s.parse::<f64>().unwrap_or(0.0),
42 FieldValue::Boolean(b) => {
43 if *b {
44 1.0
45 } else {
46 0.0
47 }
48 }
49 FieldValue::Empty => 0.0,
50 }
51 }
52
53 #[allow(clippy::inherent_to_string)]
55 pub fn to_string(&self) -> String {
56 match self {
57 FieldValue::Number(n) => {
58 if n.fract() == 0.0 {
60 format!("{:.0}", n)
61 } else {
62 format!("{:.2}", n)
63 }
64 }
65 FieldValue::Text(s) => s.clone(),
66 FieldValue::Boolean(b) => b.to_string(),
67 FieldValue::Empty => String::new(),
68 }
69 }
70}
71
72#[derive(Debug, Clone)]
74pub enum Calculation {
75 Arithmetic(ArithmeticExpression),
77 Function(CalculationFunction),
79 JavaScript(String),
81 Constant(FieldValue),
83}
84
85#[derive(Debug, Clone)]
87pub struct ArithmeticExpression {
88 tokens: Vec<ExpressionToken>,
90}
91
92#[derive(Debug, Clone)]
94pub enum ExpressionToken {
95 Field(String),
97 Number(f64),
99 Operator(Operator),
101 LeftParen,
103 RightParen,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq)]
109pub enum Operator {
110 Add,
111 Subtract,
112 Multiply,
113 Divide,
114 Modulo,
115 Power,
116}
117
118impl Operator {
119 pub fn precedence(&self) -> i32 {
121 match self {
122 Operator::Power => 3,
123 Operator::Multiply | Operator::Divide | Operator::Modulo => 2,
124 Operator::Add | Operator::Subtract => 1,
125 }
126 }
127
128 pub fn apply(&self, left: f64, right: f64) -> f64 {
130 match self {
131 Operator::Add => left + right,
132 Operator::Subtract => left - right,
133 Operator::Multiply => left * right,
134 Operator::Divide => {
135 if right != 0.0 {
136 left / right
137 } else {
138 f64::INFINITY }
140 }
141 Operator::Modulo => {
142 if right != 0.0 {
143 left % right
144 } else {
145 0.0
146 }
147 }
148 Operator::Power => left.powf(right),
149 }
150 }
151}
152
153#[derive(Debug, Clone)]
155pub enum CalculationFunction {
156 Sum(Vec<String>),
158 Average(Vec<String>),
160 Min(Vec<String>),
162 Max(Vec<String>),
164 Product(Vec<String>),
166 Count(Vec<String>),
168 If {
170 condition_field: String,
171 true_value: Box<Calculation>,
172 false_value: Box<Calculation>,
173 },
174}
175
176#[allow(clippy::derivable_impls)]
177impl Default for CalculationEngine {
178 fn default() -> Self {
179 Self {
180 field_values: HashMap::new(),
181 calculations: HashMap::new(),
182 dependencies: HashMap::new(),
183 calculation_order: Vec::new(),
184 }
185 }
186}
187
188impl CalculationEngine {
189 pub fn new() -> Self {
191 Self::default()
192 }
193
194 pub fn set_field_value(&mut self, field_name: impl Into<String>, value: FieldValue) {
196 let field_name = field_name.into();
197 self.field_values.insert(field_name.clone(), value);
198
199 self.recalculate_dependents(&field_name);
201 }
202
203 pub fn get_field_value(&self, field_name: &str) -> Option<&FieldValue> {
205 self.field_values.get(field_name)
206 }
207
208 pub fn add_calculation(
210 &mut self,
211 field_name: impl Into<String>,
212 calculation: Calculation,
213 ) -> Result<(), PdfError> {
214 let field_name = field_name.into();
215
216 let deps = self.extract_dependencies(&calculation);
218
219 if self.would_create_cycle(&field_name, &deps) {
221 return Err(PdfError::InvalidStructure(format!(
222 "Circular dependency detected for field '{}'",
223 field_name
224 )));
225 }
226
227 for dep in &deps {
229 self.dependencies
230 .entry(dep.clone())
231 .or_default()
232 .insert(field_name.clone());
233 }
234
235 self.calculations.insert(field_name.clone(), calculation);
237
238 self.update_calculation_order()?;
240
241 self.calculate_field(&field_name)?;
243
244 Ok(())
245 }
246
247 #[allow(clippy::only_used_in_recursion)]
249 fn extract_dependencies(&self, calculation: &Calculation) -> HashSet<String> {
250 let mut deps = HashSet::new();
251
252 match calculation {
253 Calculation::Arithmetic(expr) => {
254 for token in &expr.tokens {
255 if let ExpressionToken::Field(field_name) = token {
256 deps.insert(field_name.clone());
257 }
258 }
259 }
260 Calculation::Function(func) => match func {
261 CalculationFunction::Sum(fields)
262 | CalculationFunction::Average(fields)
263 | CalculationFunction::Min(fields)
264 | CalculationFunction::Max(fields)
265 | CalculationFunction::Product(fields)
266 | CalculationFunction::Count(fields) => {
267 deps.extend(fields.iter().cloned());
268 }
269 CalculationFunction::If {
270 condition_field,
271 true_value,
272 false_value,
273 } => {
274 deps.insert(condition_field.clone());
275 deps.extend(self.extract_dependencies(true_value));
276 deps.extend(self.extract_dependencies(false_value));
277 }
278 },
279 Calculation::JavaScript(_) => {
280 }
283 Calculation::Constant(_) => {
284 }
286 }
287
288 deps
289 }
290
291 fn would_create_cycle(&self, field: &str, new_deps: &HashSet<String>) -> bool {
293 for dep in new_deps {
294 if dep == field {
295 return true; }
297
298 if self.depends_on(dep, field) {
300 return true;
301 }
302 }
303
304 false
305 }
306
307 fn depends_on(&self, field_a: &str, field_b: &str) -> bool {
309 let mut visited = HashSet::new();
310 let mut queue = VecDeque::new();
311 queue.push_back(field_a.to_string());
312
313 while let Some(current) = queue.pop_front() {
314 if current == field_b {
315 return true;
316 }
317
318 if visited.contains(¤t) {
319 continue;
320 }
321 visited.insert(current.clone());
322
323 if let Some(calc) = self.calculations.get(¤t) {
325 let deps = self.extract_dependencies(calc);
326 for dep in deps {
327 queue.push_back(dep);
328 }
329 }
330 }
331
332 false
333 }
334
335 fn update_calculation_order(&mut self) -> Result<(), PdfError> {
337 let mut order = Vec::new();
338 let mut visited = HashSet::new();
339 let mut visiting = HashSet::new();
340
341 for field in self.calculations.keys() {
342 if !visited.contains(field) {
343 self.topological_sort(field, &mut visited, &mut visiting, &mut order)?;
344 }
345 }
346
347 self.calculation_order = order;
348 Ok(())
349 }
350
351 fn topological_sort(
353 &self,
354 field: &str,
355 visited: &mut HashSet<String>,
356 visiting: &mut HashSet<String>,
357 order: &mut Vec<String>,
358 ) -> Result<(), PdfError> {
359 if visiting.contains(field) {
360 return Err(PdfError::InvalidStructure(
361 "Circular dependency detected".to_string(),
362 ));
363 }
364
365 if visited.contains(field) {
366 return Ok(());
367 }
368
369 visiting.insert(field.to_string());
370
371 if let Some(calc) = self.calculations.get(field) {
373 let deps = self.extract_dependencies(calc);
374 for dep in deps {
375 if self.calculations.contains_key(&dep) {
376 self.topological_sort(&dep, visited, visiting, order)?;
377 }
378 }
379 }
380
381 visiting.remove(field);
382 visited.insert(field.to_string());
383 order.push(field.to_string());
384
385 Ok(())
386 }
387
388 fn recalculate_dependents(&mut self, changed_field: &str) {
390 let _ = self.update_calculation_order();
392
393 let mut fields_to_recalc = HashSet::new();
395 if let Some(dependents) = self.dependencies.get(changed_field) {
396 fields_to_recalc.extend(dependents.clone());
397 }
398
399 let calc_order = self.calculation_order.clone();
401
402 for field in calc_order {
404 if fields_to_recalc.contains(&field) {
405 let _ = self.calculate_field(&field);
406 if let Some(deps) = self.dependencies.get(&field).cloned() {
408 fields_to_recalc.extend(deps);
409 }
410 }
411 }
412 }
413
414 pub fn calculate_field(&mut self, field_name: &str) -> Result<(), PdfError> {
416 if let Some(calculation) = self.calculations.get(field_name).cloned() {
417 let value = self.evaluate_calculation(&calculation)?;
418 self.field_values.insert(field_name.to_string(), value);
419 }
420 Ok(())
421 }
422
423 fn evaluate_calculation(&self, calculation: &Calculation) -> Result<FieldValue, PdfError> {
425 match calculation {
426 Calculation::Arithmetic(expr) => {
427 let result = self.evaluate_expression(expr)?;
428 Ok(FieldValue::Number(result))
429 }
430 Calculation::Function(func) => self.evaluate_function(func),
431 Calculation::JavaScript(code) => {
432 self.evaluate_javascript(code)
434 }
435 Calculation::Constant(value) => Ok(value.clone()),
436 }
437 }
438
439 fn evaluate_expression(&self, expr: &ArithmeticExpression) -> Result<f64, PdfError> {
441 let postfix = self.infix_to_postfix(&expr.tokens)?;
443
444 let mut stack = Vec::new();
446
447 for token in postfix {
448 match token {
449 ExpressionToken::Number(n) => stack.push(n),
450 ExpressionToken::Field(field_name) => {
451 let value = self
452 .field_values
453 .get(&field_name)
454 .map(|v| v.to_number())
455 .unwrap_or(0.0);
456 stack.push(value);
457 }
458 ExpressionToken::Operator(op) => {
459 if stack.len() < 2 {
460 return Err(PdfError::InvalidStructure("Invalid expression".to_string()));
461 }
462 let right = stack.pop().unwrap_or(0.0);
464 let left = stack.pop().unwrap_or(0.0);
465 stack.push(op.apply(left, right));
466 }
467 _ => {}
468 }
469 }
470
471 stack
472 .pop()
473 .ok_or_else(|| PdfError::InvalidStructure("Invalid expression".to_string()))
474 }
475
476 fn infix_to_postfix(
478 &self,
479 tokens: &[ExpressionToken],
480 ) -> Result<Vec<ExpressionToken>, PdfError> {
481 let mut output = Vec::new();
482 let mut operators = Vec::new();
483
484 for token in tokens {
485 match token {
486 ExpressionToken::Number(_) | ExpressionToken::Field(_) => {
487 output.push(token.clone());
488 }
489 ExpressionToken::Operator(op) => {
490 while let Some(ExpressionToken::Operator(top_op)) = operators.last() {
491 if top_op.precedence() >= op.precedence() {
492 if let Some(operator) = operators.pop() {
493 output.push(operator);
494 }
495 } else {
496 break;
497 }
498 }
499 operators.push(token.clone());
500 }
501 ExpressionToken::LeftParen => {
502 operators.push(token.clone());
503 }
504 ExpressionToken::RightParen => {
505 while let Some(op) = operators.pop() {
506 if matches!(op, ExpressionToken::LeftParen) {
507 break;
508 }
509 output.push(op);
510 }
511 }
512 }
513 }
514
515 while let Some(op) = operators.pop() {
516 output.push(op);
517 }
518
519 Ok(output)
520 }
521
522 fn evaluate_function(&self, func: &CalculationFunction) -> Result<FieldValue, PdfError> {
524 match func {
525 CalculationFunction::Sum(fields) => {
526 let sum = fields
527 .iter()
528 .filter_map(|f| self.field_values.get(f))
529 .map(|v| v.to_number())
530 .sum();
531 Ok(FieldValue::Number(sum))
532 }
533 CalculationFunction::Average(fields) => {
534 let values: Vec<f64> = fields
535 .iter()
536 .filter_map(|f| self.field_values.get(f))
537 .map(|v| v.to_number())
538 .collect();
539
540 if values.is_empty() {
541 Ok(FieldValue::Number(0.0))
542 } else {
543 let avg = values.iter().sum::<f64>() / values.len() as f64;
544 Ok(FieldValue::Number(avg))
545 }
546 }
547 CalculationFunction::Min(fields) => {
548 let min = fields
549 .iter()
550 .filter_map(|f| self.field_values.get(f))
551 .map(|v| v.to_number())
552 .filter(|n| !n.is_nan()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
554 .unwrap_or(0.0);
555 Ok(FieldValue::Number(min))
556 }
557 CalculationFunction::Max(fields) => {
558 let max = fields
559 .iter()
560 .filter_map(|f| self.field_values.get(f))
561 .map(|v| v.to_number())
562 .filter(|n| !n.is_nan()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
564 .unwrap_or(0.0);
565 Ok(FieldValue::Number(max))
566 }
567 CalculationFunction::Product(fields) => {
568 let product = fields
569 .iter()
570 .filter_map(|f| self.field_values.get(f))
571 .map(|v| v.to_number())
572 .product();
573 Ok(FieldValue::Number(product))
574 }
575 CalculationFunction::Count(fields) => {
576 let count = fields
577 .iter()
578 .filter_map(|f| self.field_values.get(f))
579 .filter(|v| !matches!(v, FieldValue::Empty))
580 .count() as f64;
581 Ok(FieldValue::Number(count))
582 }
583 CalculationFunction::If {
584 condition_field,
585 true_value,
586 false_value,
587 } => {
588 let condition = self
589 .field_values
590 .get(condition_field)
591 .map(|v| match v {
592 FieldValue::Boolean(b) => *b,
593 FieldValue::Number(n) => *n != 0.0,
594 FieldValue::Text(s) => !s.is_empty(),
595 FieldValue::Empty => false,
596 })
597 .unwrap_or(false);
598
599 if condition {
600 self.evaluate_calculation(true_value)
601 } else {
602 self.evaluate_calculation(false_value)
603 }
604 }
605 }
606 }
607
608 fn evaluate_javascript(&self, _code: &str) -> Result<FieldValue, PdfError> {
610 Ok(FieldValue::Empty)
616 }
617
618 pub fn recalculate_all(&mut self) -> Result<(), PdfError> {
620 for field in self.calculation_order.clone() {
621 self.calculate_field(&field)?;
622 }
623 Ok(())
624 }
625
626 pub fn remove_calculation(&mut self, field_name: &str) {
628 if self.calculations.remove(field_name).is_some() {
630 self.calculation_order.retain(|f| f != field_name);
632
633 self.dependencies.values_mut().for_each(|deps| {
635 deps.remove(field_name);
636 });
637
638 self.dependencies.remove(field_name);
640
641 self.field_values.remove(field_name);
643 }
644 }
645
646 pub fn get_summary(&self) -> CalculationSummary {
648 CalculationSummary {
649 total_fields: self.field_values.len(),
650 calculated_fields: self.calculations.len(),
651 dependencies: self.dependencies.len(),
652 calculation_order: self.calculation_order.clone(),
653 }
654 }
655}
656
657#[derive(Debug, Clone)]
659pub struct CalculationSummary {
660 pub total_fields: usize,
662 pub calculated_fields: usize,
664 pub dependencies: usize,
666 pub calculation_order: Vec<String>,
668}
669
670impl fmt::Display for CalculationSummary {
671 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
672 write!(
673 f,
674 "Calculation Summary:\n\
675 - Total fields: {}\n\
676 - Calculated fields: {}\n\
677 - Dependencies: {}\n\
678 - Calculation order: {}",
679 self.total_fields,
680 self.calculated_fields,
681 self.dependencies,
682 self.calculation_order.join(" -> ")
683 )
684 }
685}
686
687impl ArithmeticExpression {
688 pub fn from_string(expr: &str) -> Result<Self, PdfError> {
690 let tokens = Self::tokenize(expr)?;
691 Ok(Self { tokens })
692 }
693
694 fn tokenize(expr: &str) -> Result<Vec<ExpressionToken>, PdfError> {
696 let mut tokens = Vec::new();
697 let mut chars = expr.chars().peekable();
698
699 if expr.trim().is_empty() {
701 return Err(PdfError::InvalidFormat("Empty expression".to_string()));
702 }
703
704 while let Some(ch) = chars.next() {
705 match ch {
706 ' ' | '\t' | '\n' => continue,
707 '+' => tokens.push(ExpressionToken::Operator(Operator::Add)),
708 '-' => tokens.push(ExpressionToken::Operator(Operator::Subtract)),
709 '*' => tokens.push(ExpressionToken::Operator(Operator::Multiply)),
710 '/' => tokens.push(ExpressionToken::Operator(Operator::Divide)),
711 '%' => tokens.push(ExpressionToken::Operator(Operator::Modulo)),
712 '^' => tokens.push(ExpressionToken::Operator(Operator::Power)),
713 '(' => tokens.push(ExpressionToken::LeftParen),
714 ')' => tokens.push(ExpressionToken::RightParen),
715 '0'..='9' | '.' => {
716 let mut num_str = String::new();
717 num_str.push(ch);
718 while let Some(&next_ch) = chars.peek() {
719 if next_ch.is_ascii_digit() || next_ch == '.' {
720 if let Some(consumed_ch) = chars.next() {
721 num_str.push(consumed_ch);
722 } else {
723 break; }
725 } else {
726 break;
727 }
728 }
729 let num = num_str
730 .parse::<f64>()
731 .map_err(|_| PdfError::InvalidFormat("Invalid number".to_string()))?;
732 tokens.push(ExpressionToken::Number(num));
733 }
734 'a'..='z' | 'A'..='Z' | '_' => {
735 let mut field_name = String::new();
736 field_name.push(ch);
737 while let Some(&next_ch) = chars.peek() {
738 if next_ch.is_alphanumeric() || next_ch == '_' {
739 if let Some(consumed_ch) = chars.next() {
740 field_name.push(consumed_ch);
741 } else {
742 break; }
744 } else {
745 break;
746 }
747 }
748 tokens.push(ExpressionToken::Field(field_name));
749 }
750 _ => {
751 return Err(PdfError::InvalidFormat(format!(
752 "Invalid character in expression: '{}'",
753 ch
754 )));
755 }
756 }
757 }
758
759 Self::validate_tokens(&tokens)?;
761
762 Ok(tokens)
763 }
764
765 fn validate_tokens(tokens: &[ExpressionToken]) -> Result<(), PdfError> {
767 if tokens.is_empty() {
768 return Err(PdfError::InvalidFormat("Empty expression".to_string()));
769 }
770
771 let mut paren_count = 0;
772 let mut last_was_operator = true; for token in tokens.iter() {
775 match token {
776 ExpressionToken::LeftParen => {
777 paren_count += 1;
778 last_was_operator = true; }
780 ExpressionToken::RightParen => {
781 paren_count -= 1;
782 if paren_count < 0 {
783 return Err(PdfError::InvalidFormat(
784 "Unbalanced parentheses".to_string(),
785 ));
786 }
787 last_was_operator = false;
788 }
789 ExpressionToken::Operator(_) => {
790 if last_was_operator {
791 return Err(PdfError::InvalidFormat(
792 "Invalid operator sequence".to_string(),
793 ));
794 }
795 last_was_operator = true;
796 }
797 ExpressionToken::Number(_) | ExpressionToken::Field(_) => {
798 last_was_operator = false;
799 }
800 }
801 }
802
803 if paren_count != 0 {
804 return Err(PdfError::InvalidFormat(
805 "Unbalanced parentheses".to_string(),
806 ));
807 }
808
809 if last_was_operator {
810 return Err(PdfError::InvalidFormat(
811 "Expression ends with operator".to_string(),
812 ));
813 }
814
815 Ok(())
816 }
817}
818
819#[cfg(test)]
820mod tests {
821 use super::*;
822
823 #[test]
824 fn test_field_value_conversion() {
825 assert_eq!(FieldValue::Number(42.5).to_number(), 42.5);
826 assert_eq!(FieldValue::Text("123".to_string()).to_number(), 123.0);
827 assert_eq!(FieldValue::Boolean(true).to_number(), 1.0);
828 assert_eq!(FieldValue::Empty.to_number(), 0.0);
829 }
830
831 #[test]
832 fn test_arithmetic_expression() {
833 let expr = ArithmeticExpression::from_string("2 + 3 * 4").unwrap();
834 assert_eq!(expr.tokens.len(), 5);
835 }
836
837 #[test]
838 fn test_calculation_engine() {
839 let mut engine = CalculationEngine::new();
840
841 engine.set_field_value("quantity", FieldValue::Number(5.0));
843 engine.set_field_value("price", FieldValue::Number(10.0));
844
845 let expr = ArithmeticExpression::from_string("quantity * price").unwrap();
847 engine
848 .add_calculation("total", Calculation::Arithmetic(expr))
849 .unwrap();
850
851 let total = engine.get_field_value("total").unwrap();
853 assert_eq!(total.to_number(), 50.0);
854 }
855
856 #[test]
857 fn test_sum_function() {
858 let mut engine = CalculationEngine::new();
859
860 engine.set_field_value("field1", FieldValue::Number(10.0));
861 engine.set_field_value("field2", FieldValue::Number(20.0));
862 engine.set_field_value("field3", FieldValue::Number(30.0));
863
864 let calc = Calculation::Function(CalculationFunction::Sum(vec![
865 "field1".to_string(),
866 "field2".to_string(),
867 "field3".to_string(),
868 ]));
869
870 engine.add_calculation("total", calc).unwrap();
871
872 let total = engine.get_field_value("total").unwrap();
873 assert_eq!(total.to_number(), 60.0);
874 }
875
876 #[test]
877 fn test_circular_dependency_detection() {
878 let mut engine = CalculationEngine::new();
879
880 let expr1 = ArithmeticExpression::from_string("fieldB + 1").unwrap();
882 engine
883 .add_calculation("fieldA", Calculation::Arithmetic(expr1))
884 .unwrap();
885
886 let expr2 = ArithmeticExpression::from_string("fieldA + 1").unwrap();
888 let result = engine.add_calculation("fieldB", Calculation::Arithmetic(expr2));
889
890 assert!(result.is_err());
891 }
892
893 #[test]
896 fn test_field_value_conversions() {
897 let num_val = FieldValue::Number(42.5);
899 assert_eq!(num_val.to_number(), 42.5);
900 assert_eq!(num_val.to_string(), "42.50");
901
902 let int_val = FieldValue::Number(100.0);
903 assert_eq!(int_val.to_string(), "100");
904
905 let text_val = FieldValue::Text("123.45".to_string());
907 assert_eq!(text_val.to_number(), 123.45);
908 assert_eq!(text_val.to_string(), "123.45");
909
910 let non_numeric_text = FieldValue::Text("hello".to_string());
911 assert_eq!(non_numeric_text.to_number(), 0.0);
912
913 let true_val = FieldValue::Boolean(true);
915 assert_eq!(true_val.to_number(), 1.0);
916 assert_eq!(true_val.to_string(), "true");
917
918 let false_val = FieldValue::Boolean(false);
919 assert_eq!(false_val.to_number(), 0.0);
920 assert_eq!(false_val.to_string(), "false");
921
922 let empty_val = FieldValue::Empty;
924 assert_eq!(empty_val.to_number(), 0.0);
925 assert_eq!(empty_val.to_string(), "");
926 }
927
928 #[test]
929 fn test_complex_arithmetic_expressions() {
930 let mut engine = CalculationEngine::new();
931
932 engine.set_field_value("a", FieldValue::Number(10.0));
934 engine.set_field_value("b", FieldValue::Number(5.0));
935 engine.set_field_value("c", FieldValue::Number(2.0));
936
937 let expr = ArithmeticExpression::from_string("(a + b) * c").unwrap();
939 engine
940 .add_calculation("result1", Calculation::Arithmetic(expr))
941 .unwrap();
942
943 let result = engine.get_field_value("result1").unwrap();
944 assert_eq!(result.to_number(), 30.0); let expr2 = ArithmeticExpression::from_string("a + b - c * 2 / 4").unwrap();
948 engine
949 .add_calculation("result2", Calculation::Arithmetic(expr2))
950 .unwrap();
951
952 let result2 = engine.get_field_value("result2").unwrap();
953 assert_eq!(result2.to_number(), 14.0); }
955
956 #[test]
957 fn test_calculation_functions() {
958 let mut engine = CalculationEngine::new();
959
960 engine.set_field_value("val1", FieldValue::Number(100.0));
962 engine.set_field_value("val2", FieldValue::Number(50.0));
963 engine.set_field_value("val3", FieldValue::Number(25.0));
964 engine.set_field_value("val4", FieldValue::Number(75.0));
965
966 let avg_calc = Calculation::Function(CalculationFunction::Average(vec![
968 "val1".to_string(),
969 "val2".to_string(),
970 "val3".to_string(),
971 "val4".to_string(),
972 ]));
973 engine.add_calculation("average", avg_calc).unwrap();
974
975 let avg = engine.get_field_value("average").unwrap();
976 assert_eq!(avg.to_number(), 62.5); let min_calc = Calculation::Function(CalculationFunction::Min(vec![
980 "val1".to_string(),
981 "val2".to_string(),
982 "val3".to_string(),
983 "val4".to_string(),
984 ]));
985 engine.add_calculation("minimum", min_calc).unwrap();
986
987 let min = engine.get_field_value("minimum").unwrap();
988 assert_eq!(min.to_number(), 25.0);
989
990 let max_calc = Calculation::Function(CalculationFunction::Max(vec![
992 "val1".to_string(),
993 "val2".to_string(),
994 "val3".to_string(),
995 "val4".to_string(),
996 ]));
997 engine.add_calculation("maximum", max_calc).unwrap();
998
999 let max = engine.get_field_value("maximum").unwrap();
1000 assert_eq!(max.to_number(), 100.0);
1001 }
1002
1003 #[test]
1004 fn test_calculation_order_dependencies() {
1005 let mut engine = CalculationEngine::new();
1006
1007 engine.set_field_value("base", FieldValue::Number(10.0));
1009
1010 let expr1 = ArithmeticExpression::from_string("base * 2").unwrap();
1012 engine
1013 .add_calculation("level1", Calculation::Arithmetic(expr1))
1014 .unwrap();
1015
1016 let expr2 = ArithmeticExpression::from_string("level1 + 5").unwrap();
1018 engine
1019 .add_calculation("level2", Calculation::Arithmetic(expr2))
1020 .unwrap();
1021
1022 let expr3 = ArithmeticExpression::from_string("level2 / 5").unwrap();
1024 engine
1025 .add_calculation("level3", Calculation::Arithmetic(expr3))
1026 .unwrap();
1027
1028 assert_eq!(engine.calculation_order.len(), 3);
1030 assert_eq!(engine.calculation_order[0], "level1");
1031 assert_eq!(engine.calculation_order[1], "level2");
1032 assert_eq!(engine.calculation_order[2], "level3");
1033
1034 assert_eq!(engine.get_field_value("level1").unwrap().to_number(), 20.0);
1036 assert_eq!(engine.get_field_value("level2").unwrap().to_number(), 25.0);
1037 assert_eq!(engine.get_field_value("level3").unwrap().to_number(), 5.0);
1038 }
1039
1040 #[test]
1041 fn test_field_update_recalculation() {
1042 let mut engine = CalculationEngine::new();
1043
1044 engine.set_field_value("price", FieldValue::Number(10.0));
1046 engine.set_field_value("quantity", FieldValue::Number(5.0));
1047
1048 let expr = ArithmeticExpression::from_string("price * quantity").unwrap();
1050 engine
1051 .add_calculation("total", Calculation::Arithmetic(expr))
1052 .unwrap();
1053
1054 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 50.0);
1056
1057 engine.set_field_value("price", FieldValue::Number(15.0));
1059 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 75.0);
1060
1061 engine.set_field_value("quantity", FieldValue::Number(10.0));
1063 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 150.0);
1064 }
1065
1066 #[test]
1067 fn test_edge_cases_division_by_zero() {
1068 let mut engine = CalculationEngine::new();
1069
1070 engine.set_field_value("numerator", FieldValue::Number(100.0));
1071 engine.set_field_value("denominator", FieldValue::Number(0.0));
1072
1073 let expr = ArithmeticExpression::from_string("numerator / denominator").unwrap();
1074 engine
1075 .add_calculation("result", Calculation::Arithmetic(expr))
1076 .unwrap();
1077
1078 let result = engine.get_field_value("result").unwrap();
1079 assert!(result.to_number().is_infinite());
1081 }
1082
1083 #[test]
1084 fn test_mixed_value_types() {
1085 let mut engine = CalculationEngine::new();
1086
1087 engine.set_field_value("num", FieldValue::Number(10.0));
1089 engine.set_field_value("text_num", FieldValue::Text("20".to_string()));
1090 engine.set_field_value("bool_val", FieldValue::Boolean(true));
1091 engine.set_field_value("empty", FieldValue::Empty);
1092
1093 let calc = Calculation::Function(CalculationFunction::Sum(vec![
1095 "num".to_string(),
1096 "text_num".to_string(),
1097 "bool_val".to_string(),
1098 "empty".to_string(),
1099 ]));
1100 engine.add_calculation("total", calc).unwrap();
1101
1102 let total = engine.get_field_value("total").unwrap();
1103 assert_eq!(total.to_number(), 31.0); }
1105
1106 #[test]
1107 fn test_constant_calculations() {
1108 let mut engine = CalculationEngine::new();
1109
1110 engine
1112 .add_calculation("pi", Calculation::Constant(FieldValue::Number(3.14159)))
1113 .unwrap();
1114 engine
1115 .add_calculation(
1116 "label",
1117 Calculation::Constant(FieldValue::Text("Total:".to_string())),
1118 )
1119 .unwrap();
1120 engine
1121 .add_calculation("enabled", Calculation::Constant(FieldValue::Boolean(true)))
1122 .unwrap();
1123
1124 assert_eq!(engine.get_field_value("pi").unwrap().to_number(), 3.14159);
1125 assert_eq!(
1126 engine.get_field_value("label").unwrap().to_string(),
1127 "Total:"
1128 );
1129 assert_eq!(
1130 *engine.get_field_value("enabled").unwrap(),
1131 FieldValue::Boolean(true)
1132 );
1133 }
1134
1135 #[test]
1136 fn test_expression_parsing_errors() {
1137 assert!(ArithmeticExpression::from_string("").is_err());
1139 assert!(ArithmeticExpression::from_string("(a + b").is_err()); assert!(ArithmeticExpression::from_string("a + + b").is_err()); assert!(ArithmeticExpression::from_string("* a + b").is_err()); assert!(ArithmeticExpression::from_string("a b +").is_err()); }
1144
1145 #[test]
1146 fn test_multiple_dependencies() {
1147 let mut engine = CalculationEngine::new();
1148
1149 engine.set_field_value("a", FieldValue::Number(5.0));
1151 engine.set_field_value("b", FieldValue::Number(10.0));
1152
1153 let expr1 = ArithmeticExpression::from_string("a + b").unwrap();
1155 engine
1156 .add_calculation("c", Calculation::Arithmetic(expr1))
1157 .unwrap();
1158
1159 let expr2 = ArithmeticExpression::from_string("a * 2").unwrap();
1161 engine
1162 .add_calculation("d", Calculation::Arithmetic(expr2))
1163 .unwrap();
1164
1165 let expr3 = ArithmeticExpression::from_string("c + d").unwrap();
1167 engine
1168 .add_calculation("e", Calculation::Arithmetic(expr3))
1169 .unwrap();
1170
1171 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 15.0);
1172 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 10.0);
1173 assert_eq!(engine.get_field_value("e").unwrap().to_number(), 25.0);
1174
1175 engine.set_field_value("a", FieldValue::Number(10.0));
1177 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 20.0);
1178 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 20.0);
1179 assert_eq!(engine.get_field_value("e").unwrap().to_number(), 40.0);
1180 }
1181
1182 #[test]
1183 fn test_calculation_removal() {
1184 let mut engine = CalculationEngine::new();
1185
1186 engine.set_field_value("x", FieldValue::Number(10.0));
1187
1188 let expr = ArithmeticExpression::from_string("x * 2").unwrap();
1189 engine
1190 .add_calculation("y", Calculation::Arithmetic(expr))
1191 .unwrap();
1192
1193 assert_eq!(engine.get_field_value("y").unwrap().to_number(), 20.0);
1194
1195 engine.remove_calculation("y");
1197
1198 assert!(engine.get_field_value("y").is_none());
1200
1201 engine.set_field_value("y", FieldValue::Number(100.0));
1203 assert_eq!(engine.get_field_value("y").unwrap().to_number(), 100.0);
1204 }
1205
1206 #[test]
1207 fn test_large_calculation_chain() {
1208 let mut engine = CalculationEngine::new();
1209
1210 engine.set_field_value("f0", FieldValue::Number(1.0));
1212
1213 for i in 1..20 {
1214 let prev = format!("f{}", i - 1);
1215 let curr = format!("f{}", i);
1216 let expr = ArithmeticExpression::from_string(&format!("{} + 1", prev)).unwrap();
1217 engine
1218 .add_calculation(&curr, Calculation::Arithmetic(expr))
1219 .unwrap();
1220 }
1221
1222 assert_eq!(engine.get_field_value("f19").unwrap().to_number(), 20.0);
1224
1225 engine.set_field_value("f0", FieldValue::Number(10.0));
1227 assert_eq!(engine.get_field_value("f19").unwrap().to_number(), 29.0);
1228 }
1229
1230 #[test]
1231 fn test_operator_precedence() {
1232 let mut engine = CalculationEngine::new();
1233
1234 engine.set_field_value("a", FieldValue::Number(2.0));
1235 engine.set_field_value("b", FieldValue::Number(3.0));
1236 engine.set_field_value("c", FieldValue::Number(4.0));
1237
1238 let expr = ArithmeticExpression::from_string("a + b * c").unwrap();
1240 engine
1241 .add_calculation("result", Calculation::Arithmetic(expr))
1242 .unwrap();
1243
1244 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 14.0); let expr2 = ArithmeticExpression::from_string("(a + b) * c").unwrap();
1248 engine
1249 .add_calculation("result2", Calculation::Arithmetic(expr2))
1250 .unwrap();
1251
1252 assert_eq!(engine.get_field_value("result2").unwrap().to_number(), 20.0);
1253 }
1255
1256 #[test]
1257 fn test_negative_numbers() {
1258 let mut engine = CalculationEngine::new();
1259
1260 engine.set_field_value("positive", FieldValue::Number(10.0));
1261 engine.set_field_value("negative", FieldValue::Number(-5.0));
1262
1263 let expr = ArithmeticExpression::from_string("positive + negative").unwrap();
1265 engine
1266 .add_calculation("result", Calculation::Arithmetic(expr))
1267 .unwrap();
1268
1269 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 5.0);
1270
1271 let expr2 = ArithmeticExpression::from_string("negative * negative").unwrap();
1273 engine
1274 .add_calculation("result2", Calculation::Arithmetic(expr2))
1275 .unwrap();
1276
1277 assert_eq!(engine.get_field_value("result2").unwrap().to_number(), 25.0);
1278 }
1279
1280 #[test]
1281 fn test_floating_point_precision() {
1282 let mut engine = CalculationEngine::new();
1283
1284 engine.set_field_value("a", FieldValue::Number(0.1));
1285 engine.set_field_value("b", FieldValue::Number(0.2));
1286
1287 let expr = ArithmeticExpression::from_string("a + b").unwrap();
1288 engine
1289 .add_calculation("result", Calculation::Arithmetic(expr))
1290 .unwrap();
1291
1292 let result = engine.get_field_value("result").unwrap().to_number();
1293 assert!((result - 0.3).abs() < 0.0001);
1295 }
1296
1297 #[test]
1298 fn test_empty_field_references() {
1299 let mut engine = CalculationEngine::new();
1300
1301 let expr = ArithmeticExpression::from_string("missing1 + missing2").unwrap();
1303 engine
1304 .add_calculation("result", Calculation::Arithmetic(expr))
1305 .unwrap();
1306
1307 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 0.0);
1309
1310 engine.set_field_value("missing1", FieldValue::Number(10.0));
1312 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 10.0);
1313 }
1314
1315 #[test]
1316 fn test_calculation_with_product_function() {
1317 let mut engine = CalculationEngine::new();
1318
1319 engine.set_field_value("f1", FieldValue::Number(2.0));
1320 engine.set_field_value("f2", FieldValue::Number(3.0));
1321 engine.set_field_value("f3", FieldValue::Number(4.0));
1322 engine.set_field_value("f4", FieldValue::Number(5.0));
1323
1324 let calc = Calculation::Function(CalculationFunction::Product(vec![
1325 "f1".to_string(),
1326 "f2".to_string(),
1327 "f3".to_string(),
1328 "f4".to_string(),
1329 ]));
1330 engine.add_calculation("product", calc).unwrap();
1331
1332 let product = engine.get_field_value("product").unwrap();
1333 assert_eq!(product.to_number(), 120.0); }
1335
1336 #[test]
1337 fn test_complex_dependency_graph() {
1338 let mut engine = CalculationEngine::new();
1339
1340 engine.set_field_value("a", FieldValue::Number(10.0));
1348
1349 let expr_b = ArithmeticExpression::from_string("a * 2").unwrap();
1350 engine
1351 .add_calculation("b", Calculation::Arithmetic(expr_b))
1352 .unwrap();
1353
1354 let expr_c = ArithmeticExpression::from_string("a + 5").unwrap();
1355 engine
1356 .add_calculation("c", Calculation::Arithmetic(expr_c))
1357 .unwrap();
1358
1359 let expr_d = ArithmeticExpression::from_string("b + c").unwrap();
1360 engine
1361 .add_calculation("d", Calculation::Arithmetic(expr_d))
1362 .unwrap();
1363
1364 assert_eq!(engine.get_field_value("b").unwrap().to_number(), 20.0);
1365 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 15.0);
1366 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 35.0);
1367
1368 engine.set_field_value("a", FieldValue::Number(20.0));
1370 assert_eq!(engine.get_field_value("b").unwrap().to_number(), 40.0);
1371 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 25.0);
1372 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 65.0);
1373 }
1374
1375 #[test]
1376 fn test_field_value_conversions_extended() {
1377 assert_eq!(FieldValue::Number(42.5).to_number(), 42.5);
1379 assert_eq!(FieldValue::Text("123.45".to_string()).to_number(), 123.45);
1380 assert_eq!(FieldValue::Text("invalid".to_string()).to_number(), 0.0);
1381 assert_eq!(FieldValue::Boolean(true).to_number(), 1.0);
1382 assert_eq!(FieldValue::Boolean(false).to_number(), 0.0);
1383 assert_eq!(FieldValue::Empty.to_number(), 0.0);
1384
1385 assert_eq!(FieldValue::Number(42.0).to_string(), "42");
1387 assert_eq!(FieldValue::Number(42.5).to_string(), "42.50");
1388 assert_eq!(FieldValue::Text("hello".to_string()).to_string(), "hello");
1389 assert_eq!(FieldValue::Boolean(true).to_string(), "true");
1390 assert_eq!(FieldValue::Boolean(false).to_string(), "false");
1391 assert_eq!(FieldValue::Empty.to_string(), "");
1392 }
1393
1394 #[test]
1395 fn test_min_max_functions() {
1396 let mut engine = CalculationEngine::new();
1397
1398 engine.set_field_value("a", FieldValue::Number(10.0));
1399 engine.set_field_value("b", FieldValue::Number(5.0));
1400 engine.set_field_value("c", FieldValue::Number(15.0));
1401 engine.set_field_value("d", FieldValue::Number(8.0));
1402
1403 let min_calc = Calculation::Function(CalculationFunction::Min(vec![
1405 "a".to_string(),
1406 "b".to_string(),
1407 "c".to_string(),
1408 "d".to_string(),
1409 ]));
1410 engine.add_calculation("min_val", min_calc).unwrap();
1411 assert_eq!(engine.get_field_value("min_val").unwrap().to_number(), 5.0);
1412
1413 let max_calc = Calculation::Function(CalculationFunction::Max(vec![
1415 "a".to_string(),
1416 "b".to_string(),
1417 "c".to_string(),
1418 "d".to_string(),
1419 ]));
1420 engine.add_calculation("max_val", max_calc).unwrap();
1421 assert_eq!(engine.get_field_value("max_val").unwrap().to_number(), 15.0);
1422 }
1423
1424 #[test]
1425 fn test_count_function() {
1426 let mut engine = CalculationEngine::new();
1427
1428 engine.set_field_value("f1", FieldValue::Number(10.0));
1429 engine.set_field_value("f2", FieldValue::Empty);
1430 engine.set_field_value("f3", FieldValue::Text("text".to_string()));
1431 engine.set_field_value("f4", FieldValue::Number(0.0));
1432
1433 let count_calc = Calculation::Function(CalculationFunction::Count(vec![
1434 "f1".to_string(),
1435 "f2".to_string(),
1436 "f3".to_string(),
1437 "f4".to_string(),
1438 ]));
1439 engine.add_calculation("count", count_calc).unwrap();
1440
1441 assert_eq!(engine.get_field_value("count").unwrap().to_number(), 3.0);
1443 }
1444
1445 #[test]
1446 fn test_if_function() {
1447 let mut engine = CalculationEngine::new();
1448
1449 engine.set_field_value("condition", FieldValue::Boolean(true));
1451
1452 let if_calc = Calculation::Function(CalculationFunction::If {
1453 condition_field: "condition".to_string(),
1454 true_value: Box::new(Calculation::Constant(FieldValue::Number(100.0))),
1455 false_value: Box::new(Calculation::Constant(FieldValue::Number(200.0))),
1456 });
1457 engine.add_calculation("result", if_calc).unwrap();
1458 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 100.0);
1459
1460 engine.set_field_value("condition", FieldValue::Boolean(false));
1462 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 200.0);
1463
1464 engine.set_field_value("condition", FieldValue::Number(5.0));
1466 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 100.0);
1467
1468 engine.set_field_value("condition", FieldValue::Number(0.0));
1469 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 200.0);
1470 }
1471
1472 #[test]
1473 fn test_modulo_and_power_operations() {
1474 let mut engine = CalculationEngine::new();
1475
1476 engine.set_field_value("a", FieldValue::Number(10.0));
1477 engine.set_field_value("b", FieldValue::Number(3.0));
1478
1479 let mod_expr = ArithmeticExpression::from_string("a % b").unwrap();
1481 engine
1482 .add_calculation("mod_result", Calculation::Arithmetic(mod_expr))
1483 .unwrap();
1484 assert_eq!(
1485 engine.get_field_value("mod_result").unwrap().to_number(),
1486 1.0
1487 );
1488
1489 let pow_expr = ArithmeticExpression::from_string("b ^ 3").unwrap();
1491 engine
1492 .add_calculation("pow_result", Calculation::Arithmetic(pow_expr))
1493 .unwrap();
1494 assert_eq!(
1495 engine.get_field_value("pow_result").unwrap().to_number(),
1496 27.0
1497 );
1498 }
1499
1500 #[test]
1501 fn test_calculation_summary() {
1502 let mut engine = CalculationEngine::new();
1503
1504 engine.set_field_value("a", FieldValue::Number(10.0));
1506 engine.set_field_value("b", FieldValue::Number(20.0));
1507
1508 let expr = ArithmeticExpression::from_string("a + b").unwrap();
1509 engine
1510 .add_calculation("sum", Calculation::Arithmetic(expr))
1511 .unwrap();
1512
1513 let summary = engine.get_summary();
1514 assert_eq!(summary.total_fields, 3); assert_eq!(summary.calculated_fields, 1); assert_eq!(summary.calculation_order.len(), 1);
1517 assert_eq!(summary.calculation_order[0], "sum");
1518
1519 let summary_str = format!("{}", summary);
1521 assert!(summary_str.contains("Total fields: 3"));
1522 assert!(summary_str.contains("Calculated fields: 1"));
1523 }
1524
1525 #[test]
1526 fn test_recalculate_all() {
1527 let mut engine = CalculationEngine::new();
1528
1529 engine.set_field_value("x", FieldValue::Number(5.0));
1530 engine.set_field_value("y", FieldValue::Number(10.0));
1531
1532 let expr1 = ArithmeticExpression::from_string("x + y").unwrap();
1533 engine
1534 .add_calculation("sum", Calculation::Arithmetic(expr1))
1535 .unwrap();
1536
1537 let expr2 = ArithmeticExpression::from_string("sum * 2").unwrap();
1538 engine
1539 .add_calculation("double", Calculation::Arithmetic(expr2))
1540 .unwrap();
1541
1542 assert_eq!(engine.get_field_value("sum").unwrap().to_number(), 15.0);
1544 assert_eq!(engine.get_field_value("double").unwrap().to_number(), 30.0);
1545
1546 engine.recalculate_all().unwrap();
1548
1549 assert_eq!(engine.get_field_value("sum").unwrap().to_number(), 15.0);
1551 assert_eq!(engine.get_field_value("double").unwrap().to_number(), 30.0);
1552 }
1553
1554 #[test]
1555 fn test_javascript_calculation() {
1556 let mut engine = CalculationEngine::new();
1557
1558 let js_calc = Calculation::JavaScript("var sum = a + b;".to_string());
1560 engine.add_calculation("js_result", js_calc).unwrap();
1561
1562 assert_eq!(
1563 *engine.get_field_value("js_result").unwrap(),
1564 FieldValue::Empty
1565 );
1566 }
1567
1568 #[test]
1569 fn test_division_by_zero() {
1570 let mut engine = CalculationEngine::new();
1572
1573 engine.set_field_value("numerator", FieldValue::Number(100.0));
1574 engine.set_field_value("denominator", FieldValue::Number(0.0));
1575
1576 let expr = ArithmeticExpression {
1578 tokens: vec![
1579 ExpressionToken::Field("numerator".to_string()),
1580 ExpressionToken::Operator(Operator::Divide),
1581 ExpressionToken::Field("denominator".to_string()),
1582 ],
1583 };
1584
1585 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1586
1587 let result = engine.calculate_field("result");
1589 match result {
1591 Ok(_) => {
1592 let value = engine.get_field_value("result");
1594 assert!(
1595 matches!(value, Some(FieldValue::Number(n)) if n.is_infinite() || n.is_nan()),
1596 "Division by zero should produce infinity or NaN, got: {:?}",
1597 value
1598 );
1599 }
1600 Err(_) => {
1601 }
1604 }
1605 }
1606
1607 #[test]
1608 fn test_circular_reference_detection() {
1609 let mut engine = CalculationEngine::new();
1611
1612 let _ = engine.add_calculation(
1614 "field_a",
1615 Calculation::Arithmetic(ArithmeticExpression {
1616 tokens: vec![
1617 ExpressionToken::Field("field_b".to_string()),
1618 ExpressionToken::Number(1.0),
1619 ExpressionToken::Operator(Operator::Add),
1620 ],
1621 }),
1622 );
1623
1624 let _ = engine.add_calculation(
1625 "field_b",
1626 Calculation::Arithmetic(ArithmeticExpression {
1627 tokens: vec![
1628 ExpressionToken::Field("field_c".to_string()),
1629 ExpressionToken::Number(2.0),
1630 ExpressionToken::Operator(Operator::Add),
1631 ],
1632 }),
1633 );
1634
1635 let _ = engine.add_calculation(
1636 "field_c",
1637 Calculation::Arithmetic(ArithmeticExpression {
1638 tokens: vec![
1639 ExpressionToken::Field("field_a".to_string()),
1640 ExpressionToken::Number(3.0),
1641 ExpressionToken::Operator(Operator::Add),
1642 ],
1643 }),
1644 );
1645
1646 let result = engine.update_calculation_order();
1648 assert!(result.is_ok() || result.is_err());
1650 }
1651
1652 #[test]
1653 fn test_non_numeric_calculation() {
1654 let mut engine = CalculationEngine::new();
1656
1657 engine.set_field_value("text_field", FieldValue::Text("not a number".to_string()));
1658 engine.set_field_value("numeric_field", FieldValue::Number(42.0));
1659
1660 let expr = ArithmeticExpression {
1662 tokens: vec![
1663 ExpressionToken::Field("text_field".to_string()),
1664 ExpressionToken::Field("numeric_field".to_string()),
1665 ExpressionToken::Operator(Operator::Add),
1666 ],
1667 };
1668
1669 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1670
1671 let _ = engine.calculate_field("result");
1673 if let Some(FieldValue::Number(n)) = engine.get_field_value("result") {
1674 assert_eq!(*n, 42.0); }
1676 }
1677
1678 #[test]
1679 fn test_empty_field_calculation() {
1680 let mut engine = CalculationEngine::new();
1682
1683 let expr = ArithmeticExpression {
1685 tokens: vec![
1686 ExpressionToken::Field("undefined1".to_string()),
1687 ExpressionToken::Field("undefined2".to_string()),
1688 ExpressionToken::Operator(Operator::Multiply),
1689 ],
1690 };
1691
1692 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1693
1694 let _ = engine.calculate_field("result");
1696 if let Some(FieldValue::Number(n)) = engine.get_field_value("result") {
1697 assert_eq!(*n, 0.0); }
1699 }
1700
1701 #[test]
1702 fn test_max_function_with_empty_fields() {
1703 let mut engine = CalculationEngine::new();
1705
1706 engine.set_field_value("val1", FieldValue::Number(10.0));
1707 engine.set_field_value("val2", FieldValue::Empty);
1708 engine.set_field_value("val3", FieldValue::Number(25.0));
1709 engine.set_field_value("val4", FieldValue::Text("invalid".to_string()));
1710
1711 let _ = engine.add_calculation(
1712 "max_result",
1713 Calculation::Function(CalculationFunction::Max(vec![
1714 "val1".to_string(),
1715 "val2".to_string(),
1716 "val3".to_string(),
1717 "val4".to_string(),
1718 ])),
1719 );
1720
1721 let _ = engine.calculate_field("max_result");
1722 if let Some(FieldValue::Number(n)) = engine.get_field_value("max_result") {
1723 assert_eq!(*n, 25.0); }
1725 }
1726
1727 #[test]
1730 fn test_expression_parsing_comprehensive_edge_cases() {
1731 let test_cases = vec![
1733 ("((a + b)", "Mismatched left parentheses"),
1734 ("a + b))", "Mismatched right parentheses"),
1735 ("a ++ b", "Double operators"),
1736 ("+ a", "Leading operator"),
1737 ("a +", "Trailing operator"),
1738 ("5..3", "Double decimal point"),
1739 ("3.14.159", "Multiple decimal points"),
1740 ("a + * b", "Consecutive operators"),
1741 ("(a + b) * ", "Operator without operand"),
1742 ("@#$%", "Invalid characters"),
1743 ("", "Empty expression"),
1744 (" \t\n ", "Whitespace only"),
1745 ];
1746
1747 for (expr, description) in test_cases {
1748 let result = ArithmeticExpression::from_string(expr);
1749 assert!(
1750 result.is_err(),
1751 "Expression '{}' should fail parsing: {}",
1752 expr,
1753 description
1754 );
1755 }
1756
1757 let valid_cases = vec![
1759 ("()", 0.0), ("a b", 0.0), ("123abc", 123.0), ];
1763
1764 let mut engine = CalculationEngine::new();
1765 engine.set_field_value("a", FieldValue::Number(5.0));
1766 engine.set_field_value("b", FieldValue::Number(3.0));
1767
1768 for (i, (expr, _expected)) in valid_cases.iter().enumerate() {
1769 let result = ArithmeticExpression::from_string(expr);
1770 match result {
1772 Ok(parsed_expr) => {
1773 let calc_name = format!("edge_valid_{}", i);
1775 let add_result =
1776 engine.add_calculation(&calc_name, Calculation::Arithmetic(parsed_expr));
1777 let _ = add_result;
1779 }
1780 Err(_) => {
1781 }
1783 }
1784 }
1785 }
1786
1787 #[test]
1788 fn test_arithmetic_overflow_edge_cases() {
1789 let mut engine = CalculationEngine::new();
1790
1791 engine.set_field_value("max_val", FieldValue::Number(f64::MAX));
1793 engine.set_field_value("min_val", FieldValue::Number(f64::MIN));
1794 engine.set_field_value("infinity", FieldValue::Number(f64::INFINITY));
1795 engine.set_field_value("neg_infinity", FieldValue::Number(f64::NEG_INFINITY));
1796 engine.set_field_value("zero", FieldValue::Number(0.0));
1797 engine.set_field_value("small", FieldValue::Number(f64::MIN_POSITIVE));
1798
1799 let overflow_expr = ArithmeticExpression::from_string("max_val * 2").unwrap();
1801 engine
1802 .add_calculation("overflow_result", Calculation::Arithmetic(overflow_expr))
1803 .unwrap();
1804
1805 let overflow_result = engine.get_field_value("overflow_result").unwrap();
1806 assert!(overflow_result.to_number().is_infinite());
1807
1808 let inf_expr = ArithmeticExpression::from_string("infinity + 100").unwrap();
1810 engine
1811 .add_calculation("inf_result", Calculation::Arithmetic(inf_expr))
1812 .unwrap();
1813
1814 let inf_result = engine.get_field_value("inf_result").unwrap();
1815 assert_eq!(inf_result.to_number(), f64::INFINITY);
1816
1817 let nan_expr = ArithmeticExpression::from_string("infinity - infinity").unwrap();
1819 engine
1820 .add_calculation("nan_result", Calculation::Arithmetic(nan_expr))
1821 .unwrap();
1822
1823 let nan_result = engine.get_field_value("nan_result").unwrap();
1824 assert!(nan_result.to_number().is_nan());
1825 }
1826
1827 #[test]
1828 fn test_complex_financial_calculations() {
1829 let mut engine = CalculationEngine::new();
1830
1831 engine.set_field_value("unit_price", FieldValue::Number(19.99));
1833 engine.set_field_value("quantity", FieldValue::Number(150.0));
1834 engine.set_field_value("discount_rate", FieldValue::Number(0.15)); engine.set_field_value("tax_rate", FieldValue::Number(0.08)); engine.set_field_value("shipping_base", FieldValue::Number(25.0));
1837 engine.set_field_value("shipping_per_item", FieldValue::Number(1.50));
1838
1839 let subtotal_expr = ArithmeticExpression::from_string("unit_price * quantity").unwrap();
1841 engine
1842 .add_calculation("subtotal", Calculation::Arithmetic(subtotal_expr))
1843 .unwrap();
1844
1845 let discount_expr = ArithmeticExpression::from_string("subtotal * discount_rate").unwrap();
1847 engine
1848 .add_calculation("discount_amount", Calculation::Arithmetic(discount_expr))
1849 .unwrap();
1850
1851 let after_discount_expr =
1853 ArithmeticExpression::from_string("subtotal - discount_amount").unwrap();
1854 engine
1855 .add_calculation(
1856 "after_discount",
1857 Calculation::Arithmetic(after_discount_expr),
1858 )
1859 .unwrap();
1860
1861 let shipping_expr =
1863 ArithmeticExpression::from_string("shipping_base + quantity * shipping_per_item")
1864 .unwrap();
1865 engine
1866 .add_calculation("shipping", Calculation::Arithmetic(shipping_expr))
1867 .unwrap();
1868
1869 let pretax_expr = ArithmeticExpression::from_string("after_discount + shipping").unwrap();
1871 engine
1872 .add_calculation("pretax_total", Calculation::Arithmetic(pretax_expr))
1873 .unwrap();
1874
1875 let tax_expr = ArithmeticExpression::from_string("pretax_total * tax_rate").unwrap();
1877 engine
1878 .add_calculation("tax_amount", Calculation::Arithmetic(tax_expr))
1879 .unwrap();
1880
1881 let total_expr = ArithmeticExpression::from_string("pretax_total + tax_amount").unwrap();
1883 engine
1884 .add_calculation("final_total", Calculation::Arithmetic(total_expr))
1885 .unwrap();
1886
1887 let subtotal = engine.get_field_value("subtotal").unwrap().to_number();
1889 assert!(
1890 (subtotal - 2998.5).abs() < 0.01,
1891 "Subtotal calculation incorrect: expected 2998.5, got {}",
1892 subtotal
1893 );
1894 let discount_amount = engine
1895 .get_field_value("discount_amount")
1896 .unwrap()
1897 .to_number();
1898 assert!(
1899 (discount_amount - 449.775).abs() < 0.01,
1900 "Discount amount calculation incorrect: expected 449.775, got {}",
1901 discount_amount
1902 );
1903
1904 let after_discount = engine
1905 .get_field_value("after_discount")
1906 .unwrap()
1907 .to_number();
1908 assert!(
1909 (after_discount - 2548.725).abs() < 0.01,
1910 "After discount calculation incorrect: expected 2548.725, got {}",
1911 after_discount
1912 );
1913
1914 assert_eq!(
1915 engine.get_field_value("shipping").unwrap().to_number(),
1916 250.0
1917 ); let pretax_total = engine.get_field_value("pretax_total").unwrap().to_number();
1920 assert!(
1921 (pretax_total - 2798.725).abs() < 0.01,
1922 "Pretax total calculation incorrect: expected 2798.725, got {}",
1923 pretax_total
1924 );
1925
1926 let final_total = engine.get_field_value("final_total").unwrap().to_number();
1928 let tax_amount = engine.get_field_value("tax_amount").unwrap().to_number();
1929 let pretax_total = engine.get_field_value("pretax_total").unwrap().to_number();
1930 let expected_tax = pretax_total * 0.08;
1931 let expected_final = pretax_total + expected_tax;
1932
1933 assert!(
1934 (tax_amount - expected_tax).abs() < 0.01,
1935 "Tax amount calculation incorrect: expected {}, got {}",
1936 expected_tax,
1937 tax_amount
1938 );
1939 assert!(
1940 (final_total - expected_final).abs() < 0.01,
1941 "Final total calculation incorrect: expected {}, got {}",
1942 expected_final,
1943 final_total
1944 );
1945 }
1946
1947 #[test]
1948 fn test_deeply_nested_expressions() {
1949 let mut engine = CalculationEngine::new();
1950
1951 engine.set_field_value("a", FieldValue::Number(2.0));
1952 engine.set_field_value("b", FieldValue::Number(3.0));
1953 engine.set_field_value("c", FieldValue::Number(4.0));
1954 engine.set_field_value("d", FieldValue::Number(5.0));
1955
1956 let deep_expr = ArithmeticExpression::from_string(
1958 "(((a + b) * (c - d)) / ((a * b) + (c / d))) ^ 2 + ((a - b) * (c + d))",
1959 )
1960 .unwrap();
1961 engine
1962 .add_calculation("deep_result", Calculation::Arithmetic(deep_expr))
1963 .unwrap();
1964
1965 let result = engine.get_field_value("deep_result").unwrap();
1967 let result_num = result.to_number();
1968 assert!(
1969 result_num.is_finite() || result_num.is_nan(),
1970 "Deep expression should produce a finite number or NaN, got {}",
1971 result_num
1972 );
1973
1974 let chain_expr = ArithmeticExpression::from_string(
1976 "a + b - c + d * a / b + c - d ^ 2 + a * b * c / d - a + b + c - d",
1977 )
1978 .unwrap();
1979 engine
1980 .add_calculation("chain_result", Calculation::Arithmetic(chain_expr))
1981 .unwrap();
1982
1983 let chain_result = engine.get_field_value("chain_result").unwrap();
1984 assert!(chain_result.to_number().is_finite());
1985 }
1986
1987 #[test]
1988 fn test_comprehensive_function_combinations() {
1989 let mut engine = CalculationEngine::new();
1990
1991 let field_names: Vec<String> = (1..=10).map(|i| format!("val{}", i)).collect();
1993 let values = vec![10.0, 20.0, 5.0, 15.0, 25.0, 8.0, 30.0, 12.0, 18.0, 22.0];
1994
1995 for (name, value) in field_names.iter().zip(values.iter()) {
1996 engine.set_field_value(name, FieldValue::Number(*value));
1997 }
1998
1999 let sum_calc = Calculation::Function(CalculationFunction::Sum(field_names.clone()));
2001 engine.add_calculation("total_sum", sum_calc).unwrap();
2002 assert_eq!(
2003 engine.get_field_value("total_sum").unwrap().to_number(),
2004 165.0
2005 );
2006
2007 engine.set_field_value("condition1", FieldValue::Boolean(true));
2009 engine.set_field_value("condition2", FieldValue::Boolean(false));
2010
2011 let nested_if = Calculation::Function(CalculationFunction::If {
2012 condition_field: "condition1".to_string(),
2013 true_value: Box::new(Calculation::Function(CalculationFunction::If {
2014 condition_field: "condition2".to_string(),
2015 true_value: Box::new(Calculation::Constant(FieldValue::Number(100.0))),
2016 false_value: Box::new(Calculation::Constant(FieldValue::Number(200.0))),
2017 })),
2018 false_value: Box::new(Calculation::Constant(FieldValue::Number(300.0))),
2019 });
2020 engine
2021 .add_calculation("nested_if_result", nested_if)
2022 .unwrap();
2023 assert_eq!(
2024 engine
2025 .get_field_value("nested_if_result")
2026 .unwrap()
2027 .to_number(),
2028 200.0
2029 );
2030
2031 let product_calc =
2033 Calculation::Function(CalculationFunction::Product(field_names[0..3].to_vec()));
2034 engine
2035 .add_calculation("product_result", product_calc)
2036 .unwrap();
2037 assert_eq!(
2038 engine
2039 .get_field_value("product_result")
2040 .unwrap()
2041 .to_number(),
2042 1000.0
2043 ); }
2045
2046 #[test]
2047 fn test_error_recovery_and_handling() {
2048 let mut engine = CalculationEngine::new();
2049
2050 engine.set_field_value("valid1", FieldValue::Number(10.0));
2052 engine.set_field_value("valid2", FieldValue::Number(20.0));
2053
2054 let expr_with_invalid =
2056 ArithmeticExpression::from_string("valid1 + nonexistent + valid2").unwrap();
2057 engine
2058 .add_calculation("mixed_result", Calculation::Arithmetic(expr_with_invalid))
2059 .unwrap();
2060
2061 assert_eq!(
2063 engine.get_field_value("mixed_result").unwrap().to_number(),
2064 30.0
2065 ); let empty_sum = Calculation::Function(CalculationFunction::Sum(vec![]));
2069 engine.add_calculation("empty_sum", empty_sum).unwrap();
2070 assert_eq!(
2071 engine.get_field_value("empty_sum").unwrap().to_number(),
2072 0.0
2073 );
2074
2075 let mixed_avg = Calculation::Function(CalculationFunction::Average(vec![
2077 "valid1".to_string(),
2078 "nonexistent1".to_string(),
2079 "valid2".to_string(),
2080 "nonexistent2".to_string(),
2081 ]));
2082 engine.add_calculation("mixed_avg", mixed_avg).unwrap();
2083 let avg_result = engine.get_field_value("mixed_avg").unwrap().to_number();
2087 assert!(
2090 avg_result == 7.5 || avg_result == 15.0,
2091 "Average result should be either 7.5 or 15.0, got {}",
2092 avg_result
2093 );
2094 }
2095
2096 #[test]
2097 fn test_real_world_business_scenarios() {
2098 let mut engine = CalculationEngine::new();
2099
2100 engine.set_field_value("principal", FieldValue::Number(200000.0)); engine.set_field_value("annual_rate", FieldValue::Number(0.035)); engine.set_field_value("years", FieldValue::Number(30.0)); let monthly_rate_expr = ArithmeticExpression::from_string("annual_rate / 12").unwrap();
2107 engine
2108 .add_calculation("monthly_rate", Calculation::Arithmetic(monthly_rate_expr))
2109 .unwrap();
2110
2111 let total_payments_expr = ArithmeticExpression::from_string("years * 12").unwrap();
2113 engine
2114 .add_calculation(
2115 "total_payments",
2116 Calculation::Arithmetic(total_payments_expr),
2117 )
2118 .unwrap();
2119
2120 engine.set_field_value("hourly_rate", FieldValue::Number(25.50));
2122 engine.set_field_value("hours_worked", FieldValue::Number(42.5));
2123 engine.set_field_value("overtime_multiplier", FieldValue::Number(1.5));
2124 engine.set_field_value("standard_hours", FieldValue::Number(40.0));
2125
2126 let regular_pay_expr =
2128 ArithmeticExpression::from_string("standard_hours * hourly_rate").unwrap();
2129 engine
2130 .add_calculation("regular_pay", Calculation::Arithmetic(regular_pay_expr))
2131 .unwrap();
2132
2133 engine.set_field_value("overtime_hours", FieldValue::Number(2.5)); let overtime_expr =
2138 ArithmeticExpression::from_string("overtime_hours * hourly_rate * overtime_multiplier")
2139 .unwrap();
2140 engine
2141 .add_calculation("overtime_pay", Calculation::Arithmetic(overtime_expr))
2142 .unwrap();
2143
2144 let gross_expr = ArithmeticExpression::from_string("regular_pay + overtime_pay").unwrap();
2146 engine
2147 .add_calculation("gross_pay", Calculation::Arithmetic(gross_expr))
2148 .unwrap();
2149
2150 assert_eq!(
2152 engine.get_field_value("regular_pay").unwrap().to_number(),
2153 1020.0
2154 ); assert_eq!(
2156 engine.get_field_value("overtime_pay").unwrap().to_number(),
2157 95.625
2158 ); assert_eq!(
2160 engine.get_field_value("gross_pay").unwrap().to_number(),
2161 1115.625
2162 ); engine.set_field_value("batch1_qty", FieldValue::Number(100.0));
2166 engine.set_field_value("batch1_cost", FieldValue::Number(10.50));
2167 engine.set_field_value("batch2_qty", FieldValue::Number(75.0));
2168 engine.set_field_value("batch2_cost", FieldValue::Number(11.25));
2169 engine.set_field_value("batch3_qty", FieldValue::Number(50.0));
2170 engine.set_field_value("batch3_cost", FieldValue::Number(12.00));
2171
2172 let batch1_value_expr =
2174 ArithmeticExpression::from_string("batch1_qty * batch1_cost").unwrap();
2175 engine
2176 .add_calculation("batch1_value", Calculation::Arithmetic(batch1_value_expr))
2177 .unwrap();
2178
2179 let batch2_value_expr =
2180 ArithmeticExpression::from_string("batch2_qty * batch2_cost").unwrap();
2181 engine
2182 .add_calculation("batch2_value", Calculation::Arithmetic(batch2_value_expr))
2183 .unwrap();
2184
2185 let batch3_value_expr =
2186 ArithmeticExpression::from_string("batch3_qty * batch3_cost").unwrap();
2187 engine
2188 .add_calculation("batch3_value", Calculation::Arithmetic(batch3_value_expr))
2189 .unwrap();
2190
2191 let total_inventory_calc = Calculation::Function(CalculationFunction::Sum(vec![
2193 "batch1_value".to_string(),
2194 "batch2_value".to_string(),
2195 "batch3_value".to_string(),
2196 ]));
2197 engine
2198 .add_calculation("total_inventory", total_inventory_calc)
2199 .unwrap();
2200
2201 assert_eq!(
2203 engine.get_field_value("batch1_value").unwrap().to_number(),
2204 1050.0
2205 );
2206 assert_eq!(
2207 engine.get_field_value("batch2_value").unwrap().to_number(),
2208 843.75
2209 );
2210 assert_eq!(
2211 engine.get_field_value("batch3_value").unwrap().to_number(),
2212 600.0
2213 );
2214 assert_eq!(
2215 engine
2216 .get_field_value("total_inventory")
2217 .unwrap()
2218 .to_number(),
2219 2493.75
2220 );
2221 }
2222
2223 #[test]
2224 fn test_special_number_values() {
2225 let mut engine = CalculationEngine::new();
2226
2227 engine.set_field_value("nan_val", FieldValue::Number(f64::NAN));
2229 engine.set_field_value("normal_val", FieldValue::Number(10.0));
2230
2231 let nan_expr = ArithmeticExpression::from_string("nan_val + normal_val").unwrap();
2233 engine
2234 .add_calculation("nan_result", Calculation::Arithmetic(nan_expr))
2235 .unwrap();
2236
2237 let result = engine.get_field_value("nan_result").unwrap().to_number();
2238 assert!(result.is_nan());
2239
2240 let sum_with_nan = Calculation::Function(CalculationFunction::Sum(vec![
2242 "nan_val".to_string(),
2243 "normal_val".to_string(),
2244 ]));
2245 engine.add_calculation("sum_nan", sum_with_nan).unwrap();
2246
2247 let sum_result = engine.get_field_value("sum_nan").unwrap().to_number();
2248 assert!(sum_result.is_nan());
2249
2250 engine.set_field_value("val1", FieldValue::Number(5.0));
2252 engine.set_field_value("val2", FieldValue::Number(15.0));
2253
2254 let max_with_nan = Calculation::Function(CalculationFunction::Max(vec![
2255 "nan_val".to_string(),
2256 "val1".to_string(),
2257 "val2".to_string(),
2258 ]));
2259 engine.add_calculation("max_nan", max_with_nan).unwrap();
2260
2261 let max_result = engine.get_field_value("max_nan").unwrap().to_number();
2262 assert_eq!(max_result, 15.0); }
2264
2265 #[test]
2266 fn test_precision_and_rounding_scenarios() {
2267 let mut engine = CalculationEngine::new();
2268
2269 engine.set_field_value("precise1", FieldValue::Number(1.0 / 3.0));
2271 engine.set_field_value("precise2", FieldValue::Number(2.0 / 3.0));
2272
2273 let precision_expr = ArithmeticExpression::from_string("precise1 + precise2").unwrap();
2274 engine
2275 .add_calculation("precision_result", Calculation::Arithmetic(precision_expr))
2276 .unwrap();
2277
2278 let result = engine
2279 .get_field_value("precision_result")
2280 .unwrap()
2281 .to_number();
2282 assert!((result - 1.0).abs() < 1e-15); engine.set_field_value("tiny", FieldValue::Number(1e-100));
2286 engine.set_field_value("huge", FieldValue::Number(1e100));
2287
2288 let scale_expr = ArithmeticExpression::from_string("tiny * huge").unwrap();
2289 engine
2290 .add_calculation("scale_result", Calculation::Arithmetic(scale_expr))
2291 .unwrap();
2292
2293 let scale_result = engine.get_field_value("scale_result").unwrap().to_number();
2294 assert!((scale_result - 1.0).abs() < 1e-14);
2295
2296 engine.set_field_value("price", FieldValue::Number(19.999));
2298 engine.set_field_value("quantity", FieldValue::Number(3.0));
2299
2300 let financial_expr = ArithmeticExpression::from_string("price * quantity").unwrap();
2301 engine
2302 .add_calculation("financial_result", Calculation::Arithmetic(financial_expr))
2303 .unwrap();
2304
2305 let financial_result = engine
2306 .get_field_value("financial_result")
2307 .unwrap()
2308 .to_number();
2309 assert!((financial_result - 59.997).abs() < 1e-10);
2311 }
2312
2313 #[test]
2314 fn test_extreme_calculation_chains() {
2315 let mut engine = CalculationEngine::new();
2316
2317 engine.set_field_value("seed", FieldValue::Number(1.0));
2319
2320 for i in 1..=50 {
2322 let prev = if i == 1 {
2323 "seed".to_string()
2324 } else {
2325 format!("chain_{}", i - 1)
2326 };
2327 let current = format!("chain_{}", i);
2328
2329 let expr = ArithmeticExpression::from_string(&format!("{} + 1", prev)).unwrap();
2330 engine
2331 .add_calculation(¤t, Calculation::Arithmetic(expr))
2332 .unwrap();
2333 }
2334
2335 assert_eq!(
2337 engine.get_field_value("chain_50").unwrap().to_number(),
2338 51.0
2339 );
2340
2341 engine.set_field_value("seed", FieldValue::Number(10.0));
2343 assert_eq!(
2344 engine.get_field_value("chain_50").unwrap().to_number(),
2345 60.0
2346 ); engine.set_field_value("base", FieldValue::Number(5.0));
2350
2351 for i in 1..=20 {
2352 let field_name = format!("derived_{}", i);
2353 let expr = ArithmeticExpression::from_string(&format!("base * {}", i)).unwrap();
2354 engine
2355 .add_calculation(&field_name, Calculation::Arithmetic(expr))
2356 .unwrap();
2357 }
2358
2359 for i in 1..=20 {
2361 let field_name = format!("derived_{}", i);
2362 let expected = 5.0 * i as f64;
2363 assert_eq!(
2364 engine.get_field_value(&field_name).unwrap().to_number(),
2365 expected
2366 );
2367 }
2368
2369 engine.set_field_value("base", FieldValue::Number(10.0));
2371 for i in 1..=20 {
2372 let field_name = format!("derived_{}", i);
2373 let expected = 10.0 * i as f64;
2374 assert_eq!(
2375 engine.get_field_value(&field_name).unwrap().to_number(),
2376 expected
2377 );
2378 }
2379 }
2380
2381 #[test]
2382 fn test_comprehensive_operator_combinations() {
2383 let mut engine = CalculationEngine::new();
2384
2385 engine.set_field_value("a", FieldValue::Number(12.0));
2386 engine.set_field_value("b", FieldValue::Number(4.0));
2387 engine.set_field_value("c", FieldValue::Number(3.0));
2388 engine.set_field_value("d", FieldValue::Number(2.0));
2389
2390 let test_cases = vec![
2392 ("a + b * c - d", 22.0), ("a / b + c * d", 9.0), ("a % b + c ^ d", 9.0), ("(a + b) * (c - d)", 16.0), ("a ^ d / b + c", 39.0), ("a - b / c + d * c", 16.67), ];
2399
2400 for (i, (expr_str, expected)) in test_cases.iter().enumerate() {
2401 let expr = ArithmeticExpression::from_string(expr_str).unwrap();
2402 let field_name = format!("test_{}", i);
2403 engine
2404 .add_calculation(&field_name, Calculation::Arithmetic(expr))
2405 .unwrap();
2406
2407 let result = engine.get_field_value(&field_name).unwrap().to_number();
2408 assert!(
2409 (result - expected).abs() < 0.1,
2410 "Expression '{}' expected {}, got {}",
2411 expr_str,
2412 expected,
2413 result
2414 );
2415 }
2416 }
2417
2418 #[test]
2419 fn test_conditional_calculation_complexity() {
2420 let mut engine = CalculationEngine::new();
2421
2422 engine.set_field_value("customer_type", FieldValue::Text("premium".to_string()));
2424 engine.set_field_value("order_amount", FieldValue::Number(1000.0));
2425 engine.set_field_value("is_premium", FieldValue::Boolean(true));
2426 engine.set_field_value("is_bulk_order", FieldValue::Boolean(true));
2427
2428 let premium_discount = Calculation::Function(CalculationFunction::If {
2430 condition_field: "is_premium".to_string(),
2431 true_value: Box::new(Calculation::Constant(FieldValue::Number(0.15))), false_value: Box::new(Calculation::Constant(FieldValue::Number(0.05))), });
2434 engine
2435 .add_calculation("base_discount", premium_discount)
2436 .unwrap();
2437
2438 let bulk_discount = Calculation::Function(CalculationFunction::If {
2440 condition_field: "is_bulk_order".to_string(),
2441 true_value: Box::new(Calculation::Constant(FieldValue::Number(0.05))), false_value: Box::new(Calculation::Constant(FieldValue::Number(0.0))),
2443 });
2444 engine.add_calculation("bulk_bonus", bulk_discount).unwrap();
2445
2446 let total_discount_expr =
2448 ArithmeticExpression::from_string("base_discount + bulk_bonus").unwrap();
2449 engine
2450 .add_calculation(
2451 "total_discount_rate",
2452 Calculation::Arithmetic(total_discount_expr),
2453 )
2454 .unwrap();
2455
2456 let discount_amount_expr =
2458 ArithmeticExpression::from_string("order_amount * total_discount_rate").unwrap();
2459 engine
2460 .add_calculation(
2461 "discount_amount",
2462 Calculation::Arithmetic(discount_amount_expr),
2463 )
2464 .unwrap();
2465
2466 let final_amount_expr =
2468 ArithmeticExpression::from_string("order_amount - discount_amount").unwrap();
2469 engine
2470 .add_calculation("final_amount", Calculation::Arithmetic(final_amount_expr))
2471 .unwrap();
2472
2473 assert_eq!(
2475 engine.get_field_value("base_discount").unwrap().to_number(),
2476 0.15
2477 );
2478 assert_eq!(
2479 engine.get_field_value("bulk_bonus").unwrap().to_number(),
2480 0.05
2481 );
2482 assert_eq!(
2483 engine
2484 .get_field_value("total_discount_rate")
2485 .unwrap()
2486 .to_number(),
2487 0.20
2488 );
2489 assert_eq!(
2490 engine
2491 .get_field_value("discount_amount")
2492 .unwrap()
2493 .to_number(),
2494 200.0
2495 );
2496 assert_eq!(
2497 engine.get_field_value("final_amount").unwrap().to_number(),
2498 800.0
2499 );
2500
2501 engine.set_field_value("is_premium", FieldValue::Boolean(false));
2503 assert_eq!(
2504 engine.get_field_value("base_discount").unwrap().to_number(),
2505 0.05
2506 );
2507 assert_eq!(
2508 engine.get_field_value("final_amount").unwrap().to_number(),
2509 900.0
2510 ); }
2512
2513 #[test]
2514 fn test_field_value_type_edge_cases() {
2515 let mut engine = CalculationEngine::new();
2516
2517 let edge_cases = vec![
2519 ("", 0.0), ("0", 0.0), ("0.0", 0.0), ("-0", 0.0), ("123.456", 123.456), ("-123.456", -123.456), ("1.23e10", 1.23e10), ("1.23E-5", 1.23e-5), ("inf", f64::INFINITY), ("-inf", f64::NEG_INFINITY), ("nan", f64::NAN), ("not_a_number", 0.0), ("123abc", 0.0), (" 456 ", 0.0), ];
2534
2535 for (i, (text_val, expected)) in edge_cases.iter().enumerate() {
2536 let field_name = format!("edge_case_{}", i);
2537 engine.set_field_value(&field_name, FieldValue::Text((*text_val).to_string()));
2538
2539 let result = engine.get_field_value(&field_name).unwrap().to_number();
2540
2541 if expected.is_nan() {
2542 assert!(
2545 result.is_nan() || result == 0.0,
2546 "Text '{}' should convert to NaN or 0.0, got {}",
2547 text_val,
2548 result
2549 );
2550 } else if expected.is_infinite() {
2551 assert_eq!(
2552 result, *expected,
2553 "Text '{}' should convert to {}, got {}",
2554 text_val, expected, result
2555 );
2556 } else {
2557 assert!(
2558 (result - expected).abs() < 1e-10,
2559 "Text '{}' should convert to {}, got {}",
2560 text_val,
2561 expected,
2562 result
2563 );
2564 }
2565 }
2566 }
2567
2568 #[test]
2569 fn test_calculation_engine_state_management() {
2570 let mut engine = CalculationEngine::new();
2571
2572 let summary = engine.get_summary();
2574 assert_eq!(summary.total_fields, 0);
2575 assert_eq!(summary.calculated_fields, 0);
2576
2577 engine.set_field_value("input1", FieldValue::Number(10.0));
2579 engine.set_field_value("input2", FieldValue::Number(20.0));
2580
2581 let expr = ArithmeticExpression::from_string("input1 + input2").unwrap();
2582 engine
2583 .add_calculation("output", Calculation::Arithmetic(expr))
2584 .unwrap();
2585
2586 let summary_after = engine.get_summary();
2587 assert_eq!(summary_after.total_fields, 3); assert_eq!(summary_after.calculated_fields, 1); assert_eq!(summary_after.calculation_order, vec!["output".to_string()]);
2590
2591 engine.remove_calculation("output");
2593 let summary_removed = engine.get_summary();
2594 assert_eq!(summary_removed.total_fields, 2); assert_eq!(summary_removed.calculated_fields, 0);
2596 assert_eq!(summary_removed.calculation_order.len(), 0);
2597
2598 let display_str = format!("{}", summary_removed);
2600 assert!(display_str.contains("Total fields: 2"));
2601 assert!(display_str.contains("Calculated fields: 0"));
2602 }
2603
2604 #[test]
2605 fn test_calculation_error_boundary_conditions() {
2606 let mut engine = CalculationEngine::new();
2607
2608 engine.set_field_value("existing", FieldValue::Number(42.0));
2610 assert_eq!(
2611 engine.get_field_value("existing").unwrap().to_number(),
2612 42.0
2613 );
2614
2615 let expr = ArithmeticExpression::from_string("10 + 5").unwrap();
2617 engine
2618 .add_calculation("existing", Calculation::Arithmetic(expr))
2619 .unwrap();
2620 assert_eq!(
2621 engine.get_field_value("existing").unwrap().to_number(),
2622 15.0
2623 );
2624
2625 engine.set_field_value("base1", FieldValue::Number(5.0));
2627 engine.set_field_value("base2", FieldValue::Number(10.0));
2628
2629 let calc1 = ArithmeticExpression::from_string("base1 * 2").unwrap();
2630 let calc2 = ArithmeticExpression::from_string("base2 / 2").unwrap();
2631
2632 engine
2633 .add_calculation("independent1", Calculation::Arithmetic(calc1))
2634 .unwrap();
2635 engine
2636 .add_calculation("independent2", Calculation::Arithmetic(calc2))
2637 .unwrap();
2638
2639 assert_eq!(
2641 engine.get_field_value("independent1").unwrap().to_number(),
2642 10.0
2643 );
2644 assert_eq!(
2645 engine.get_field_value("independent2").unwrap().to_number(),
2646 5.0
2647 );
2648
2649 engine.recalculate_all().unwrap();
2651 assert_eq!(
2652 engine.get_field_value("existing").unwrap().to_number(),
2653 15.0
2654 );
2655 assert_eq!(
2656 engine.get_field_value("independent1").unwrap().to_number(),
2657 10.0
2658 );
2659 assert_eq!(
2660 engine.get_field_value("independent2").unwrap().to_number(),
2661 5.0
2662 );
2663 }
2664
2665 #[test]
2666 fn test_calculation_stress_and_boundary_conditions() {
2667 let mut engine = CalculationEngine::new();
2668
2669 for i in 0..100 {
2671 let field_name = format!("rapid_field_{}", i);
2672 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2673 }
2674
2675 let field_refs = (0..50)
2677 .map(|i| format!("rapid_field_{}", i))
2678 .collect::<Vec<_>>();
2679
2680 let sum_calc = Calculation::Function(CalculationFunction::Sum(field_refs.clone()));
2681 engine.add_calculation("rapid_sum", sum_calc).unwrap();
2682
2683 let avg_calc = Calculation::Function(CalculationFunction::Average(field_refs));
2684 engine.add_calculation("rapid_avg", avg_calc).unwrap();
2685
2686 assert_eq!(
2688 engine.get_field_value("rapid_sum").unwrap().to_number(),
2689 1225.0
2690 );
2691 assert_eq!(
2692 engine.get_field_value("rapid_avg").unwrap().to_number(),
2693 24.5
2694 );
2695
2696 for update_round in 0..10 {
2698 for i in 0..50 {
2699 let field_name = format!("rapid_field_{}", i);
2700 let new_value = (i as f64) * (update_round as f64 + 1.0);
2701 engine.set_field_value(&field_name, FieldValue::Number(new_value));
2702 }
2703
2704 let current_sum = engine.get_field_value("rapid_sum").unwrap().to_number();
2706 let expected_sum = 1225.0 * (update_round as f64 + 1.0);
2707 assert!(
2708 (current_sum - expected_sum).abs() < 0.01,
2709 "Sum calculation incorrect in round {}: expected {}, got {}",
2710 update_round,
2711 expected_sum,
2712 current_sum
2713 );
2714 }
2715 }
2716
2717 #[test]
2718 fn test_calculation_engine_memory_and_cleanup() {
2719 let mut engine = CalculationEngine::new();
2720
2721 for i in 0..50 {
2723 let field_name = format!("temp_field_{}", i);
2724 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2725
2726 let expr = ArithmeticExpression::from_string(&format!("temp_field_{} * 2", i)).unwrap();
2727 let calc_name = format!("temp_calc_{}", i);
2728 engine
2729 .add_calculation(&calc_name, Calculation::Arithmetic(expr))
2730 .unwrap();
2731 }
2732
2733 for i in 0..50 {
2735 let calc_name = format!("temp_calc_{}", i);
2736 let expected = (i as f64) * 2.0;
2737 assert_eq!(
2738 engine.get_field_value(&calc_name).unwrap().to_number(),
2739 expected
2740 );
2741 }
2742
2743 for i in 0..25 {
2745 let calc_name = format!("temp_calc_{}", i);
2746 engine.remove_calculation(&calc_name);
2747 }
2748
2749 for i in 0..25 {
2751 let calc_name = format!("temp_calc_{}", i);
2752 assert!(engine.get_field_value(&calc_name).is_none());
2753 }
2754
2755 for i in 25..50 {
2756 let calc_name = format!("temp_calc_{}", i);
2757 let expected = (i as f64) * 2.0;
2758 assert_eq!(
2759 engine.get_field_value(&calc_name).unwrap().to_number(),
2760 expected
2761 );
2762 }
2763
2764 let summary = engine.get_summary();
2766 assert_eq!(summary.total_fields, 75);
2768 assert_eq!(summary.calculated_fields, 25);
2769 }
2770
2771 #[test]
2772 fn test_maximum_expression_complexity() {
2773 let mut engine = CalculationEngine::new();
2774
2775 for i in 1..=10 {
2777 let field_name = format!("x{}", i);
2778 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2779 }
2780
2781 let complex_expr = ArithmeticExpression::from_string(
2783 "((x1 + x2) * (x3 - x4) / (x5 + 1)) ^ 2 + ((x6 * x7) % (x8 + x9)) - x10",
2784 )
2785 .unwrap();
2786
2787 engine
2788 .add_calculation("max_complexity", Calculation::Arithmetic(complex_expr))
2789 .unwrap();
2790
2791 let result = engine
2793 .get_field_value("max_complexity")
2794 .unwrap()
2795 .to_number();
2796 assert!(
2797 result.is_finite(),
2798 "Maximum complexity expression should produce a finite result, got {}",
2799 result
2800 );
2801
2802 engine.set_field_value("condition_flag", FieldValue::Boolean(true));
2804
2805 let conditional_complex = Calculation::Function(CalculationFunction::If {
2806 condition_field: "condition_flag".to_string(),
2807 true_value: Box::new(Calculation::Function(CalculationFunction::Sum(vec![
2808 "x1".to_string(),
2809 "x2".to_string(),
2810 "x3".to_string(),
2811 "x4".to_string(),
2812 "x5".to_string(),
2813 ]))),
2814 false_value: Box::new(Calculation::Function(CalculationFunction::Product(vec![
2815 "x6".to_string(),
2816 "x7".to_string(),
2817 "x8".to_string(),
2818 ]))),
2819 });
2820
2821 engine
2822 .add_calculation("conditional_complex", conditional_complex)
2823 .unwrap();
2824
2825 let conditional_result = engine
2826 .get_field_value("conditional_complex")
2827 .unwrap()
2828 .to_number();
2829 assert_eq!(conditional_result, 15.0); engine.set_field_value("condition_flag", FieldValue::Boolean(false));
2833 let switched_result = engine
2834 .get_field_value("conditional_complex")
2835 .unwrap()
2836 .to_number();
2837 assert_eq!(switched_result, 336.0); }
2839
2840 #[test]
2841 fn test_calculation_order_determinism() {
2842 let mut engine1 = CalculationEngine::new();
2844 let mut engine2 = CalculationEngine::new();
2845
2846 for i in 1..=5 {
2848 let field_name = format!("base{}", i);
2849 let value = FieldValue::Number(i as f64 * 10.0);
2850 engine1.set_field_value(&field_name, value.clone());
2851 engine2.set_field_value(&field_name, value);
2852 }
2853
2854 let calculations = vec![
2856 ("calc1", "base1 + base2"), ("calc2", "base3 * base4"), ("calc3", "base5 / base1"), ("calc4", "base2 - base3"), ];
2861
2862 for (name, expr) in &calculations {
2864 let parsed_expr = ArithmeticExpression::from_string(expr).unwrap();
2865 engine1
2866 .add_calculation(*name, Calculation::Arithmetic(parsed_expr))
2867 .unwrap();
2868 }
2869
2870 for (name, expr) in calculations.iter().rev() {
2872 let parsed_expr = ArithmeticExpression::from_string(expr).unwrap();
2873 engine2
2874 .add_calculation(*name, Calculation::Arithmetic(parsed_expr))
2875 .unwrap();
2876 }
2877
2878 let expected_results = vec![
2880 ("calc1", 30.0),
2881 ("calc2", 1200.0),
2882 ("calc3", 5.0),
2883 ("calc4", -10.0),
2884 ];
2885
2886 for (field_name, expected) in expected_results {
2887 let result1 = engine1.get_field_value(field_name).unwrap().to_number();
2888 let result2 = engine2.get_field_value(field_name).unwrap().to_number();
2889
2890 assert_eq!(
2891 result1, expected,
2892 "Engine1 calculation {} should be {}, got {}",
2893 field_name, expected, result1
2894 );
2895 assert_eq!(
2896 result2, expected,
2897 "Engine2 calculation {} should be {}, got {}",
2898 field_name, expected, result2
2899 );
2900 assert_eq!(
2901 result1, result2,
2902 "Both engines should produce same result for {}: engine1={}, engine2={}",
2903 field_name, result1, result2
2904 );
2905 }
2906
2907 let summary1 = engine1.get_summary();
2909 let summary2 = engine2.get_summary();
2910
2911 assert_eq!(summary1.total_fields, summary2.total_fields);
2912 assert_eq!(summary1.calculated_fields, summary2.calculated_fields);
2913 assert_eq!(
2914 summary1.calculation_order.len(),
2915 summary2.calculation_order.len()
2916 );
2917 }
2918}