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 0.0 }
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
463 .pop()
464 .expect("Stack should have at least 2 elements after length check");
465 let left = stack
466 .pop()
467 .expect("Stack should have at least 2 elements after length check");
468 stack.push(op.apply(left, right));
469 }
470 _ => {}
471 }
472 }
473
474 stack
475 .pop()
476 .ok_or_else(|| PdfError::InvalidStructure("Invalid expression".to_string()))
477 }
478
479 fn infix_to_postfix(
481 &self,
482 tokens: &[ExpressionToken],
483 ) -> Result<Vec<ExpressionToken>, PdfError> {
484 let mut output = Vec::new();
485 let mut operators = Vec::new();
486
487 for token in tokens {
488 match token {
489 ExpressionToken::Number(_) | ExpressionToken::Field(_) => {
490 output.push(token.clone());
491 }
492 ExpressionToken::Operator(op) => {
493 while let Some(ExpressionToken::Operator(top_op)) = operators.last() {
494 if top_op.precedence() >= op.precedence() {
495 if let Some(operator) = operators.pop() {
496 output.push(operator);
497 }
498 } else {
499 break;
500 }
501 }
502 operators.push(token.clone());
503 }
504 ExpressionToken::LeftParen => {
505 operators.push(token.clone());
506 }
507 ExpressionToken::RightParen => {
508 while let Some(op) = operators.pop() {
509 if matches!(op, ExpressionToken::LeftParen) {
510 break;
511 }
512 output.push(op);
513 }
514 }
515 }
516 }
517
518 while let Some(op) = operators.pop() {
519 output.push(op);
520 }
521
522 Ok(output)
523 }
524
525 fn evaluate_function(&self, func: &CalculationFunction) -> Result<FieldValue, PdfError> {
527 match func {
528 CalculationFunction::Sum(fields) => {
529 let sum = fields
530 .iter()
531 .filter_map(|f| self.field_values.get(f))
532 .map(|v| v.to_number())
533 .sum();
534 Ok(FieldValue::Number(sum))
535 }
536 CalculationFunction::Average(fields) => {
537 let values: Vec<f64> = fields
538 .iter()
539 .filter_map(|f| self.field_values.get(f))
540 .map(|v| v.to_number())
541 .collect();
542
543 if values.is_empty() {
544 Ok(FieldValue::Number(0.0))
545 } else {
546 let avg = values.iter().sum::<f64>() / values.len() as f64;
547 Ok(FieldValue::Number(avg))
548 }
549 }
550 CalculationFunction::Min(fields) => {
551 let min = fields
552 .iter()
553 .filter_map(|f| self.field_values.get(f))
554 .map(|v| v.to_number())
555 .filter(|n| !n.is_nan()) .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
557 .unwrap_or(0.0);
558 Ok(FieldValue::Number(min))
559 }
560 CalculationFunction::Max(fields) => {
561 let max = fields
562 .iter()
563 .filter_map(|f| self.field_values.get(f))
564 .map(|v| v.to_number())
565 .filter(|n| !n.is_nan()) .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
567 .unwrap_or(0.0);
568 Ok(FieldValue::Number(max))
569 }
570 CalculationFunction::Product(fields) => {
571 let product = fields
572 .iter()
573 .filter_map(|f| self.field_values.get(f))
574 .map(|v| v.to_number())
575 .product();
576 Ok(FieldValue::Number(product))
577 }
578 CalculationFunction::Count(fields) => {
579 let count = fields
580 .iter()
581 .filter_map(|f| self.field_values.get(f))
582 .filter(|v| !matches!(v, FieldValue::Empty))
583 .count() as f64;
584 Ok(FieldValue::Number(count))
585 }
586 CalculationFunction::If {
587 condition_field,
588 true_value,
589 false_value,
590 } => {
591 let condition = self
592 .field_values
593 .get(condition_field)
594 .map(|v| match v {
595 FieldValue::Boolean(b) => *b,
596 FieldValue::Number(n) => *n != 0.0,
597 FieldValue::Text(s) => !s.is_empty(),
598 FieldValue::Empty => false,
599 })
600 .unwrap_or(false);
601
602 if condition {
603 self.evaluate_calculation(true_value)
604 } else {
605 self.evaluate_calculation(false_value)
606 }
607 }
608 }
609 }
610
611 fn evaluate_javascript(&self, _code: &str) -> Result<FieldValue, PdfError> {
613 Ok(FieldValue::Empty)
619 }
620
621 pub fn recalculate_all(&mut self) -> Result<(), PdfError> {
623 for field in self.calculation_order.clone() {
624 self.calculate_field(&field)?;
625 }
626 Ok(())
627 }
628
629 pub fn remove_calculation(&mut self, field_name: &str) {
631 if self.calculations.remove(field_name).is_some() {
633 self.calculation_order.retain(|f| f != field_name);
635
636 self.dependencies.values_mut().for_each(|deps| {
638 deps.remove(field_name);
639 });
640
641 self.dependencies.remove(field_name);
643
644 self.field_values.remove(field_name);
646 }
647 }
648
649 pub fn get_summary(&self) -> CalculationSummary {
651 CalculationSummary {
652 total_fields: self.field_values.len(),
653 calculated_fields: self.calculations.len(),
654 dependencies: self.dependencies.len(),
655 calculation_order: self.calculation_order.clone(),
656 }
657 }
658}
659
660#[derive(Debug, Clone)]
662pub struct CalculationSummary {
663 pub total_fields: usize,
665 pub calculated_fields: usize,
667 pub dependencies: usize,
669 pub calculation_order: Vec<String>,
671}
672
673impl fmt::Display for CalculationSummary {
674 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
675 write!(
676 f,
677 "Calculation Summary:\n\
678 - Total fields: {}\n\
679 - Calculated fields: {}\n\
680 - Dependencies: {}\n\
681 - Calculation order: {}",
682 self.total_fields,
683 self.calculated_fields,
684 self.dependencies,
685 self.calculation_order.join(" -> ")
686 )
687 }
688}
689
690impl ArithmeticExpression {
691 pub fn from_string(expr: &str) -> Result<Self, PdfError> {
693 let tokens = Self::tokenize(expr)?;
694 Ok(Self { tokens })
695 }
696
697 fn tokenize(expr: &str) -> Result<Vec<ExpressionToken>, PdfError> {
699 let mut tokens = Vec::new();
700 let mut chars = expr.chars().peekable();
701
702 if expr.trim().is_empty() {
704 return Err(PdfError::InvalidFormat("Empty expression".to_string()));
705 }
706
707 while let Some(ch) = chars.next() {
708 match ch {
709 ' ' | '\t' | '\n' => continue,
710 '+' => tokens.push(ExpressionToken::Operator(Operator::Add)),
711 '-' => tokens.push(ExpressionToken::Operator(Operator::Subtract)),
712 '*' => tokens.push(ExpressionToken::Operator(Operator::Multiply)),
713 '/' => tokens.push(ExpressionToken::Operator(Operator::Divide)),
714 '%' => tokens.push(ExpressionToken::Operator(Operator::Modulo)),
715 '^' => tokens.push(ExpressionToken::Operator(Operator::Power)),
716 '(' => tokens.push(ExpressionToken::LeftParen),
717 ')' => tokens.push(ExpressionToken::RightParen),
718 '0'..='9' | '.' => {
719 let mut num_str = String::new();
720 num_str.push(ch);
721 while let Some(&next_ch) = chars.peek() {
722 if next_ch.is_ascii_digit() || next_ch == '.' {
723 if let Some(consumed_ch) = chars.next() {
724 num_str.push(consumed_ch);
725 } else {
726 break; }
728 } else {
729 break;
730 }
731 }
732 let num = num_str
733 .parse::<f64>()
734 .map_err(|_| PdfError::InvalidFormat("Invalid number".to_string()))?;
735 tokens.push(ExpressionToken::Number(num));
736 }
737 'a'..='z' | 'A'..='Z' | '_' => {
738 let mut field_name = String::new();
739 field_name.push(ch);
740 while let Some(&next_ch) = chars.peek() {
741 if next_ch.is_alphanumeric() || next_ch == '_' {
742 if let Some(consumed_ch) = chars.next() {
743 field_name.push(consumed_ch);
744 } else {
745 break; }
747 } else {
748 break;
749 }
750 }
751 tokens.push(ExpressionToken::Field(field_name));
752 }
753 _ => {
754 return Err(PdfError::InvalidFormat(format!(
755 "Invalid character in expression: '{}'",
756 ch
757 )));
758 }
759 }
760 }
761
762 Self::validate_tokens(&tokens)?;
764
765 Ok(tokens)
766 }
767
768 fn validate_tokens(tokens: &[ExpressionToken]) -> Result<(), PdfError> {
770 if tokens.is_empty() {
771 return Err(PdfError::InvalidFormat("Empty expression".to_string()));
772 }
773
774 let mut paren_count = 0;
775 let mut last_was_operator = true; for token in tokens.iter() {
778 match token {
779 ExpressionToken::LeftParen => {
780 paren_count += 1;
781 last_was_operator = true; }
783 ExpressionToken::RightParen => {
784 paren_count -= 1;
785 if paren_count < 0 {
786 return Err(PdfError::InvalidFormat(
787 "Unbalanced parentheses".to_string(),
788 ));
789 }
790 last_was_operator = false;
791 }
792 ExpressionToken::Operator(_) => {
793 if last_was_operator {
794 return Err(PdfError::InvalidFormat(
795 "Invalid operator sequence".to_string(),
796 ));
797 }
798 last_was_operator = true;
799 }
800 ExpressionToken::Number(_) | ExpressionToken::Field(_) => {
801 last_was_operator = false;
802 }
803 }
804 }
805
806 if paren_count != 0 {
807 return Err(PdfError::InvalidFormat(
808 "Unbalanced parentheses".to_string(),
809 ));
810 }
811
812 if last_was_operator {
813 return Err(PdfError::InvalidFormat(
814 "Expression ends with operator".to_string(),
815 ));
816 }
817
818 Ok(())
819 }
820}
821
822#[cfg(test)]
823mod tests {
824 use super::*;
825
826 #[test]
827 fn test_field_value_conversion() {
828 assert_eq!(FieldValue::Number(42.5).to_number(), 42.5);
829 assert_eq!(FieldValue::Text("123".to_string()).to_number(), 123.0);
830 assert_eq!(FieldValue::Boolean(true).to_number(), 1.0);
831 assert_eq!(FieldValue::Empty.to_number(), 0.0);
832 }
833
834 #[test]
835 fn test_arithmetic_expression() {
836 let expr = ArithmeticExpression::from_string("2 + 3 * 4").unwrap();
837 assert_eq!(expr.tokens.len(), 5);
838 }
839
840 #[test]
841 fn test_calculation_engine() {
842 let mut engine = CalculationEngine::new();
843
844 engine.set_field_value("quantity", FieldValue::Number(5.0));
846 engine.set_field_value("price", FieldValue::Number(10.0));
847
848 let expr = ArithmeticExpression::from_string("quantity * price").unwrap();
850 engine
851 .add_calculation("total", Calculation::Arithmetic(expr))
852 .unwrap();
853
854 let total = engine.get_field_value("total").unwrap();
856 assert_eq!(total.to_number(), 50.0);
857 }
858
859 #[test]
860 fn test_sum_function() {
861 let mut engine = CalculationEngine::new();
862
863 engine.set_field_value("field1", FieldValue::Number(10.0));
864 engine.set_field_value("field2", FieldValue::Number(20.0));
865 engine.set_field_value("field3", FieldValue::Number(30.0));
866
867 let calc = Calculation::Function(CalculationFunction::Sum(vec![
868 "field1".to_string(),
869 "field2".to_string(),
870 "field3".to_string(),
871 ]));
872
873 engine.add_calculation("total", calc).unwrap();
874
875 let total = engine.get_field_value("total").unwrap();
876 assert_eq!(total.to_number(), 60.0);
877 }
878
879 #[test]
880 fn test_circular_dependency_detection() {
881 let mut engine = CalculationEngine::new();
882
883 let expr1 = ArithmeticExpression::from_string("fieldB + 1").unwrap();
885 engine
886 .add_calculation("fieldA", Calculation::Arithmetic(expr1))
887 .unwrap();
888
889 let expr2 = ArithmeticExpression::from_string("fieldA + 1").unwrap();
891 let result = engine.add_calculation("fieldB", Calculation::Arithmetic(expr2));
892
893 assert!(result.is_err());
894 }
895
896 #[test]
899 fn test_field_value_conversions() {
900 let num_val = FieldValue::Number(42.5);
902 assert_eq!(num_val.to_number(), 42.5);
903 assert_eq!(num_val.to_string(), "42.50");
904
905 let int_val = FieldValue::Number(100.0);
906 assert_eq!(int_val.to_string(), "100");
907
908 let text_val = FieldValue::Text("123.45".to_string());
910 assert_eq!(text_val.to_number(), 123.45);
911 assert_eq!(text_val.to_string(), "123.45");
912
913 let non_numeric_text = FieldValue::Text("hello".to_string());
914 assert_eq!(non_numeric_text.to_number(), 0.0);
915
916 let true_val = FieldValue::Boolean(true);
918 assert_eq!(true_val.to_number(), 1.0);
919 assert_eq!(true_val.to_string(), "true");
920
921 let false_val = FieldValue::Boolean(false);
922 assert_eq!(false_val.to_number(), 0.0);
923 assert_eq!(false_val.to_string(), "false");
924
925 let empty_val = FieldValue::Empty;
927 assert_eq!(empty_val.to_number(), 0.0);
928 assert_eq!(empty_val.to_string(), "");
929 }
930
931 #[test]
932 fn test_complex_arithmetic_expressions() {
933 let mut engine = CalculationEngine::new();
934
935 engine.set_field_value("a", FieldValue::Number(10.0));
937 engine.set_field_value("b", FieldValue::Number(5.0));
938 engine.set_field_value("c", FieldValue::Number(2.0));
939
940 let expr = ArithmeticExpression::from_string("(a + b) * c").unwrap();
942 engine
943 .add_calculation("result1", Calculation::Arithmetic(expr))
944 .unwrap();
945
946 let result = engine.get_field_value("result1").unwrap();
947 assert_eq!(result.to_number(), 30.0); let expr2 = ArithmeticExpression::from_string("a + b - c * 2 / 4").unwrap();
951 engine
952 .add_calculation("result2", Calculation::Arithmetic(expr2))
953 .unwrap();
954
955 let result2 = engine.get_field_value("result2").unwrap();
956 assert_eq!(result2.to_number(), 14.0); }
958
959 #[test]
960 fn test_calculation_functions() {
961 let mut engine = CalculationEngine::new();
962
963 engine.set_field_value("val1", FieldValue::Number(100.0));
965 engine.set_field_value("val2", FieldValue::Number(50.0));
966 engine.set_field_value("val3", FieldValue::Number(25.0));
967 engine.set_field_value("val4", FieldValue::Number(75.0));
968
969 let avg_calc = Calculation::Function(CalculationFunction::Average(vec![
971 "val1".to_string(),
972 "val2".to_string(),
973 "val3".to_string(),
974 "val4".to_string(),
975 ]));
976 engine.add_calculation("average", avg_calc).unwrap();
977
978 let avg = engine.get_field_value("average").unwrap();
979 assert_eq!(avg.to_number(), 62.5); let min_calc = Calculation::Function(CalculationFunction::Min(vec![
983 "val1".to_string(),
984 "val2".to_string(),
985 "val3".to_string(),
986 "val4".to_string(),
987 ]));
988 engine.add_calculation("minimum", min_calc).unwrap();
989
990 let min = engine.get_field_value("minimum").unwrap();
991 assert_eq!(min.to_number(), 25.0);
992
993 let max_calc = Calculation::Function(CalculationFunction::Max(vec![
995 "val1".to_string(),
996 "val2".to_string(),
997 "val3".to_string(),
998 "val4".to_string(),
999 ]));
1000 engine.add_calculation("maximum", max_calc).unwrap();
1001
1002 let max = engine.get_field_value("maximum").unwrap();
1003 assert_eq!(max.to_number(), 100.0);
1004 }
1005
1006 #[test]
1007 fn test_calculation_order_dependencies() {
1008 let mut engine = CalculationEngine::new();
1009
1010 engine.set_field_value("base", FieldValue::Number(10.0));
1012
1013 let expr1 = ArithmeticExpression::from_string("base * 2").unwrap();
1015 engine
1016 .add_calculation("level1", Calculation::Arithmetic(expr1))
1017 .unwrap();
1018
1019 let expr2 = ArithmeticExpression::from_string("level1 + 5").unwrap();
1021 engine
1022 .add_calculation("level2", Calculation::Arithmetic(expr2))
1023 .unwrap();
1024
1025 let expr3 = ArithmeticExpression::from_string("level2 / 5").unwrap();
1027 engine
1028 .add_calculation("level3", Calculation::Arithmetic(expr3))
1029 .unwrap();
1030
1031 assert_eq!(engine.calculation_order.len(), 3);
1033 assert_eq!(engine.calculation_order[0], "level1");
1034 assert_eq!(engine.calculation_order[1], "level2");
1035 assert_eq!(engine.calculation_order[2], "level3");
1036
1037 assert_eq!(engine.get_field_value("level1").unwrap().to_number(), 20.0);
1039 assert_eq!(engine.get_field_value("level2").unwrap().to_number(), 25.0);
1040 assert_eq!(engine.get_field_value("level3").unwrap().to_number(), 5.0);
1041 }
1042
1043 #[test]
1044 fn test_field_update_recalculation() {
1045 let mut engine = CalculationEngine::new();
1046
1047 engine.set_field_value("price", FieldValue::Number(10.0));
1049 engine.set_field_value("quantity", FieldValue::Number(5.0));
1050
1051 let expr = ArithmeticExpression::from_string("price * quantity").unwrap();
1053 engine
1054 .add_calculation("total", Calculation::Arithmetic(expr))
1055 .unwrap();
1056
1057 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 50.0);
1059
1060 engine.set_field_value("price", FieldValue::Number(15.0));
1062 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 75.0);
1063
1064 engine.set_field_value("quantity", FieldValue::Number(10.0));
1066 assert_eq!(engine.get_field_value("total").unwrap().to_number(), 150.0);
1067 }
1068
1069 #[test]
1070 fn test_edge_cases_division_by_zero() {
1071 let mut engine = CalculationEngine::new();
1072
1073 engine.set_field_value("numerator", FieldValue::Number(100.0));
1074 engine.set_field_value("denominator", FieldValue::Number(0.0));
1075
1076 let expr = ArithmeticExpression::from_string("numerator / denominator").unwrap();
1077 engine
1078 .add_calculation("result", Calculation::Arithmetic(expr))
1079 .unwrap();
1080
1081 let result = engine.get_field_value("result").unwrap();
1082 assert_eq!(result.to_number(), 0.0);
1084 }
1085
1086 #[test]
1087 fn test_mixed_value_types() {
1088 let mut engine = CalculationEngine::new();
1089
1090 engine.set_field_value("num", FieldValue::Number(10.0));
1092 engine.set_field_value("text_num", FieldValue::Text("20".to_string()));
1093 engine.set_field_value("bool_val", FieldValue::Boolean(true));
1094 engine.set_field_value("empty", FieldValue::Empty);
1095
1096 let calc = Calculation::Function(CalculationFunction::Sum(vec![
1098 "num".to_string(),
1099 "text_num".to_string(),
1100 "bool_val".to_string(),
1101 "empty".to_string(),
1102 ]));
1103 engine.add_calculation("total", calc).unwrap();
1104
1105 let total = engine.get_field_value("total").unwrap();
1106 assert_eq!(total.to_number(), 31.0); }
1108
1109 #[test]
1110 fn test_constant_calculations() {
1111 let mut engine = CalculationEngine::new();
1112
1113 engine
1115 .add_calculation("pi", Calculation::Constant(FieldValue::Number(3.14159)))
1116 .unwrap();
1117 engine
1118 .add_calculation(
1119 "label",
1120 Calculation::Constant(FieldValue::Text("Total:".to_string())),
1121 )
1122 .unwrap();
1123 engine
1124 .add_calculation("enabled", Calculation::Constant(FieldValue::Boolean(true)))
1125 .unwrap();
1126
1127 assert_eq!(engine.get_field_value("pi").unwrap().to_number(), 3.14159);
1128 assert_eq!(
1129 engine.get_field_value("label").unwrap().to_string(),
1130 "Total:"
1131 );
1132 assert_eq!(
1133 *engine.get_field_value("enabled").unwrap(),
1134 FieldValue::Boolean(true)
1135 );
1136 }
1137
1138 #[test]
1139 fn test_expression_parsing_errors() {
1140 assert!(ArithmeticExpression::from_string("").is_err());
1142 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()); }
1147
1148 #[test]
1149 fn test_multiple_dependencies() {
1150 let mut engine = CalculationEngine::new();
1151
1152 engine.set_field_value("a", FieldValue::Number(5.0));
1154 engine.set_field_value("b", FieldValue::Number(10.0));
1155
1156 let expr1 = ArithmeticExpression::from_string("a + b").unwrap();
1158 engine
1159 .add_calculation("c", Calculation::Arithmetic(expr1))
1160 .unwrap();
1161
1162 let expr2 = ArithmeticExpression::from_string("a * 2").unwrap();
1164 engine
1165 .add_calculation("d", Calculation::Arithmetic(expr2))
1166 .unwrap();
1167
1168 let expr3 = ArithmeticExpression::from_string("c + d").unwrap();
1170 engine
1171 .add_calculation("e", Calculation::Arithmetic(expr3))
1172 .unwrap();
1173
1174 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 15.0);
1175 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 10.0);
1176 assert_eq!(engine.get_field_value("e").unwrap().to_number(), 25.0);
1177
1178 engine.set_field_value("a", FieldValue::Number(10.0));
1180 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 20.0);
1181 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 20.0);
1182 assert_eq!(engine.get_field_value("e").unwrap().to_number(), 40.0);
1183 }
1184
1185 #[test]
1186 fn test_calculation_removal() {
1187 let mut engine = CalculationEngine::new();
1188
1189 engine.set_field_value("x", FieldValue::Number(10.0));
1190
1191 let expr = ArithmeticExpression::from_string("x * 2").unwrap();
1192 engine
1193 .add_calculation("y", Calculation::Arithmetic(expr))
1194 .unwrap();
1195
1196 assert_eq!(engine.get_field_value("y").unwrap().to_number(), 20.0);
1197
1198 engine.remove_calculation("y");
1200
1201 assert!(engine.get_field_value("y").is_none());
1203
1204 engine.set_field_value("y", FieldValue::Number(100.0));
1206 assert_eq!(engine.get_field_value("y").unwrap().to_number(), 100.0);
1207 }
1208
1209 #[test]
1210 fn test_large_calculation_chain() {
1211 let mut engine = CalculationEngine::new();
1212
1213 engine.set_field_value("f0", FieldValue::Number(1.0));
1215
1216 for i in 1..20 {
1217 let prev = format!("f{}", i - 1);
1218 let curr = format!("f{}", i);
1219 let expr = ArithmeticExpression::from_string(&format!("{} + 1", prev)).unwrap();
1220 engine
1221 .add_calculation(&curr, Calculation::Arithmetic(expr))
1222 .unwrap();
1223 }
1224
1225 assert_eq!(engine.get_field_value("f19").unwrap().to_number(), 20.0);
1227
1228 engine.set_field_value("f0", FieldValue::Number(10.0));
1230 assert_eq!(engine.get_field_value("f19").unwrap().to_number(), 29.0);
1231 }
1232
1233 #[test]
1234 fn test_operator_precedence() {
1235 let mut engine = CalculationEngine::new();
1236
1237 engine.set_field_value("a", FieldValue::Number(2.0));
1238 engine.set_field_value("b", FieldValue::Number(3.0));
1239 engine.set_field_value("c", FieldValue::Number(4.0));
1240
1241 let expr = ArithmeticExpression::from_string("a + b * c").unwrap();
1243 engine
1244 .add_calculation("result", Calculation::Arithmetic(expr))
1245 .unwrap();
1246
1247 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 14.0); let expr2 = ArithmeticExpression::from_string("(a + b) * c").unwrap();
1251 engine
1252 .add_calculation("result2", Calculation::Arithmetic(expr2))
1253 .unwrap();
1254
1255 assert_eq!(engine.get_field_value("result2").unwrap().to_number(), 20.0);
1256 }
1258
1259 #[test]
1260 fn test_negative_numbers() {
1261 let mut engine = CalculationEngine::new();
1262
1263 engine.set_field_value("positive", FieldValue::Number(10.0));
1264 engine.set_field_value("negative", FieldValue::Number(-5.0));
1265
1266 let expr = ArithmeticExpression::from_string("positive + negative").unwrap();
1268 engine
1269 .add_calculation("result", Calculation::Arithmetic(expr))
1270 .unwrap();
1271
1272 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 5.0);
1273
1274 let expr2 = ArithmeticExpression::from_string("negative * negative").unwrap();
1276 engine
1277 .add_calculation("result2", Calculation::Arithmetic(expr2))
1278 .unwrap();
1279
1280 assert_eq!(engine.get_field_value("result2").unwrap().to_number(), 25.0);
1281 }
1282
1283 #[test]
1284 fn test_floating_point_precision() {
1285 let mut engine = CalculationEngine::new();
1286
1287 engine.set_field_value("a", FieldValue::Number(0.1));
1288 engine.set_field_value("b", FieldValue::Number(0.2));
1289
1290 let expr = ArithmeticExpression::from_string("a + b").unwrap();
1291 engine
1292 .add_calculation("result", Calculation::Arithmetic(expr))
1293 .unwrap();
1294
1295 let result = engine.get_field_value("result").unwrap().to_number();
1296 assert!((result - 0.3).abs() < 0.0001);
1298 }
1299
1300 #[test]
1301 fn test_empty_field_references() {
1302 let mut engine = CalculationEngine::new();
1303
1304 let expr = ArithmeticExpression::from_string("missing1 + missing2").unwrap();
1306 engine
1307 .add_calculation("result", Calculation::Arithmetic(expr))
1308 .unwrap();
1309
1310 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 0.0);
1312
1313 engine.set_field_value("missing1", FieldValue::Number(10.0));
1315 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 10.0);
1316 }
1317
1318 #[test]
1319 fn test_calculation_with_product_function() {
1320 let mut engine = CalculationEngine::new();
1321
1322 engine.set_field_value("f1", FieldValue::Number(2.0));
1323 engine.set_field_value("f2", FieldValue::Number(3.0));
1324 engine.set_field_value("f3", FieldValue::Number(4.0));
1325 engine.set_field_value("f4", FieldValue::Number(5.0));
1326
1327 let calc = Calculation::Function(CalculationFunction::Product(vec![
1328 "f1".to_string(),
1329 "f2".to_string(),
1330 "f3".to_string(),
1331 "f4".to_string(),
1332 ]));
1333 engine.add_calculation("product", calc).unwrap();
1334
1335 let product = engine.get_field_value("product").unwrap();
1336 assert_eq!(product.to_number(), 120.0); }
1338
1339 #[test]
1340 fn test_complex_dependency_graph() {
1341 let mut engine = CalculationEngine::new();
1342
1343 engine.set_field_value("a", FieldValue::Number(10.0));
1351
1352 let expr_b = ArithmeticExpression::from_string("a * 2").unwrap();
1353 engine
1354 .add_calculation("b", Calculation::Arithmetic(expr_b))
1355 .unwrap();
1356
1357 let expr_c = ArithmeticExpression::from_string("a + 5").unwrap();
1358 engine
1359 .add_calculation("c", Calculation::Arithmetic(expr_c))
1360 .unwrap();
1361
1362 let expr_d = ArithmeticExpression::from_string("b + c").unwrap();
1363 engine
1364 .add_calculation("d", Calculation::Arithmetic(expr_d))
1365 .unwrap();
1366
1367 assert_eq!(engine.get_field_value("b").unwrap().to_number(), 20.0);
1368 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 15.0);
1369 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 35.0);
1370
1371 engine.set_field_value("a", FieldValue::Number(20.0));
1373 assert_eq!(engine.get_field_value("b").unwrap().to_number(), 40.0);
1374 assert_eq!(engine.get_field_value("c").unwrap().to_number(), 25.0);
1375 assert_eq!(engine.get_field_value("d").unwrap().to_number(), 65.0);
1376 }
1377
1378 #[test]
1379 fn test_field_value_conversions_extended() {
1380 assert_eq!(FieldValue::Number(42.5).to_number(), 42.5);
1382 assert_eq!(FieldValue::Text("123.45".to_string()).to_number(), 123.45);
1383 assert_eq!(FieldValue::Text("invalid".to_string()).to_number(), 0.0);
1384 assert_eq!(FieldValue::Boolean(true).to_number(), 1.0);
1385 assert_eq!(FieldValue::Boolean(false).to_number(), 0.0);
1386 assert_eq!(FieldValue::Empty.to_number(), 0.0);
1387
1388 assert_eq!(FieldValue::Number(42.0).to_string(), "42");
1390 assert_eq!(FieldValue::Number(42.5).to_string(), "42.50");
1391 assert_eq!(FieldValue::Text("hello".to_string()).to_string(), "hello");
1392 assert_eq!(FieldValue::Boolean(true).to_string(), "true");
1393 assert_eq!(FieldValue::Boolean(false).to_string(), "false");
1394 assert_eq!(FieldValue::Empty.to_string(), "");
1395 }
1396
1397 #[test]
1398 fn test_min_max_functions() {
1399 let mut engine = CalculationEngine::new();
1400
1401 engine.set_field_value("a", FieldValue::Number(10.0));
1402 engine.set_field_value("b", FieldValue::Number(5.0));
1403 engine.set_field_value("c", FieldValue::Number(15.0));
1404 engine.set_field_value("d", FieldValue::Number(8.0));
1405
1406 let min_calc = Calculation::Function(CalculationFunction::Min(vec![
1408 "a".to_string(),
1409 "b".to_string(),
1410 "c".to_string(),
1411 "d".to_string(),
1412 ]));
1413 engine.add_calculation("min_val", min_calc).unwrap();
1414 assert_eq!(engine.get_field_value("min_val").unwrap().to_number(), 5.0);
1415
1416 let max_calc = Calculation::Function(CalculationFunction::Max(vec![
1418 "a".to_string(),
1419 "b".to_string(),
1420 "c".to_string(),
1421 "d".to_string(),
1422 ]));
1423 engine.add_calculation("max_val", max_calc).unwrap();
1424 assert_eq!(engine.get_field_value("max_val").unwrap().to_number(), 15.0);
1425 }
1426
1427 #[test]
1428 fn test_count_function() {
1429 let mut engine = CalculationEngine::new();
1430
1431 engine.set_field_value("f1", FieldValue::Number(10.0));
1432 engine.set_field_value("f2", FieldValue::Empty);
1433 engine.set_field_value("f3", FieldValue::Text("text".to_string()));
1434 engine.set_field_value("f4", FieldValue::Number(0.0));
1435
1436 let count_calc = Calculation::Function(CalculationFunction::Count(vec![
1437 "f1".to_string(),
1438 "f2".to_string(),
1439 "f3".to_string(),
1440 "f4".to_string(),
1441 ]));
1442 engine.add_calculation("count", count_calc).unwrap();
1443
1444 assert_eq!(engine.get_field_value("count").unwrap().to_number(), 3.0);
1446 }
1447
1448 #[test]
1449 fn test_if_function() {
1450 let mut engine = CalculationEngine::new();
1451
1452 engine.set_field_value("condition", FieldValue::Boolean(true));
1454
1455 let if_calc = Calculation::Function(CalculationFunction::If {
1456 condition_field: "condition".to_string(),
1457 true_value: Box::new(Calculation::Constant(FieldValue::Number(100.0))),
1458 false_value: Box::new(Calculation::Constant(FieldValue::Number(200.0))),
1459 });
1460 engine.add_calculation("result", if_calc).unwrap();
1461 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 100.0);
1462
1463 engine.set_field_value("condition", FieldValue::Boolean(false));
1465 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 200.0);
1466
1467 engine.set_field_value("condition", FieldValue::Number(5.0));
1469 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 100.0);
1470
1471 engine.set_field_value("condition", FieldValue::Number(0.0));
1472 assert_eq!(engine.get_field_value("result").unwrap().to_number(), 200.0);
1473 }
1474
1475 #[test]
1476 fn test_modulo_and_power_operations() {
1477 let mut engine = CalculationEngine::new();
1478
1479 engine.set_field_value("a", FieldValue::Number(10.0));
1480 engine.set_field_value("b", FieldValue::Number(3.0));
1481
1482 let mod_expr = ArithmeticExpression::from_string("a % b").unwrap();
1484 engine
1485 .add_calculation("mod_result", Calculation::Arithmetic(mod_expr))
1486 .unwrap();
1487 assert_eq!(
1488 engine.get_field_value("mod_result").unwrap().to_number(),
1489 1.0
1490 );
1491
1492 let pow_expr = ArithmeticExpression::from_string("b ^ 3").unwrap();
1494 engine
1495 .add_calculation("pow_result", Calculation::Arithmetic(pow_expr))
1496 .unwrap();
1497 assert_eq!(
1498 engine.get_field_value("pow_result").unwrap().to_number(),
1499 27.0
1500 );
1501 }
1502
1503 #[test]
1504 fn test_calculation_summary() {
1505 let mut engine = CalculationEngine::new();
1506
1507 engine.set_field_value("a", FieldValue::Number(10.0));
1509 engine.set_field_value("b", FieldValue::Number(20.0));
1510
1511 let expr = ArithmeticExpression::from_string("a + b").unwrap();
1512 engine
1513 .add_calculation("sum", Calculation::Arithmetic(expr))
1514 .unwrap();
1515
1516 let summary = engine.get_summary();
1517 assert_eq!(summary.total_fields, 3); assert_eq!(summary.calculated_fields, 1); assert_eq!(summary.calculation_order.len(), 1);
1520 assert_eq!(summary.calculation_order[0], "sum");
1521
1522 let summary_str = format!("{}", summary);
1524 assert!(summary_str.contains("Total fields: 3"));
1525 assert!(summary_str.contains("Calculated fields: 1"));
1526 }
1527
1528 #[test]
1529 fn test_recalculate_all() {
1530 let mut engine = CalculationEngine::new();
1531
1532 engine.set_field_value("x", FieldValue::Number(5.0));
1533 engine.set_field_value("y", FieldValue::Number(10.0));
1534
1535 let expr1 = ArithmeticExpression::from_string("x + y").unwrap();
1536 engine
1537 .add_calculation("sum", Calculation::Arithmetic(expr1))
1538 .unwrap();
1539
1540 let expr2 = ArithmeticExpression::from_string("sum * 2").unwrap();
1541 engine
1542 .add_calculation("double", Calculation::Arithmetic(expr2))
1543 .unwrap();
1544
1545 assert_eq!(engine.get_field_value("sum").unwrap().to_number(), 15.0);
1547 assert_eq!(engine.get_field_value("double").unwrap().to_number(), 30.0);
1548
1549 engine.recalculate_all().unwrap();
1551
1552 assert_eq!(engine.get_field_value("sum").unwrap().to_number(), 15.0);
1554 assert_eq!(engine.get_field_value("double").unwrap().to_number(), 30.0);
1555 }
1556
1557 #[test]
1558 fn test_javascript_calculation() {
1559 let mut engine = CalculationEngine::new();
1560
1561 let js_calc = Calculation::JavaScript("var sum = a + b;".to_string());
1563 engine.add_calculation("js_result", js_calc).unwrap();
1564
1565 assert_eq!(
1566 *engine.get_field_value("js_result").unwrap(),
1567 FieldValue::Empty
1568 );
1569 }
1570
1571 #[test]
1572 #[ignore] fn test_division_by_zero() {
1574 let mut engine = CalculationEngine::new();
1576
1577 engine.set_field_value("numerator", FieldValue::Number(100.0));
1578 engine.set_field_value("denominator", FieldValue::Number(0.0));
1579
1580 let expr = ArithmeticExpression {
1582 tokens: vec![
1583 ExpressionToken::Field("numerator".to_string()),
1584 ExpressionToken::Operator(Operator::Divide),
1585 ExpressionToken::Field("denominator".to_string()),
1586 ],
1587 };
1588
1589 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1590
1591 let result = engine.calculate_field("result");
1593 match result {
1595 Ok(_) => {
1596 let value = engine.get_field_value("result");
1598 assert!(
1599 matches!(value, Some(FieldValue::Number(n)) if n.is_infinite() || n.is_nan()),
1600 "Division by zero should produce infinity or NaN, got: {:?}",
1601 value
1602 );
1603 }
1604 Err(_) => {
1605 }
1608 }
1609 }
1610
1611 #[test]
1612 fn test_circular_reference_detection() {
1613 let mut engine = CalculationEngine::new();
1615
1616 let _ = engine.add_calculation(
1618 "field_a",
1619 Calculation::Arithmetic(ArithmeticExpression {
1620 tokens: vec![
1621 ExpressionToken::Field("field_b".to_string()),
1622 ExpressionToken::Number(1.0),
1623 ExpressionToken::Operator(Operator::Add),
1624 ],
1625 }),
1626 );
1627
1628 let _ = engine.add_calculation(
1629 "field_b",
1630 Calculation::Arithmetic(ArithmeticExpression {
1631 tokens: vec![
1632 ExpressionToken::Field("field_c".to_string()),
1633 ExpressionToken::Number(2.0),
1634 ExpressionToken::Operator(Operator::Add),
1635 ],
1636 }),
1637 );
1638
1639 let _ = engine.add_calculation(
1640 "field_c",
1641 Calculation::Arithmetic(ArithmeticExpression {
1642 tokens: vec![
1643 ExpressionToken::Field("field_a".to_string()),
1644 ExpressionToken::Number(3.0),
1645 ExpressionToken::Operator(Operator::Add),
1646 ],
1647 }),
1648 );
1649
1650 let result = engine.update_calculation_order();
1652 assert!(result.is_ok() || result.is_err());
1654 }
1655
1656 #[test]
1657 fn test_non_numeric_calculation() {
1658 let mut engine = CalculationEngine::new();
1660
1661 engine.set_field_value("text_field", FieldValue::Text("not a number".to_string()));
1662 engine.set_field_value("numeric_field", FieldValue::Number(42.0));
1663
1664 let expr = ArithmeticExpression {
1666 tokens: vec![
1667 ExpressionToken::Field("text_field".to_string()),
1668 ExpressionToken::Field("numeric_field".to_string()),
1669 ExpressionToken::Operator(Operator::Add),
1670 ],
1671 };
1672
1673 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1674
1675 let _ = engine.calculate_field("result");
1677 if let Some(FieldValue::Number(n)) = engine.get_field_value("result") {
1678 assert_eq!(*n, 42.0); }
1680 }
1681
1682 #[test]
1683 fn test_empty_field_calculation() {
1684 let mut engine = CalculationEngine::new();
1686
1687 let expr = ArithmeticExpression {
1689 tokens: vec![
1690 ExpressionToken::Field("undefined1".to_string()),
1691 ExpressionToken::Field("undefined2".to_string()),
1692 ExpressionToken::Operator(Operator::Multiply),
1693 ],
1694 };
1695
1696 let _ = engine.add_calculation("result", Calculation::Arithmetic(expr));
1697
1698 let _ = engine.calculate_field("result");
1700 if let Some(FieldValue::Number(n)) = engine.get_field_value("result") {
1701 assert_eq!(*n, 0.0); }
1703 }
1704
1705 #[test]
1706 fn test_max_function_with_empty_fields() {
1707 let mut engine = CalculationEngine::new();
1709
1710 engine.set_field_value("val1", FieldValue::Number(10.0));
1711 engine.set_field_value("val2", FieldValue::Empty);
1712 engine.set_field_value("val3", FieldValue::Number(25.0));
1713 engine.set_field_value("val4", FieldValue::Text("invalid".to_string()));
1714
1715 let _ = engine.add_calculation(
1716 "max_result",
1717 Calculation::Function(CalculationFunction::Max(vec![
1718 "val1".to_string(),
1719 "val2".to_string(),
1720 "val3".to_string(),
1721 "val4".to_string(),
1722 ])),
1723 );
1724
1725 let _ = engine.calculate_field("max_result");
1726 if let Some(FieldValue::Number(n)) = engine.get_field_value("max_result") {
1727 assert_eq!(*n, 25.0); }
1729 }
1730
1731 #[test]
1734 fn test_expression_parsing_comprehensive_edge_cases() {
1735 let test_cases = vec![
1737 ("((a + b)", "Mismatched left parentheses"),
1738 ("a + b))", "Mismatched right parentheses"),
1739 ("a ++ b", "Double operators"),
1740 ("+ a", "Leading operator"),
1741 ("a +", "Trailing operator"),
1742 ("5..3", "Double decimal point"),
1743 ("3.14.159", "Multiple decimal points"),
1744 ("a + * b", "Consecutive operators"),
1745 ("(a + b) * ", "Operator without operand"),
1746 ("@#$%", "Invalid characters"),
1747 ("", "Empty expression"),
1748 (" \t\n ", "Whitespace only"),
1749 ];
1750
1751 for (expr, description) in test_cases {
1752 let result = ArithmeticExpression::from_string(expr);
1753 assert!(
1754 result.is_err(),
1755 "Expression '{}' should fail parsing: {}",
1756 expr,
1757 description
1758 );
1759 }
1760
1761 let valid_cases = vec![
1763 ("()", 0.0), ("a b", 0.0), ("123abc", 123.0), ];
1767
1768 let mut engine = CalculationEngine::new();
1769 engine.set_field_value("a", FieldValue::Number(5.0));
1770 engine.set_field_value("b", FieldValue::Number(3.0));
1771
1772 for (i, (expr, _expected)) in valid_cases.iter().enumerate() {
1773 let result = ArithmeticExpression::from_string(expr);
1774 match result {
1776 Ok(parsed_expr) => {
1777 let calc_name = format!("edge_valid_{}", i);
1779 let add_result =
1780 engine.add_calculation(&calc_name, Calculation::Arithmetic(parsed_expr));
1781 let _ = add_result;
1783 }
1784 Err(_) => {
1785 }
1787 }
1788 }
1789 }
1790
1791 #[test]
1792 fn test_arithmetic_overflow_edge_cases() {
1793 let mut engine = CalculationEngine::new();
1794
1795 engine.set_field_value("max_val", FieldValue::Number(f64::MAX));
1797 engine.set_field_value("min_val", FieldValue::Number(f64::MIN));
1798 engine.set_field_value("infinity", FieldValue::Number(f64::INFINITY));
1799 engine.set_field_value("neg_infinity", FieldValue::Number(f64::NEG_INFINITY));
1800 engine.set_field_value("zero", FieldValue::Number(0.0));
1801 engine.set_field_value("small", FieldValue::Number(f64::MIN_POSITIVE));
1802
1803 let overflow_expr = ArithmeticExpression::from_string("max_val * 2").unwrap();
1805 engine
1806 .add_calculation("overflow_result", Calculation::Arithmetic(overflow_expr))
1807 .unwrap();
1808
1809 let overflow_result = engine.get_field_value("overflow_result").unwrap();
1810 assert!(overflow_result.to_number().is_infinite());
1811
1812 let inf_expr = ArithmeticExpression::from_string("infinity + 100").unwrap();
1814 engine
1815 .add_calculation("inf_result", Calculation::Arithmetic(inf_expr))
1816 .unwrap();
1817
1818 let inf_result = engine.get_field_value("inf_result").unwrap();
1819 assert_eq!(inf_result.to_number(), f64::INFINITY);
1820
1821 let nan_expr = ArithmeticExpression::from_string("infinity - infinity").unwrap();
1823 engine
1824 .add_calculation("nan_result", Calculation::Arithmetic(nan_expr))
1825 .unwrap();
1826
1827 let nan_result = engine.get_field_value("nan_result").unwrap();
1828 assert!(nan_result.to_number().is_nan());
1829 }
1830
1831 #[test]
1832 fn test_complex_financial_calculations() {
1833 let mut engine = CalculationEngine::new();
1834
1835 engine.set_field_value("unit_price", FieldValue::Number(19.99));
1837 engine.set_field_value("quantity", FieldValue::Number(150.0));
1838 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));
1841 engine.set_field_value("shipping_per_item", FieldValue::Number(1.50));
1842
1843 let subtotal_expr = ArithmeticExpression::from_string("unit_price * quantity").unwrap();
1845 engine
1846 .add_calculation("subtotal", Calculation::Arithmetic(subtotal_expr))
1847 .unwrap();
1848
1849 let discount_expr = ArithmeticExpression::from_string("subtotal * discount_rate").unwrap();
1851 engine
1852 .add_calculation("discount_amount", Calculation::Arithmetic(discount_expr))
1853 .unwrap();
1854
1855 let after_discount_expr =
1857 ArithmeticExpression::from_string("subtotal - discount_amount").unwrap();
1858 engine
1859 .add_calculation(
1860 "after_discount",
1861 Calculation::Arithmetic(after_discount_expr),
1862 )
1863 .unwrap();
1864
1865 let shipping_expr =
1867 ArithmeticExpression::from_string("shipping_base + quantity * shipping_per_item")
1868 .unwrap();
1869 engine
1870 .add_calculation("shipping", Calculation::Arithmetic(shipping_expr))
1871 .unwrap();
1872
1873 let pretax_expr = ArithmeticExpression::from_string("after_discount + shipping").unwrap();
1875 engine
1876 .add_calculation("pretax_total", Calculation::Arithmetic(pretax_expr))
1877 .unwrap();
1878
1879 let tax_expr = ArithmeticExpression::from_string("pretax_total * tax_rate").unwrap();
1881 engine
1882 .add_calculation("tax_amount", Calculation::Arithmetic(tax_expr))
1883 .unwrap();
1884
1885 let total_expr = ArithmeticExpression::from_string("pretax_total + tax_amount").unwrap();
1887 engine
1888 .add_calculation("final_total", Calculation::Arithmetic(total_expr))
1889 .unwrap();
1890
1891 let subtotal = engine.get_field_value("subtotal").unwrap().to_number();
1893 assert!(
1894 (subtotal - 2998.5).abs() < 0.01,
1895 "Subtotal calculation incorrect: expected 2998.5, got {}",
1896 subtotal
1897 );
1898 let discount_amount = engine
1899 .get_field_value("discount_amount")
1900 .unwrap()
1901 .to_number();
1902 assert!(
1903 (discount_amount - 449.775).abs() < 0.01,
1904 "Discount amount calculation incorrect: expected 449.775, got {}",
1905 discount_amount
1906 );
1907
1908 let after_discount = engine
1909 .get_field_value("after_discount")
1910 .unwrap()
1911 .to_number();
1912 assert!(
1913 (after_discount - 2548.725).abs() < 0.01,
1914 "After discount calculation incorrect: expected 2548.725, got {}",
1915 after_discount
1916 );
1917
1918 assert_eq!(
1919 engine.get_field_value("shipping").unwrap().to_number(),
1920 250.0
1921 ); let pretax_total = engine.get_field_value("pretax_total").unwrap().to_number();
1924 assert!(
1925 (pretax_total - 2798.725).abs() < 0.01,
1926 "Pretax total calculation incorrect: expected 2798.725, got {}",
1927 pretax_total
1928 );
1929
1930 let final_total = engine.get_field_value("final_total").unwrap().to_number();
1932 let tax_amount = engine.get_field_value("tax_amount").unwrap().to_number();
1933 let pretax_total = engine.get_field_value("pretax_total").unwrap().to_number();
1934 let expected_tax = pretax_total * 0.08;
1935 let expected_final = pretax_total + expected_tax;
1936
1937 assert!(
1938 (tax_amount - expected_tax).abs() < 0.01,
1939 "Tax amount calculation incorrect: expected {}, got {}",
1940 expected_tax,
1941 tax_amount
1942 );
1943 assert!(
1944 (final_total - expected_final).abs() < 0.01,
1945 "Final total calculation incorrect: expected {}, got {}",
1946 expected_final,
1947 final_total
1948 );
1949 }
1950
1951 #[test]
1952 fn test_deeply_nested_expressions() {
1953 let mut engine = CalculationEngine::new();
1954
1955 engine.set_field_value("a", FieldValue::Number(2.0));
1956 engine.set_field_value("b", FieldValue::Number(3.0));
1957 engine.set_field_value("c", FieldValue::Number(4.0));
1958 engine.set_field_value("d", FieldValue::Number(5.0));
1959
1960 let deep_expr = ArithmeticExpression::from_string(
1962 "(((a + b) * (c - d)) / ((a * b) + (c / d))) ^ 2 + ((a - b) * (c + d))",
1963 )
1964 .unwrap();
1965 engine
1966 .add_calculation("deep_result", Calculation::Arithmetic(deep_expr))
1967 .unwrap();
1968
1969 let result = engine.get_field_value("deep_result").unwrap();
1971 let result_num = result.to_number();
1972 assert!(
1973 result_num.is_finite() || result_num.is_nan(),
1974 "Deep expression should produce a finite number or NaN, got {}",
1975 result_num
1976 );
1977
1978 let chain_expr = ArithmeticExpression::from_string(
1980 "a + b - c + d * a / b + c - d ^ 2 + a * b * c / d - a + b + c - d",
1981 )
1982 .unwrap();
1983 engine
1984 .add_calculation("chain_result", Calculation::Arithmetic(chain_expr))
1985 .unwrap();
1986
1987 let chain_result = engine.get_field_value("chain_result").unwrap();
1988 assert!(chain_result.to_number().is_finite());
1989 }
1990
1991 #[test]
1992 fn test_comprehensive_function_combinations() {
1993 let mut engine = CalculationEngine::new();
1994
1995 let field_names: Vec<String> = (1..=10).map(|i| format!("val{}", i)).collect();
1997 let values = vec![10.0, 20.0, 5.0, 15.0, 25.0, 8.0, 30.0, 12.0, 18.0, 22.0];
1998
1999 for (name, value) in field_names.iter().zip(values.iter()) {
2000 engine.set_field_value(name, FieldValue::Number(*value));
2001 }
2002
2003 let sum_calc = Calculation::Function(CalculationFunction::Sum(field_names.clone()));
2005 engine.add_calculation("total_sum", sum_calc).unwrap();
2006 assert_eq!(
2007 engine.get_field_value("total_sum").unwrap().to_number(),
2008 165.0
2009 );
2010
2011 engine.set_field_value("condition1", FieldValue::Boolean(true));
2013 engine.set_field_value("condition2", FieldValue::Boolean(false));
2014
2015 let nested_if = Calculation::Function(CalculationFunction::If {
2016 condition_field: "condition1".to_string(),
2017 true_value: Box::new(Calculation::Function(CalculationFunction::If {
2018 condition_field: "condition2".to_string(),
2019 true_value: Box::new(Calculation::Constant(FieldValue::Number(100.0))),
2020 false_value: Box::new(Calculation::Constant(FieldValue::Number(200.0))),
2021 })),
2022 false_value: Box::new(Calculation::Constant(FieldValue::Number(300.0))),
2023 });
2024 engine
2025 .add_calculation("nested_if_result", nested_if)
2026 .unwrap();
2027 assert_eq!(
2028 engine
2029 .get_field_value("nested_if_result")
2030 .unwrap()
2031 .to_number(),
2032 200.0
2033 );
2034
2035 let product_calc =
2037 Calculation::Function(CalculationFunction::Product(field_names[0..3].to_vec()));
2038 engine
2039 .add_calculation("product_result", product_calc)
2040 .unwrap();
2041 assert_eq!(
2042 engine
2043 .get_field_value("product_result")
2044 .unwrap()
2045 .to_number(),
2046 1000.0
2047 ); }
2049
2050 #[test]
2051 fn test_error_recovery_and_handling() {
2052 let mut engine = CalculationEngine::new();
2053
2054 engine.set_field_value("valid1", FieldValue::Number(10.0));
2056 engine.set_field_value("valid2", FieldValue::Number(20.0));
2057
2058 let expr_with_invalid =
2060 ArithmeticExpression::from_string("valid1 + nonexistent + valid2").unwrap();
2061 engine
2062 .add_calculation("mixed_result", Calculation::Arithmetic(expr_with_invalid))
2063 .unwrap();
2064
2065 assert_eq!(
2067 engine.get_field_value("mixed_result").unwrap().to_number(),
2068 30.0
2069 ); let empty_sum = Calculation::Function(CalculationFunction::Sum(vec![]));
2073 engine.add_calculation("empty_sum", empty_sum).unwrap();
2074 assert_eq!(
2075 engine.get_field_value("empty_sum").unwrap().to_number(),
2076 0.0
2077 );
2078
2079 let mixed_avg = Calculation::Function(CalculationFunction::Average(vec![
2081 "valid1".to_string(),
2082 "nonexistent1".to_string(),
2083 "valid2".to_string(),
2084 "nonexistent2".to_string(),
2085 ]));
2086 engine.add_calculation("mixed_avg", mixed_avg).unwrap();
2087 let avg_result = engine.get_field_value("mixed_avg").unwrap().to_number();
2091 assert!(
2094 avg_result == 7.5 || avg_result == 15.0,
2095 "Average result should be either 7.5 or 15.0, got {}",
2096 avg_result
2097 );
2098 }
2099
2100 #[test]
2101 fn test_real_world_business_scenarios() {
2102 let mut engine = CalculationEngine::new();
2103
2104 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();
2111 engine
2112 .add_calculation("monthly_rate", Calculation::Arithmetic(monthly_rate_expr))
2113 .unwrap();
2114
2115 let total_payments_expr = ArithmeticExpression::from_string("years * 12").unwrap();
2117 engine
2118 .add_calculation(
2119 "total_payments",
2120 Calculation::Arithmetic(total_payments_expr),
2121 )
2122 .unwrap();
2123
2124 engine.set_field_value("hourly_rate", FieldValue::Number(25.50));
2126 engine.set_field_value("hours_worked", FieldValue::Number(42.5));
2127 engine.set_field_value("overtime_multiplier", FieldValue::Number(1.5));
2128 engine.set_field_value("standard_hours", FieldValue::Number(40.0));
2129
2130 let regular_pay_expr =
2132 ArithmeticExpression::from_string("standard_hours * hourly_rate").unwrap();
2133 engine
2134 .add_calculation("regular_pay", Calculation::Arithmetic(regular_pay_expr))
2135 .unwrap();
2136
2137 engine.set_field_value("overtime_hours", FieldValue::Number(2.5)); let overtime_expr =
2142 ArithmeticExpression::from_string("overtime_hours * hourly_rate * overtime_multiplier")
2143 .unwrap();
2144 engine
2145 .add_calculation("overtime_pay", Calculation::Arithmetic(overtime_expr))
2146 .unwrap();
2147
2148 let gross_expr = ArithmeticExpression::from_string("regular_pay + overtime_pay").unwrap();
2150 engine
2151 .add_calculation("gross_pay", Calculation::Arithmetic(gross_expr))
2152 .unwrap();
2153
2154 assert_eq!(
2156 engine.get_field_value("regular_pay").unwrap().to_number(),
2157 1020.0
2158 ); assert_eq!(
2160 engine.get_field_value("overtime_pay").unwrap().to_number(),
2161 95.625
2162 ); assert_eq!(
2164 engine.get_field_value("gross_pay").unwrap().to_number(),
2165 1115.625
2166 ); engine.set_field_value("batch1_qty", FieldValue::Number(100.0));
2170 engine.set_field_value("batch1_cost", FieldValue::Number(10.50));
2171 engine.set_field_value("batch2_qty", FieldValue::Number(75.0));
2172 engine.set_field_value("batch2_cost", FieldValue::Number(11.25));
2173 engine.set_field_value("batch3_qty", FieldValue::Number(50.0));
2174 engine.set_field_value("batch3_cost", FieldValue::Number(12.00));
2175
2176 let batch1_value_expr =
2178 ArithmeticExpression::from_string("batch1_qty * batch1_cost").unwrap();
2179 engine
2180 .add_calculation("batch1_value", Calculation::Arithmetic(batch1_value_expr))
2181 .unwrap();
2182
2183 let batch2_value_expr =
2184 ArithmeticExpression::from_string("batch2_qty * batch2_cost").unwrap();
2185 engine
2186 .add_calculation("batch2_value", Calculation::Arithmetic(batch2_value_expr))
2187 .unwrap();
2188
2189 let batch3_value_expr =
2190 ArithmeticExpression::from_string("batch3_qty * batch3_cost").unwrap();
2191 engine
2192 .add_calculation("batch3_value", Calculation::Arithmetic(batch3_value_expr))
2193 .unwrap();
2194
2195 let total_inventory_calc = Calculation::Function(CalculationFunction::Sum(vec![
2197 "batch1_value".to_string(),
2198 "batch2_value".to_string(),
2199 "batch3_value".to_string(),
2200 ]));
2201 engine
2202 .add_calculation("total_inventory", total_inventory_calc)
2203 .unwrap();
2204
2205 assert_eq!(
2207 engine.get_field_value("batch1_value").unwrap().to_number(),
2208 1050.0
2209 );
2210 assert_eq!(
2211 engine.get_field_value("batch2_value").unwrap().to_number(),
2212 843.75
2213 );
2214 assert_eq!(
2215 engine.get_field_value("batch3_value").unwrap().to_number(),
2216 600.0
2217 );
2218 assert_eq!(
2219 engine
2220 .get_field_value("total_inventory")
2221 .unwrap()
2222 .to_number(),
2223 2493.75
2224 );
2225 }
2226
2227 #[test]
2228 fn test_special_number_values() {
2229 let mut engine = CalculationEngine::new();
2230
2231 engine.set_field_value("nan_val", FieldValue::Number(f64::NAN));
2233 engine.set_field_value("normal_val", FieldValue::Number(10.0));
2234
2235 let nan_expr = ArithmeticExpression::from_string("nan_val + normal_val").unwrap();
2237 engine
2238 .add_calculation("nan_result", Calculation::Arithmetic(nan_expr))
2239 .unwrap();
2240
2241 let result = engine.get_field_value("nan_result").unwrap().to_number();
2242 assert!(result.is_nan());
2243
2244 let sum_with_nan = Calculation::Function(CalculationFunction::Sum(vec![
2246 "nan_val".to_string(),
2247 "normal_val".to_string(),
2248 ]));
2249 engine.add_calculation("sum_nan", sum_with_nan).unwrap();
2250
2251 let sum_result = engine.get_field_value("sum_nan").unwrap().to_number();
2252 assert!(sum_result.is_nan());
2253
2254 engine.set_field_value("val1", FieldValue::Number(5.0));
2256 engine.set_field_value("val2", FieldValue::Number(15.0));
2257
2258 let max_with_nan = Calculation::Function(CalculationFunction::Max(vec![
2259 "nan_val".to_string(),
2260 "val1".to_string(),
2261 "val2".to_string(),
2262 ]));
2263 engine.add_calculation("max_nan", max_with_nan).unwrap();
2264
2265 let max_result = engine.get_field_value("max_nan").unwrap().to_number();
2266 assert_eq!(max_result, 15.0); }
2268
2269 #[test]
2270 fn test_precision_and_rounding_scenarios() {
2271 let mut engine = CalculationEngine::new();
2272
2273 engine.set_field_value("precise1", FieldValue::Number(1.0 / 3.0));
2275 engine.set_field_value("precise2", FieldValue::Number(2.0 / 3.0));
2276
2277 let precision_expr = ArithmeticExpression::from_string("precise1 + precise2").unwrap();
2278 engine
2279 .add_calculation("precision_result", Calculation::Arithmetic(precision_expr))
2280 .unwrap();
2281
2282 let result = engine
2283 .get_field_value("precision_result")
2284 .unwrap()
2285 .to_number();
2286 assert!((result - 1.0).abs() < 1e-15); engine.set_field_value("tiny", FieldValue::Number(1e-100));
2290 engine.set_field_value("huge", FieldValue::Number(1e100));
2291
2292 let scale_expr = ArithmeticExpression::from_string("tiny * huge").unwrap();
2293 engine
2294 .add_calculation("scale_result", Calculation::Arithmetic(scale_expr))
2295 .unwrap();
2296
2297 let scale_result = engine.get_field_value("scale_result").unwrap().to_number();
2298 assert!((scale_result - 1.0).abs() < 1e-14);
2299
2300 engine.set_field_value("price", FieldValue::Number(19.999));
2302 engine.set_field_value("quantity", FieldValue::Number(3.0));
2303
2304 let financial_expr = ArithmeticExpression::from_string("price * quantity").unwrap();
2305 engine
2306 .add_calculation("financial_result", Calculation::Arithmetic(financial_expr))
2307 .unwrap();
2308
2309 let financial_result = engine
2310 .get_field_value("financial_result")
2311 .unwrap()
2312 .to_number();
2313 assert!((financial_result - 59.997).abs() < 1e-10);
2315 }
2316
2317 #[test]
2318 fn test_extreme_calculation_chains() {
2319 let mut engine = CalculationEngine::new();
2320
2321 engine.set_field_value("seed", FieldValue::Number(1.0));
2323
2324 for i in 1..=50 {
2326 let prev = if i == 1 {
2327 "seed".to_string()
2328 } else {
2329 format!("chain_{}", i - 1)
2330 };
2331 let current = format!("chain_{}", i);
2332
2333 let expr = ArithmeticExpression::from_string(&format!("{} + 1", prev)).unwrap();
2334 engine
2335 .add_calculation(¤t, Calculation::Arithmetic(expr))
2336 .unwrap();
2337 }
2338
2339 assert_eq!(
2341 engine.get_field_value("chain_50").unwrap().to_number(),
2342 51.0
2343 );
2344
2345 engine.set_field_value("seed", FieldValue::Number(10.0));
2347 assert_eq!(
2348 engine.get_field_value("chain_50").unwrap().to_number(),
2349 60.0
2350 ); engine.set_field_value("base", FieldValue::Number(5.0));
2354
2355 for i in 1..=20 {
2356 let field_name = format!("derived_{}", i);
2357 let expr = ArithmeticExpression::from_string(&format!("base * {}", i)).unwrap();
2358 engine
2359 .add_calculation(&field_name, Calculation::Arithmetic(expr))
2360 .unwrap();
2361 }
2362
2363 for i in 1..=20 {
2365 let field_name = format!("derived_{}", i);
2366 let expected = 5.0 * i as f64;
2367 assert_eq!(
2368 engine.get_field_value(&field_name).unwrap().to_number(),
2369 expected
2370 );
2371 }
2372
2373 engine.set_field_value("base", FieldValue::Number(10.0));
2375 for i in 1..=20 {
2376 let field_name = format!("derived_{}", i);
2377 let expected = 10.0 * i as f64;
2378 assert_eq!(
2379 engine.get_field_value(&field_name).unwrap().to_number(),
2380 expected
2381 );
2382 }
2383 }
2384
2385 #[test]
2386 fn test_comprehensive_operator_combinations() {
2387 let mut engine = CalculationEngine::new();
2388
2389 engine.set_field_value("a", FieldValue::Number(12.0));
2390 engine.set_field_value("b", FieldValue::Number(4.0));
2391 engine.set_field_value("c", FieldValue::Number(3.0));
2392 engine.set_field_value("d", FieldValue::Number(2.0));
2393
2394 let test_cases = vec![
2396 ("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), ];
2403
2404 for (i, (expr_str, expected)) in test_cases.iter().enumerate() {
2405 let expr = ArithmeticExpression::from_string(expr_str).unwrap();
2406 let field_name = format!("test_{}", i);
2407 engine
2408 .add_calculation(&field_name, Calculation::Arithmetic(expr))
2409 .unwrap();
2410
2411 let result = engine.get_field_value(&field_name).unwrap().to_number();
2412 assert!(
2413 (result - expected).abs() < 0.1,
2414 "Expression '{}' expected {}, got {}",
2415 expr_str,
2416 expected,
2417 result
2418 );
2419 }
2420 }
2421
2422 #[test]
2423 fn test_conditional_calculation_complexity() {
2424 let mut engine = CalculationEngine::new();
2425
2426 engine.set_field_value("customer_type", FieldValue::Text("premium".to_string()));
2428 engine.set_field_value("order_amount", FieldValue::Number(1000.0));
2429 engine.set_field_value("is_premium", FieldValue::Boolean(true));
2430 engine.set_field_value("is_bulk_order", FieldValue::Boolean(true));
2431
2432 let premium_discount = Calculation::Function(CalculationFunction::If {
2434 condition_field: "is_premium".to_string(),
2435 true_value: Box::new(Calculation::Constant(FieldValue::Number(0.15))), false_value: Box::new(Calculation::Constant(FieldValue::Number(0.05))), });
2438 engine
2439 .add_calculation("base_discount", premium_discount)
2440 .unwrap();
2441
2442 let bulk_discount = Calculation::Function(CalculationFunction::If {
2444 condition_field: "is_bulk_order".to_string(),
2445 true_value: Box::new(Calculation::Constant(FieldValue::Number(0.05))), false_value: Box::new(Calculation::Constant(FieldValue::Number(0.0))),
2447 });
2448 engine.add_calculation("bulk_bonus", bulk_discount).unwrap();
2449
2450 let total_discount_expr =
2452 ArithmeticExpression::from_string("base_discount + bulk_bonus").unwrap();
2453 engine
2454 .add_calculation(
2455 "total_discount_rate",
2456 Calculation::Arithmetic(total_discount_expr),
2457 )
2458 .unwrap();
2459
2460 let discount_amount_expr =
2462 ArithmeticExpression::from_string("order_amount * total_discount_rate").unwrap();
2463 engine
2464 .add_calculation(
2465 "discount_amount",
2466 Calculation::Arithmetic(discount_amount_expr),
2467 )
2468 .unwrap();
2469
2470 let final_amount_expr =
2472 ArithmeticExpression::from_string("order_amount - discount_amount").unwrap();
2473 engine
2474 .add_calculation("final_amount", Calculation::Arithmetic(final_amount_expr))
2475 .unwrap();
2476
2477 assert_eq!(
2479 engine.get_field_value("base_discount").unwrap().to_number(),
2480 0.15
2481 );
2482 assert_eq!(
2483 engine.get_field_value("bulk_bonus").unwrap().to_number(),
2484 0.05
2485 );
2486 assert_eq!(
2487 engine
2488 .get_field_value("total_discount_rate")
2489 .unwrap()
2490 .to_number(),
2491 0.20
2492 );
2493 assert_eq!(
2494 engine
2495 .get_field_value("discount_amount")
2496 .unwrap()
2497 .to_number(),
2498 200.0
2499 );
2500 assert_eq!(
2501 engine.get_field_value("final_amount").unwrap().to_number(),
2502 800.0
2503 );
2504
2505 engine.set_field_value("is_premium", FieldValue::Boolean(false));
2507 assert_eq!(
2508 engine.get_field_value("base_discount").unwrap().to_number(),
2509 0.05
2510 );
2511 assert_eq!(
2512 engine.get_field_value("final_amount").unwrap().to_number(),
2513 900.0
2514 ); }
2516
2517 #[test]
2518 fn test_field_value_type_edge_cases() {
2519 let mut engine = CalculationEngine::new();
2520
2521 let edge_cases = vec![
2523 ("", 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), ];
2538
2539 for (i, (text_val, expected)) in edge_cases.iter().enumerate() {
2540 let field_name = format!("edge_case_{}", i);
2541 engine.set_field_value(&field_name, FieldValue::Text(text_val.to_string()));
2542
2543 let result = engine.get_field_value(&field_name).unwrap().to_number();
2544
2545 if expected.is_nan() {
2546 assert!(
2549 result.is_nan() || result == 0.0,
2550 "Text '{}' should convert to NaN or 0.0, got {}",
2551 text_val,
2552 result
2553 );
2554 } else if expected.is_infinite() {
2555 assert_eq!(
2556 result, *expected,
2557 "Text '{}' should convert to {}, got {}",
2558 text_val, expected, result
2559 );
2560 } else {
2561 assert!(
2562 (result - expected).abs() < 1e-10,
2563 "Text '{}' should convert to {}, got {}",
2564 text_val,
2565 expected,
2566 result
2567 );
2568 }
2569 }
2570 }
2571
2572 #[test]
2573 fn test_calculation_engine_state_management() {
2574 let mut engine = CalculationEngine::new();
2575
2576 let summary = engine.get_summary();
2578 assert_eq!(summary.total_fields, 0);
2579 assert_eq!(summary.calculated_fields, 0);
2580
2581 engine.set_field_value("input1", FieldValue::Number(10.0));
2583 engine.set_field_value("input2", FieldValue::Number(20.0));
2584
2585 let expr = ArithmeticExpression::from_string("input1 + input2").unwrap();
2586 engine
2587 .add_calculation("output", Calculation::Arithmetic(expr))
2588 .unwrap();
2589
2590 let summary_after = engine.get_summary();
2591 assert_eq!(summary_after.total_fields, 3); assert_eq!(summary_after.calculated_fields, 1); assert_eq!(summary_after.calculation_order, vec!["output".to_string()]);
2594
2595 engine.remove_calculation("output");
2597 let summary_removed = engine.get_summary();
2598 assert_eq!(summary_removed.total_fields, 2); assert_eq!(summary_removed.calculated_fields, 0);
2600 assert_eq!(summary_removed.calculation_order.len(), 0);
2601
2602 let display_str = format!("{}", summary_removed);
2604 assert!(display_str.contains("Total fields: 2"));
2605 assert!(display_str.contains("Calculated fields: 0"));
2606 }
2607
2608 #[test]
2609 fn test_calculation_error_boundary_conditions() {
2610 let mut engine = CalculationEngine::new();
2611
2612 engine.set_field_value("existing", FieldValue::Number(42.0));
2614 assert_eq!(
2615 engine.get_field_value("existing").unwrap().to_number(),
2616 42.0
2617 );
2618
2619 let expr = ArithmeticExpression::from_string("10 + 5").unwrap();
2621 engine
2622 .add_calculation("existing", Calculation::Arithmetic(expr))
2623 .unwrap();
2624 assert_eq!(
2625 engine.get_field_value("existing").unwrap().to_number(),
2626 15.0
2627 );
2628
2629 engine.set_field_value("base1", FieldValue::Number(5.0));
2631 engine.set_field_value("base2", FieldValue::Number(10.0));
2632
2633 let calc1 = ArithmeticExpression::from_string("base1 * 2").unwrap();
2634 let calc2 = ArithmeticExpression::from_string("base2 / 2").unwrap();
2635
2636 engine
2637 .add_calculation("independent1", Calculation::Arithmetic(calc1))
2638 .unwrap();
2639 engine
2640 .add_calculation("independent2", Calculation::Arithmetic(calc2))
2641 .unwrap();
2642
2643 assert_eq!(
2645 engine.get_field_value("independent1").unwrap().to_number(),
2646 10.0
2647 );
2648 assert_eq!(
2649 engine.get_field_value("independent2").unwrap().to_number(),
2650 5.0
2651 );
2652
2653 engine.recalculate_all().unwrap();
2655 assert_eq!(
2656 engine.get_field_value("existing").unwrap().to_number(),
2657 15.0
2658 );
2659 assert_eq!(
2660 engine.get_field_value("independent1").unwrap().to_number(),
2661 10.0
2662 );
2663 assert_eq!(
2664 engine.get_field_value("independent2").unwrap().to_number(),
2665 5.0
2666 );
2667 }
2668
2669 #[test]
2670 fn test_calculation_stress_and_boundary_conditions() {
2671 let mut engine = CalculationEngine::new();
2672
2673 for i in 0..100 {
2675 let field_name = format!("rapid_field_{}", i);
2676 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2677 }
2678
2679 let field_refs = (0..50)
2681 .map(|i| format!("rapid_field_{}", i))
2682 .collect::<Vec<_>>();
2683
2684 let sum_calc = Calculation::Function(CalculationFunction::Sum(field_refs.clone()));
2685 engine.add_calculation("rapid_sum", sum_calc).unwrap();
2686
2687 let avg_calc = Calculation::Function(CalculationFunction::Average(field_refs));
2688 engine.add_calculation("rapid_avg", avg_calc).unwrap();
2689
2690 assert_eq!(
2692 engine.get_field_value("rapid_sum").unwrap().to_number(),
2693 1225.0
2694 );
2695 assert_eq!(
2696 engine.get_field_value("rapid_avg").unwrap().to_number(),
2697 24.5
2698 );
2699
2700 for update_round in 0..10 {
2702 for i in 0..50 {
2703 let field_name = format!("rapid_field_{}", i);
2704 let new_value = (i as f64) * (update_round as f64 + 1.0);
2705 engine.set_field_value(&field_name, FieldValue::Number(new_value));
2706 }
2707
2708 let current_sum = engine.get_field_value("rapid_sum").unwrap().to_number();
2710 let expected_sum = 1225.0 * (update_round as f64 + 1.0);
2711 assert!(
2712 (current_sum - expected_sum).abs() < 0.01,
2713 "Sum calculation incorrect in round {}: expected {}, got {}",
2714 update_round,
2715 expected_sum,
2716 current_sum
2717 );
2718 }
2719 }
2720
2721 #[test]
2722 fn test_calculation_engine_memory_and_cleanup() {
2723 let mut engine = CalculationEngine::new();
2724
2725 for i in 0..50 {
2727 let field_name = format!("temp_field_{}", i);
2728 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2729
2730 let expr = ArithmeticExpression::from_string(&format!("temp_field_{} * 2", i)).unwrap();
2731 let calc_name = format!("temp_calc_{}", i);
2732 engine
2733 .add_calculation(&calc_name, Calculation::Arithmetic(expr))
2734 .unwrap();
2735 }
2736
2737 for i in 0..50 {
2739 let calc_name = format!("temp_calc_{}", i);
2740 let expected = (i as f64) * 2.0;
2741 assert_eq!(
2742 engine.get_field_value(&calc_name).unwrap().to_number(),
2743 expected
2744 );
2745 }
2746
2747 for i in 0..25 {
2749 let calc_name = format!("temp_calc_{}", i);
2750 engine.remove_calculation(&calc_name);
2751 }
2752
2753 for i in 0..25 {
2755 let calc_name = format!("temp_calc_{}", i);
2756 assert!(engine.get_field_value(&calc_name).is_none());
2757 }
2758
2759 for i in 25..50 {
2760 let calc_name = format!("temp_calc_{}", i);
2761 let expected = (i as f64) * 2.0;
2762 assert_eq!(
2763 engine.get_field_value(&calc_name).unwrap().to_number(),
2764 expected
2765 );
2766 }
2767
2768 let summary = engine.get_summary();
2770 assert_eq!(summary.total_fields, 75);
2772 assert_eq!(summary.calculated_fields, 25);
2773 }
2774
2775 #[test]
2776 fn test_maximum_expression_complexity() {
2777 let mut engine = CalculationEngine::new();
2778
2779 for i in 1..=10 {
2781 let field_name = format!("x{}", i);
2782 engine.set_field_value(&field_name, FieldValue::Number(i as f64));
2783 }
2784
2785 let complex_expr = ArithmeticExpression::from_string(
2787 "((x1 + x2) * (x3 - x4) / (x5 + 1)) ^ 2 + ((x6 * x7) % (x8 + x9)) - x10",
2788 )
2789 .unwrap();
2790
2791 engine
2792 .add_calculation("max_complexity", Calculation::Arithmetic(complex_expr))
2793 .unwrap();
2794
2795 let result = engine
2797 .get_field_value("max_complexity")
2798 .unwrap()
2799 .to_number();
2800 assert!(
2801 result.is_finite(),
2802 "Maximum complexity expression should produce a finite result, got {}",
2803 result
2804 );
2805
2806 engine.set_field_value("condition_flag", FieldValue::Boolean(true));
2808
2809 let conditional_complex = Calculation::Function(CalculationFunction::If {
2810 condition_field: "condition_flag".to_string(),
2811 true_value: Box::new(Calculation::Function(CalculationFunction::Sum(vec![
2812 "x1".to_string(),
2813 "x2".to_string(),
2814 "x3".to_string(),
2815 "x4".to_string(),
2816 "x5".to_string(),
2817 ]))),
2818 false_value: Box::new(Calculation::Function(CalculationFunction::Product(vec![
2819 "x6".to_string(),
2820 "x7".to_string(),
2821 "x8".to_string(),
2822 ]))),
2823 });
2824
2825 engine
2826 .add_calculation("conditional_complex", conditional_complex)
2827 .unwrap();
2828
2829 let conditional_result = engine
2830 .get_field_value("conditional_complex")
2831 .unwrap()
2832 .to_number();
2833 assert_eq!(conditional_result, 15.0); engine.set_field_value("condition_flag", FieldValue::Boolean(false));
2837 let switched_result = engine
2838 .get_field_value("conditional_complex")
2839 .unwrap()
2840 .to_number();
2841 assert_eq!(switched_result, 336.0); }
2843
2844 #[test]
2845 fn test_calculation_order_determinism() {
2846 let mut engine1 = CalculationEngine::new();
2848 let mut engine2 = CalculationEngine::new();
2849
2850 for i in 1..=5 {
2852 let field_name = format!("base{}", i);
2853 let value = FieldValue::Number(i as f64 * 10.0);
2854 engine1.set_field_value(&field_name, value.clone());
2855 engine2.set_field_value(&field_name, value);
2856 }
2857
2858 let calculations = vec![
2860 ("calc1", "base1 + base2"), ("calc2", "base3 * base4"), ("calc3", "base5 / base1"), ("calc4", "base2 - base3"), ];
2865
2866 for (name, expr) in &calculations {
2868 let parsed_expr = ArithmeticExpression::from_string(expr).unwrap();
2869 engine1
2870 .add_calculation(*name, Calculation::Arithmetic(parsed_expr))
2871 .unwrap();
2872 }
2873
2874 for (name, expr) in calculations.iter().rev() {
2876 let parsed_expr = ArithmeticExpression::from_string(expr).unwrap();
2877 engine2
2878 .add_calculation(*name, Calculation::Arithmetic(parsed_expr))
2879 .unwrap();
2880 }
2881
2882 let expected_results = vec![
2884 ("calc1", 30.0),
2885 ("calc2", 1200.0),
2886 ("calc3", 5.0),
2887 ("calc4", -10.0),
2888 ];
2889
2890 for (field_name, expected) in expected_results {
2891 let result1 = engine1.get_field_value(field_name).unwrap().to_number();
2892 let result2 = engine2.get_field_value(field_name).unwrap().to_number();
2893
2894 assert_eq!(
2895 result1, expected,
2896 "Engine1 calculation {} should be {}, got {}",
2897 field_name, expected, result1
2898 );
2899 assert_eq!(
2900 result2, expected,
2901 "Engine2 calculation {} should be {}, got {}",
2902 field_name, expected, result2
2903 );
2904 assert_eq!(
2905 result1, result2,
2906 "Both engines should produce same result for {}: engine1={}, engine2={}",
2907 field_name, result1, result2
2908 );
2909 }
2910
2911 let summary1 = engine1.get_summary();
2913 let summary2 = engine2.get_summary();
2914
2915 assert_eq!(summary1.total_fields, summary2.total_fields);
2916 assert_eq!(summary1.calculated_fields, summary2.calculated_fields);
2917 assert_eq!(
2918 summary1.calculation_order.len(),
2919 summary2.calculation_order.len()
2920 );
2921 }
2922}