mathypad_core/expression/
evaluator.rs

1//! Expression evaluation functions with unit-aware arithmetic
2
3use super::parser::tokenize_with_units;
4use super::tokens::Token;
5use crate::FLOAT_EPSILON;
6use crate::rate_unit;
7use crate::units::{Unit, UnitType, UnitValue, parse_unit};
8use std::collections::HashMap;
9
10/// Main evaluation function that handles context for line references
11pub fn evaluate_expression_with_context(
12    text: &str,
13    previous_results: &[Option<String>],
14    current_line: usize,
15) -> Option<String> {
16    // New approach: tokenize everything then find mathematical patterns
17    if let Some(tokens) = super::parser::tokenize_with_units(text) {
18        // Try to find and evaluate mathematical patterns in the token stream
19        if let Some(result) =
20            evaluate_tokens_stream_with_context(&tokens, previous_results, current_line)
21        {
22            return Some(result.format());
23        }
24    }
25
26    None
27}
28
29/// Find and evaluate mathematical patterns in a token stream
30pub fn evaluate_tokens_stream_with_context(
31    tokens: &[Token],
32    previous_results: &[Option<String>],
33    current_line: usize,
34) -> Option<UnitValue> {
35    if tokens.is_empty() {
36        return None;
37    }
38
39    // Look for the longest valid mathematical subsequence
40    // Try different starting positions and lengths
41    for start in 0..tokens.len() {
42        for end in (start + 1..=tokens.len()).rev() {
43            // Try longest first
44            let subseq = &tokens[start..end];
45            if is_valid_mathematical_sequence(subseq) {
46                // Try to evaluate this subsequence
47                if let Some(result) =
48                    evaluate_tokens_with_units_and_context(subseq, previous_results, current_line)
49                {
50                    return Some(result);
51                }
52                // If this subsequence failed to evaluate and it spans the entire input,
53                // don't try shorter subsequences for certain cases:
54                // 1. Pure mathematical expressions (prevents "5 / 0" from evaluating as "5")
55                // 2. Pure conversion expressions (prevents "5 MB to QPS" from evaluating as "5 MB")
56                // 3. Mixed expressions with conversion at the end (prevents "5 GiB + 10 in seconds" fallback)
57                if start == 0 && end == tokens.len() {
58                    let has_math = has_mathematical_operators(subseq);
59                    let has_conversion = subseq.iter().any(|t| matches!(t, Token::To | Token::In));
60
61                    // Check if this is an expression with conversion at the end (like "A + B in C")
62                    // These should fail entirely if conversion is impossible, not fall back
63                    let has_conversion_at_end = tokens.len() >= 2
64                        && matches!(tokens[tokens.len() - 2], Token::To | Token::In);
65
66                    // Prevent fallback for:
67                    // 1. Pure math expressions: has_math && !has_conversion
68                    // 2. Pure conversion expressions: has_conversion && !has_math
69                    // 3. Mixed expressions with conversion at the end: has_math && has_conversion && has_conversion_at_end
70                    #[allow(clippy::nonminimal_bool)]
71                    if !has_math && has_conversion
72                        || has_math && !has_conversion
73                        || has_math && has_conversion_at_end
74                    {
75                        return None; // Fail entirely for these cases
76                    }
77                    // For other mixed expressions, allow fallback
78                }
79            }
80        }
81    }
82
83    None
84}
85
86/// Check if a token sequence contains mathematical operators
87fn has_mathematical_operators(tokens: &[Token]) -> bool {
88    tokens.iter().any(|t| {
89        matches!(
90            t,
91            Token::Plus | Token::Minus | Token::Multiply | Token::Divide | Token::Power
92        )
93    })
94}
95
96/// Check if a token sequence forms a valid mathematical expression
97fn is_valid_mathematical_sequence(tokens: &[Token]) -> bool {
98    if tokens.is_empty() {
99        return false;
100    }
101
102    // Must have at least one number, unit, line reference, variable, or function
103    let has_value = tokens.iter().any(|t| {
104        matches!(
105            t,
106            Token::Number(_)
107                | Token::NumberWithUnit(_, _)
108                | Token::LineReference(_)
109                | Token::Variable(_)
110                | Token::Function(_)
111        )
112    });
113
114    if !has_value {
115        return false;
116    }
117
118    // Simple validation: check for basic mathematical patterns
119    // More sophisticated validation can be added as needed
120
121    // Pattern 1: Single value (number, unit, variable, line ref)
122    if tokens.len() == 1 {
123        return matches!(
124            tokens[0],
125            Token::Number(_)
126                | Token::NumberWithUnit(_, _)
127                | Token::LineReference(_)
128                | Token::Variable(_)
129        );
130    }
131
132    // Pattern 2: Value + unit conversion (e.g., "5 GiB to TB", "storage to TB")
133    if tokens.len() == 3 {
134        let is_value_or_var = |t: &Token| {
135            matches!(
136                t,
137                Token::Number(_)
138                    | Token::NumberWithUnit(_, _)
139                    | Token::LineReference(_)
140                    | Token::Variable(_)
141            )
142        };
143        let is_unit_or_var =
144            |t: &Token| matches!(t, Token::NumberWithUnit(_, _) | Token::Variable(_));
145
146        if is_value_or_var(&tokens[0])
147            && matches!(tokens[1], Token::To | Token::In)
148            && is_unit_or_var(&tokens[2])
149        {
150            return true;
151        }
152
153        // Pattern: Percentage of value (e.g., "10% of 50")
154        if matches!(tokens[0], Token::NumberWithUnit(_, Unit::Percent))
155            && matches!(tokens[1], Token::Of)
156            && is_value_or_var(&tokens[2])
157        {
158            return true;
159        }
160    }
161
162    // Pattern 3: Function calls (function ( value ))
163    if tokens.len() == 4 {
164        if let (Token::Function(_), Token::LeftParen, _, Token::RightParen) =
165            (&tokens[0], &tokens[1], &tokens[2], &tokens[3])
166        {
167            // Check if the middle token is a value
168            if matches!(
169                tokens[2],
170                Token::Number(_)
171                    | Token::NumberWithUnit(_, _)
172                    | Token::LineReference(_)
173                    | Token::Variable(_)
174            ) {
175                return true;
176            }
177        }
178    }
179
180    // Pattern 4: Binary operations (value op value)
181    if tokens.len() == 3 {
182        let is_value = |t: &Token| {
183            matches!(
184                t,
185                Token::Number(_)
186                    | Token::NumberWithUnit(_, _)
187                    | Token::LineReference(_)
188                    | Token::Variable(_)
189            )
190        };
191        let is_op = |t: &Token| {
192            matches!(
193                t,
194                Token::Plus | Token::Minus | Token::Multiply | Token::Divide | Token::Power
195            )
196        };
197
198        if is_value(&tokens[0]) && is_op(&tokens[1]) && is_value(&tokens[2]) {
199            return true;
200        }
201    }
202
203    // Pattern 4: Function calls (e.g., "sqrt(16)", "sum_above()")
204    let has_function = tokens.iter().any(|t| matches!(t, Token::Function(_)));
205    if has_function {
206        // For function calls, we need to have Function, LeftParen, RightParen pattern
207        // or more complex expressions with functions
208        return true;
209    }
210
211    // Pattern 5: More complex expressions with parentheses, multiple operations
212    // For now, if we have values and operators, assume it could be valid
213    // The actual evaluation will determine if it's truly valid
214    let has_operator = tokens.iter().any(|t| {
215        matches!(
216            t,
217            Token::Plus | Token::Minus | Token::Multiply | Token::Divide | Token::Power
218        )
219    });
220
221    has_value && (tokens.len() == 1 || has_operator)
222}
223
224/// Enhanced evaluation function that handles both expressions and variable assignments
225pub fn evaluate_with_variables(
226    text: &str,
227    variables: &HashMap<String, String>,
228    previous_results: &[Option<String>],
229    current_line: usize,
230) -> (Option<String>, Option<(String, String)>) {
231    // Return (result, optional_variable_assignment)
232
233    // New approach: tokenize everything then find patterns
234    if let Some(tokens) = super::parser::tokenize_with_units(text) {
235        // First check for variable assignments
236        if let Some(assignment) =
237            find_variable_assignment_in_tokens(&tokens, variables, previous_results, current_line)
238        {
239            return (Some(assignment.1.clone()), Some(assignment));
240        }
241
242        // Then look for mathematical expressions
243        if let Some(result) = evaluate_tokens_stream_with_variables(
244            &tokens,
245            variables,
246            previous_results,
247            current_line,
248        ) {
249            return (Some(result.format()), None);
250        }
251    }
252
253    (None, None)
254}
255
256/// Find variable assignment pattern in token stream
257fn find_variable_assignment_in_tokens(
258    tokens: &[Token],
259    variables: &HashMap<String, String>,
260    previous_results: &[Option<String>],
261    current_line: usize,
262) -> Option<(String, String)> {
263    // Look for pattern: Variable Assign Expression
264    if tokens.len() >= 3 {
265        if let (Token::Variable(var_name), Token::Assign) = (&tokens[0], &tokens[1]) {
266            // Extract the right-hand side (everything after =)
267            let rhs_tokens = &tokens[2..];
268
269            // Evaluate the right-hand side
270            if let Some(value) = evaluate_tokens_with_units_and_context_and_variables(
271                rhs_tokens,
272                variables,
273                previous_results,
274                current_line,
275            ) {
276                return Some((var_name.clone(), value.format()));
277            }
278        }
279    }
280
281    None
282}
283
284/// Find and evaluate mathematical patterns in a token stream with variable support
285fn evaluate_tokens_stream_with_variables(
286    tokens: &[Token],
287    variables: &HashMap<String, String>,
288    previous_results: &[Option<String>],
289    current_line: usize,
290) -> Option<UnitValue> {
291    if tokens.is_empty() {
292        return None;
293    }
294
295    // First check if we have undefined variables in what looks like a mathematical context
296    if has_undefined_variables_in_math_context(tokens, variables) {
297        return None; // Fail entirely if undefined variables are in mathematical expressions
298    }
299
300    // Look for the longest valid mathematical subsequence
301    // Try different starting positions and lengths
302    for start in 0..tokens.len() {
303        for end in (start + 1..=tokens.len()).rev() {
304            // Try longest first
305            let subseq = &tokens[start..end];
306            if is_valid_mathematical_sequence(subseq) && all_variables_defined(subseq, variables) {
307                // Try to evaluate this subsequence
308                if let Some(result) = evaluate_tokens_with_units_and_context_and_variables(
309                    subseq,
310                    variables,
311                    previous_results,
312                    current_line,
313                ) {
314                    return Some(result);
315                }
316                // If this subsequence failed to evaluate and it spans the entire input,
317                // don't try shorter subsequences for certain cases:
318                // 1. Pure mathematical expressions (prevents "5 / 0" from evaluating as "5")
319                // 2. Pure conversion expressions (prevents "5 MB to QPS" from evaluating as "5 MB")
320                // Note: Mixed expressions (both math and conversion) allow fallback for partial evaluation
321                if start == 0 && end == tokens.len() {
322                    let has_math = has_mathematical_operators(subseq);
323                    let has_conversion = subseq.iter().any(|t| matches!(t, Token::To | Token::In));
324
325                    // Prevent fallback only for pure expressions that fail
326                    if (has_math && !has_conversion) || (has_conversion && !has_math) {
327                        return None; // Fail entirely for pure expressions
328                    }
329                    // For mixed expressions (has_math && has_conversion), allow fallback
330                }
331            }
332        }
333    }
334
335    None
336}
337
338/// Check if there are undefined variables in what appears to be a mathematical context
339fn has_undefined_variables_in_math_context(
340    tokens: &[Token],
341    variables: &HashMap<String, String>,
342) -> bool {
343    // Look for undefined variables that are adjacent to mathematical operators or values
344    for i in 0..tokens.len() {
345        if let Token::Variable(var_name) = &tokens[i] {
346            if !variables.contains_key(var_name) {
347                // Check if this undefined variable is in a mathematical context
348                let has_math_neighbor = (i > 0 && is_math_token(&tokens[i - 1]))
349                    || (i + 1 < tokens.len() && is_math_token(&tokens[i + 1]));
350
351                if has_math_neighbor {
352                    return true;
353                }
354            }
355        }
356    }
357    false
358}
359
360/// Check if a token is mathematical (operator, number, unit, etc.)
361fn is_math_token(token: &Token) -> bool {
362    matches!(
363        token,
364        Token::Number(_)
365            | Token::NumberWithUnit(_, _)
366            | Token::LineReference(_)
367            | Token::Plus
368            | Token::Minus
369            | Token::Multiply
370            | Token::Divide
371            | Token::Power
372            | Token::LeftParen
373            | Token::RightParen
374            | Token::To
375            | Token::In
376            | Token::Function(_)
377    )
378}
379
380/// Check if all variables in a token sequence are defined
381fn all_variables_defined(tokens: &[Token], variables: &HashMap<String, String>) -> bool {
382    for token in tokens {
383        if let Token::Variable(var_name) = token {
384            if !variables.contains_key(var_name) {
385                return false;
386            }
387        }
388    }
389    true
390}
391
392/// Parse and evaluate with context for line references
393pub fn parse_and_evaluate_with_context(
394    expr: &str,
395    previous_results: &[Option<String>],
396    current_line: usize,
397) -> Option<UnitValue> {
398    let tokens = tokenize_with_units(expr)?;
399    evaluate_tokens_with_units_and_context(&tokens, previous_results, current_line)
400}
401
402/// Evaluate tokens with unit-aware arithmetic and context support
403pub fn evaluate_tokens_with_units_and_context(
404    tokens: &[Token],
405    previous_results: &[Option<String>],
406    current_line: usize,
407) -> Option<UnitValue> {
408    if tokens.is_empty() {
409        return None;
410    }
411
412    // Handle simple conversion expressions like "1 GiB to KiB" (only if it's the entire expression)
413    if tokens.len() == 3 {
414        if let (
415            Token::NumberWithUnit(value, from_unit),
416            Token::To,
417            Token::NumberWithUnit(_, to_unit),
418        ) = (&tokens[0], &tokens[1], &tokens[2])
419        {
420            let unit_value = UnitValue::new(*value, Some(from_unit.clone()));
421            return unit_value.to_unit(to_unit);
422        }
423        // Handle percentage of value expressions like "10% of 50"
424        if let (Token::NumberWithUnit(percentage, Unit::Percent), Token::Of, value_token) =
425            (&tokens[0], &tokens[1], &tokens[2])
426        {
427            // Resolve the value token (could be number, unit, variable, or line reference)
428            let base_value = match value_token {
429                Token::Number(n) => UnitValue::new(*n, None),
430                Token::NumberWithUnit(n, unit) => UnitValue::new(*n, Some(unit.clone())),
431                Token::LineReference(line_index) => {
432                    resolve_line_reference(*line_index, previous_results, current_line)?
433                }
434                _ => return None, // Variables would need additional handling
435            };
436
437            // Calculate percentage: convert percentage to decimal first, then multiply
438            let percentage_decimal = Unit::Percent.to_base_value(*percentage);
439            return Some(UnitValue::new(
440                percentage_decimal * base_value.value,
441                base_value.unit,
442            ));
443        }
444    }
445
446    // Check if we have an "in" or "to" conversion request at the end
447    let mut target_unit_for_conversion = None;
448    let mut evaluation_tokens = tokens;
449
450    // Look for "in" or "to" followed by a unit at the end
451    for i in 0..tokens.len().saturating_sub(1) {
452        if let Token::In | Token::To = &tokens[i] {
453            // Look for unit after "in" or "to"
454            for j in (i + 1)..tokens.len() {
455                if let Token::NumberWithUnit(_, unit) = &tokens[j] {
456                    target_unit_for_conversion = Some(unit.clone());
457                    evaluation_tokens = &tokens[..i]; // Evaluate everything before "in"/"to"
458                    break;
459                }
460            }
461            break;
462        }
463    }
464
465    // Handle simple arithmetic with units
466    let mut operator_stack = Vec::new();
467    let mut value_stack = Vec::new();
468
469    for token in evaluation_tokens {
470        match token {
471            Token::Number(n) => {
472                value_stack.push(UnitValue::new(*n, None));
473            }
474            Token::NumberWithUnit(value, unit) => {
475                value_stack.push(UnitValue::new(*value, Some(unit.clone())));
476            }
477            Token::LineReference(line_index) => {
478                // Resolve line reference to its calculated result
479                if let Some(line_result) =
480                    resolve_line_reference(*line_index, previous_results, current_line)
481                {
482                    value_stack.push(line_result);
483                } else {
484                    return None; // Invalid or circular reference
485                }
486            }
487            Token::Plus | Token::Minus | Token::Multiply | Token::Divide | Token::Power => {
488                while let Some(top_op) = operator_stack.last() {
489                    // Power is right-associative, others are left-associative
490                    let should_pop = if matches!(token, Token::Power) {
491                        // For right-associative operators, pop only if top has higher precedence
492                        precedence_unit(token) < precedence_unit(top_op)
493                    } else {
494                        // For left-associative operators, pop if top has same or higher precedence
495                        precedence_unit(token) <= precedence_unit(top_op)
496                    };
497
498                    if should_pop {
499                        let op = operator_stack.pop().unwrap();
500                        if !apply_operator_with_units(&mut value_stack, &op) {
501                            return None;
502                        }
503                    } else {
504                        break;
505                    }
506                }
507                operator_stack.push(token.clone());
508            }
509            Token::LeftParen => {
510                operator_stack.push(token.clone());
511            }
512            Token::RightParen => {
513                // Process operators until we find a left paren or function
514                while let Some(op) = operator_stack.pop() {
515                    if matches!(op, Token::LeftParen) {
516                        // Check if there's a function waiting
517                        if let Some(Token::Function(func_name)) = operator_stack.last().cloned() {
518                            operator_stack.pop(); // Remove the function
519                            if !apply_function_with_context(
520                                &mut value_stack,
521                                &func_name,
522                                previous_results,
523                                current_line,
524                            ) {
525                                return None;
526                            }
527                        }
528                        break;
529                    }
530                    if !apply_operator_with_units(&mut value_stack, &op) {
531                        return None;
532                    }
533                }
534            }
535            Token::Function(_) => {
536                // Functions are pushed to operator stack
537                operator_stack.push(token.clone());
538            }
539            _ => {}
540        }
541    }
542
543    while let Some(op) = operator_stack.pop() {
544        if !apply_operator_with_units(&mut value_stack, &op) {
545            return None;
546        }
547    }
548
549    if value_stack.len() == 1 {
550        let mut result = value_stack.pop().unwrap();
551
552        // If we have a target unit for conversion, convert the result
553        if let Some(target_unit) = target_unit_for_conversion {
554            if let Some(converted) = result.to_unit(&target_unit) {
555                result = converted;
556            } else {
557                return None; // Explicit conversion failed, fail the entire expression
558            }
559        }
560
561        Some(result)
562    } else {
563        None
564    }
565}
566
567/// Variable-aware version of evaluate_tokens_with_units_and_context
568fn evaluate_tokens_with_units_and_context_and_variables(
569    tokens: &[Token],
570    variables: &HashMap<String, String>,
571    previous_results: &[Option<String>],
572    current_line: usize,
573) -> Option<UnitValue> {
574    if tokens.is_empty() {
575        return None;
576    }
577
578    // Handle simple conversion expressions like "1 GiB to KiB" (only if it's the entire expression)
579    if tokens.len() == 3 {
580        if let (
581            Token::NumberWithUnit(value, from_unit),
582            Token::To,
583            Token::NumberWithUnit(_, to_unit),
584        ) = (&tokens[0], &tokens[1], &tokens[2])
585        {
586            let unit_value = UnitValue::new(*value, Some(from_unit.clone()));
587            return unit_value.to_unit(to_unit);
588        }
589
590        // Handle percentage of value expressions like "10% of 50"
591        if let (Token::NumberWithUnit(percentage, Unit::Percent), Token::Of, value_token) =
592            (&tokens[0], &tokens[1], &tokens[2])
593        {
594            // Resolve the value token (could be number, unit, variable, or line reference)
595            let base_value = match value_token {
596                Token::Number(n) => UnitValue::new(*n, None),
597                Token::NumberWithUnit(n, unit) => UnitValue::new(*n, Some(unit.clone())),
598                Token::LineReference(line_index) => {
599                    resolve_line_reference(*line_index, previous_results, current_line)?
600                }
601                Token::Variable(var_name) => resolve_variable(var_name, variables)?,
602                _ => return None,
603            };
604
605            // Calculate percentage: convert percentage to decimal first, then multiply
606            let percentage_decimal = Unit::Percent.to_base_value(*percentage);
607            return Some(UnitValue::new(
608                percentage_decimal * base_value.value,
609                base_value.unit,
610            ));
611        }
612    }
613
614    // Check if we have an "in" or "to" conversion request at the end
615    let mut target_unit_for_conversion = None;
616    let mut evaluation_tokens = tokens;
617
618    // Look for "in" or "to" followed by a unit at the end
619    for i in 0..tokens.len().saturating_sub(1) {
620        if let Token::In | Token::To = &tokens[i] {
621            // Look for unit after "in" or "to"
622            for j in (i + 1)..tokens.len() {
623                if let Token::NumberWithUnit(_, unit) = &tokens[j] {
624                    target_unit_for_conversion = Some(unit.clone());
625                    evaluation_tokens = &tokens[..i]; // Evaluate everything before "in"/"to"
626                    break;
627                }
628            }
629            break;
630        }
631    }
632
633    // Handle simple arithmetic with units
634    let mut operator_stack = Vec::new();
635    let mut value_stack = Vec::new();
636
637    for token in evaluation_tokens {
638        match token {
639            Token::Number(n) => {
640                value_stack.push(UnitValue::new(*n, None));
641            }
642            Token::NumberWithUnit(value, unit) => {
643                value_stack.push(UnitValue::new(*value, Some(unit.clone())));
644            }
645            Token::LineReference(line_index) => {
646                // Resolve line reference to its calculated result
647                if let Some(line_result) =
648                    resolve_line_reference(*line_index, previous_results, current_line)
649                {
650                    value_stack.push(line_result);
651                } else {
652                    return None; // Invalid or circular reference
653                }
654            }
655            Token::Variable(var_name) => {
656                // Resolve variable to its value
657                if let Some(var_result) = resolve_variable(var_name, variables) {
658                    value_stack.push(var_result);
659                } else {
660                    return None; // Undefined variable
661                }
662            }
663            Token::Plus | Token::Minus | Token::Multiply | Token::Divide | Token::Power => {
664                while let Some(top_op) = operator_stack.last() {
665                    // Power is right-associative, others are left-associative
666                    let should_pop = if matches!(token, Token::Power) {
667                        // For right-associative operators, pop only if top has higher precedence
668                        precedence_unit(token) < precedence_unit(top_op)
669                    } else {
670                        // For left-associative operators, pop if top has same or higher precedence
671                        precedence_unit(token) <= precedence_unit(top_op)
672                    };
673
674                    if should_pop {
675                        let op = operator_stack.pop().unwrap();
676                        if !apply_operator_with_units(&mut value_stack, &op) {
677                            return None;
678                        }
679                    } else {
680                        break;
681                    }
682                }
683                operator_stack.push(token.clone());
684            }
685            Token::LeftParen => {
686                operator_stack.push(token.clone());
687            }
688            Token::RightParen => {
689                // Process operators until we find a left paren or function
690                while let Some(op) = operator_stack.pop() {
691                    if matches!(op, Token::LeftParen) {
692                        // Check if there's a function waiting
693                        if let Some(Token::Function(func_name)) = operator_stack.last().cloned() {
694                            operator_stack.pop(); // Remove the function
695                            if !apply_function_with_context(
696                                &mut value_stack,
697                                &func_name,
698                                previous_results,
699                                current_line,
700                            ) {
701                                return None;
702                            }
703                        }
704                        break;
705                    }
706                    if !apply_operator_with_units(&mut value_stack, &op) {
707                        return None;
708                    }
709                }
710            }
711            Token::Function(_) => {
712                // Functions are pushed to operator stack
713                operator_stack.push(token.clone());
714            }
715            _ => {}
716        }
717    }
718
719    while let Some(op) = operator_stack.pop() {
720        if !apply_operator_with_units(&mut value_stack, &op) {
721            return None;
722        }
723    }
724
725    if value_stack.len() == 1 {
726        let mut result = value_stack.pop().unwrap();
727
728        // If we have a target unit for conversion, convert the result
729        if let Some(target_unit) = target_unit_for_conversion {
730            if let Some(converted) = result.to_unit(&target_unit) {
731                result = converted;
732            } else {
733                return None; // Explicit conversion failed, fail the entire expression
734            }
735        }
736
737        Some(result)
738    } else {
739        None
740    }
741}
742
743/// Resolve a variable to its UnitValue
744fn resolve_variable(var_name: &str, variables: &HashMap<String, String>) -> Option<UnitValue> {
745    if let Some(var_value_str) = variables.get(var_name) {
746        // Parse the variable value back into a UnitValue
747        parse_result_string(var_value_str)
748    } else {
749        None
750    }
751}
752
753/// Resolve a line reference to its calculated result
754pub fn resolve_line_reference(
755    line_index: usize,
756    previous_results: &[Option<String>],
757    current_line: usize,
758) -> Option<UnitValue> {
759    // Prevent circular references
760    if line_index >= current_line {
761        return None;
762    }
763
764    // Check if the referenced line exists and has a result
765    if line_index < previous_results.len() {
766        if let Some(result_str) = &previous_results[line_index] {
767            // Parse the result string back into a UnitValue
768            return parse_result_string(result_str);
769        }
770    }
771
772    None
773}
774
775/// Parse a result string back into a UnitValue
776pub fn parse_result_string(result_str: &str) -> Option<UnitValue> {
777    // Parse a result string like "14 GiB" or "42" back into a UnitValue
778    let parts: Vec<&str> = result_str.split_whitespace().collect();
779
780    if parts.is_empty() {
781        return None;
782    }
783
784    // Try to parse the first part as a number
785    let number_str = parts[0].replace(",", ""); // Remove commas
786    if let Ok(value) = number_str.parse::<f64>() {
787        if parts.len() == 1 {
788            // Just a number
789            return Some(UnitValue::new(value, None));
790        } else if parts.len() == 2 {
791            // Number with unit
792            if let Some(unit) = parse_unit(parts[1]) {
793                return Some(UnitValue::new(value, Some(unit)));
794            }
795        }
796    }
797
798    None
799}
800
801/// Get operator precedence for unit-aware evaluation
802fn precedence_unit(token: &Token) -> i32 {
803    match token {
804        Token::Plus | Token::Minus => 1,
805        Token::Multiply | Token::Divide => 2,
806        Token::Power => 3, // Highest precedence
807        _ => 0,
808    }
809}
810
811/// Apply an operator to two unit values
812fn apply_operator_with_units(stack: &mut Vec<UnitValue>, op: &Token) -> bool {
813    if stack.len() < 2 {
814        return false;
815    }
816
817    let b = stack.pop().unwrap();
818    let a = stack.pop().unwrap();
819
820    let result = match op {
821        Token::Plus => {
822            // Addition: units must be compatible
823            match (&a.unit, &b.unit) {
824                (Some(unit_a), Some(unit_b)) => {
825                    if unit_a.is_compatible_for_addition(unit_b) {
826                        let base_a = unit_a.to_base_value(a.value);
827                        let base_b = unit_b.to_base_value(b.value);
828                        let result_base = base_a + base_b;
829
830                        // Choose the smaller unit (larger value) for the result
831                        let result_unit = if unit_a.to_base_value(1.0) < unit_b.to_base_value(1.0) {
832                            unit_a
833                        } else {
834                            unit_b
835                        };
836                        let result_value = result_unit.clone().from_base_value(result_base);
837                        UnitValue::new(result_value, Some(result_unit.clone()))
838                    } else {
839                        return false;
840                    }
841                }
842                (None, None) => UnitValue::new(a.value + b.value, None),
843                _ => return false, // Can't add number with unit and number without unit
844            }
845        }
846        Token::Minus => {
847            // Subtraction: units must be compatible
848            match (&a.unit, &b.unit) {
849                (Some(unit_a), Some(unit_b)) => {
850                    if unit_a.is_compatible_for_addition(unit_b) {
851                        let base_a = unit_a.to_base_value(a.value);
852                        let base_b = unit_b.to_base_value(b.value);
853                        let result_base = base_a - base_b;
854
855                        // Choose the smaller unit (larger value) for the result
856                        let result_unit = if unit_a.to_base_value(1.0) < unit_b.to_base_value(1.0) {
857                            unit_a
858                        } else {
859                            unit_b
860                        };
861                        let result_value = result_unit.clone().from_base_value(result_base);
862                        UnitValue::new(result_value, Some(result_unit.clone()))
863                    } else {
864                        return false;
865                    }
866                }
867                (None, None) => UnitValue::new(a.value - b.value, None),
868                _ => return false,
869            }
870        }
871        Token::Multiply => {
872            // Multiplication: special cases for units
873            match (&a.unit, &b.unit) {
874                // Time * Rate = Data (convert time to seconds first)
875                (Some(time_unit), Some(rate_unit)) | (Some(rate_unit), Some(time_unit))
876                    if time_unit.unit_type() == UnitType::Time
877                        && (matches!(rate_unit.unit_type(), UnitType::DataRate { .. })) =>
878                {
879                    // Determine which value is time and which is rate
880                    let (time_value, time_u, rate_value, rate_u) =
881                        if time_unit.unit_type() == UnitType::Time {
882                            (a.value, time_unit, b.value, rate_unit)
883                        } else {
884                            (b.value, time_unit, a.value, rate_unit)
885                        };
886
887                    let time_divider = match rate_unit.unit_type() {
888                        UnitType::DataRate { time_multiplier } => time_multiplier,
889                        _ => 1.0,
890                    };
891
892                    // Convert times to seconds
893                    let time_in_seconds = time_u.to_base_value(time_value) / time_divider;
894
895                    // Rate * time = data
896                    let data_unit = match rate_u.to_data_unit() {
897                        Ok(unit) => unit,
898                        Err(_) => return false,
899                    };
900                    UnitValue::new(rate_value * time_in_seconds, Some(data_unit))
901                }
902                // Time * BitRate = Bits
903                (Some(time_unit), Some(rate_unit)) | (Some(rate_unit), Some(time_unit))
904                    if time_unit.unit_type() == UnitType::Time
905                        && rate_unit.unit_type() == UnitType::BitRate =>
906                {
907                    // Check if this is a generic rate unit
908                    if let Unit::RateUnit(rate_data, rate_time) = rate_unit {
909                        // For generic rates, handle the time conversion properly
910                        let (time_value, rate_value) = if time_unit.unit_type() == UnitType::Time {
911                            (a.value, b.value)
912                        } else {
913                            (b.value, a.value)
914                        };
915
916                        // Convert time units to match
917                        let time_in_rate_units = if time_unit == rate_time.as_ref() {
918                            time_value
919                        } else {
920                            // Convert time to the rate's time unit
921                            let time_in_seconds = time_unit.to_base_value(time_value);
922                            rate_time.clone().from_base_value(time_in_seconds)
923                        };
924
925                        UnitValue::new(
926                            rate_value * time_in_rate_units,
927                            Some(rate_data.as_ref().clone()),
928                        )
929                    } else {
930                        // Standard bit rate handling (per second)
931                        let (time_value, time_u, rate_value, rate_u) =
932                            if time_unit.unit_type() == UnitType::Time {
933                                (a.value, time_unit, b.value, rate_unit)
934                            } else {
935                                (b.value, time_unit, a.value, rate_unit)
936                            };
937
938                        // Convert time to seconds
939                        let time_in_seconds = time_u.to_base_value(time_value);
940
941                        // BitRate * time = bits
942                        let bit_unit = match rate_u.to_data_unit() {
943                            Ok(unit) => unit,
944                            Err(_) => return false,
945                        };
946                        UnitValue::new(rate_value * time_in_seconds, Some(bit_unit))
947                    }
948                }
949                // Time * RequestRate = Requests (convert time to seconds first)
950                (Some(time_unit), Some(rate_unit)) | (Some(rate_unit), Some(time_unit))
951                    if time_unit.unit_type() == UnitType::Time
952                        && rate_unit.unit_type() == UnitType::RequestRate =>
953                {
954                    // Determine which value is time and which is rate
955                    let (time_value, time_u, rate_value, rate_u) =
956                        if time_unit.unit_type() == UnitType::Time {
957                            (a.value, time_unit, b.value, rate_unit)
958                        } else {
959                            (b.value, time_unit, a.value, rate_unit)
960                        };
961
962                    // Convert time to seconds
963                    let time_in_seconds = time_u.to_base_value(time_value);
964
965                    // RequestRate * time = requests
966                    let request_unit = match rate_u.to_request_unit() {
967                        Ok(unit) => unit,
968                        Err(_) => return false,
969                    };
970                    UnitValue::new(rate_value * time_in_seconds, Some(request_unit))
971                }
972                // Data * Currency/Data Rate = Currency (e.g., 1 TiB * $5/GiB = $5120)
973                (Some(data_unit), Some(Unit::RateUnit(rate_numerator, rate_denominator)))
974                    if data_unit.unit_type() == UnitType::Data
975                        && rate_numerator.unit_type() == UnitType::Currency
976                        && rate_denominator.unit_type() == UnitType::Data =>
977                {
978                    // Convert data units to match the rate's denominator
979                    let data_in_rate_units = if data_unit == rate_denominator.as_ref() {
980                        a.value
981                    } else {
982                        // Convert data to the rate's data unit
983                        let data_in_base = data_unit.to_base_value(a.value);
984                        rate_denominator.clone().from_base_value(data_in_base)
985                    };
986
987                    UnitValue::new(
988                        b.value * data_in_rate_units,
989                        Some(rate_numerator.as_ref().clone()),
990                    )
991                }
992                // Currency/Data Rate * Data = Currency (reverse order)
993                (Some(Unit::RateUnit(rate_numerator, rate_denominator)), Some(data_unit))
994                    if data_unit.unit_type() == UnitType::Data
995                        && rate_numerator.unit_type() == UnitType::Currency
996                        && rate_denominator.unit_type() == UnitType::Data =>
997                {
998                    // Convert data units to match the rate's denominator
999                    let data_in_rate_units = if data_unit == rate_denominator.as_ref() {
1000                        b.value
1001                    } else {
1002                        // Convert data to the rate's data unit
1003                        let data_in_base = data_unit.to_base_value(b.value);
1004                        rate_denominator.clone().from_base_value(data_in_base)
1005                    };
1006
1007                    UnitValue::new(
1008                        a.value * data_in_rate_units,
1009                        Some(rate_numerator.as_ref().clone()),
1010                    )
1011                }
1012                // Time * Generic Rate = Base Unit (for currency rates, etc.)
1013                (Some(time_unit), Some(rate_unit)) | (Some(rate_unit), Some(time_unit))
1014                    if time_unit.unit_type() == UnitType::Time =>
1015                {
1016                    // Check if this is a generic rate unit (exclude currency/data rates)
1017                    if let Unit::RateUnit(rate_data, rate_time) = rate_unit {
1018                        // Skip currency/data rates (they should be handled above)
1019                        if rate_data.unit_type() == UnitType::Currency
1020                            && rate_time.unit_type() == UnitType::Data
1021                        {
1022                            return false;
1023                        }
1024                        let (time_value, rate_value) = if time_unit.unit_type() == UnitType::Time {
1025                            (a.value, b.value)
1026                        } else {
1027                            (b.value, a.value)
1028                        };
1029
1030                        // Convert time units to match
1031                        let time_in_rate_units = if time_unit == rate_time.as_ref() {
1032                            time_value
1033                        } else {
1034                            // Convert time to the rate's time unit
1035                            let time_in_seconds = time_unit.to_base_value(time_value);
1036                            rate_time.clone().from_base_value(time_in_seconds)
1037                        };
1038
1039                        UnitValue::new(
1040                            rate_value * time_in_rate_units,
1041                            Some(rate_data.as_ref().clone()),
1042                        )
1043                    } else {
1044                        return false; // Not a generic rate
1045                    }
1046                }
1047                // Data * Time = Data (total transferred) - for specific data units
1048                (Some(data_unit), Some(time_unit)) | (Some(time_unit), Some(data_unit))
1049                    if data_unit.unit_type() == UnitType::Data
1050                        && time_unit.unit_type() == UnitType::Time =>
1051                {
1052                    UnitValue::new(a.value * b.value, Some(data_unit.clone()))
1053                }
1054                (Some(rate_unit), Some(Unit::Second)) | (Some(Unit::Second), Some(rate_unit))
1055                    if matches!(rate_unit.unit_type(), UnitType::DataRate { .. }) =>
1056                {
1057                    let data_unit = match rate_unit.to_data_unit() {
1058                        Ok(unit) => unit,
1059                        Err(_) => return false,
1060                    };
1061                    UnitValue::new(a.value * b.value, Some(data_unit))
1062                }
1063                (Some(unit), None) | (None, Some(unit)) => {
1064                    // Number * unit = unit
1065                    UnitValue::new(a.value * b.value, Some(unit.clone()))
1066                }
1067                (None, None) => UnitValue::new(a.value * b.value, None),
1068                _ => return false, // Unsupported unit combination
1069            }
1070        }
1071        Token::Divide => {
1072            match (&a.unit, &b.unit) {
1073                (Some(data_unit), Some(time_unit))
1074                    if data_unit.unit_type() == UnitType::Data
1075                        && time_unit.unit_type() == UnitType::Time =>
1076                {
1077                    // Check if time unit is seconds - if so, create traditional per-second rate
1078                    if time_unit == &Unit::Second {
1079                        // Data / seconds = traditional rate (for backwards compatibility)
1080                        let rate_unit = match data_unit.to_rate_unit() {
1081                            Ok(unit) => unit,
1082                            Err(_) => return false,
1083                        };
1084                        UnitValue::new(a.value / b.value, Some(rate_unit))
1085                    } else {
1086                        // Data / other time unit = generic rate
1087                        let rate_unit = Unit::RateUnit(
1088                            Box::new(data_unit.clone()),
1089                            Box::new(time_unit.clone()),
1090                        );
1091                        UnitValue::new(a.value / b.value, Some(rate_unit))
1092                    }
1093                }
1094                (Some(bit_unit), Some(time_unit))
1095                    if bit_unit.unit_type() == UnitType::Bit
1096                        && time_unit.unit_type() == UnitType::Time =>
1097                {
1098                    // Check if time unit is seconds - if so, create traditional per-second bit rate
1099                    if time_unit == &Unit::Second {
1100                        // Bit / seconds = traditional bit rate (for backwards compatibility)
1101                        let rate_unit = match bit_unit.to_rate_unit() {
1102                            Ok(unit) => unit,
1103                            Err(_) => return false,
1104                        };
1105                        UnitValue::new(a.value / b.value, Some(rate_unit))
1106                    } else {
1107                        // Bit / other time unit = generic bit rate
1108                        let rate_unit = rate_unit!(bit_unit.clone(), time_unit.clone());
1109                        UnitValue::new(a.value / b.value, Some(rate_unit))
1110                    }
1111                }
1112                (Some(request_unit), Some(time_unit))
1113                    if request_unit.unit_type() == UnitType::Request
1114                        && time_unit.unit_type() == UnitType::Time =>
1115                {
1116                    // Requests / time = request rate
1117                    // Convert time to seconds first
1118                    let time_in_seconds = time_unit.to_base_value(b.value);
1119                    let rate_unit = match request_unit.to_rate_unit() {
1120                        Ok(unit) => unit,
1121                        Err(_) => return false,
1122                    };
1123                    UnitValue::new(a.value / time_in_seconds, Some(rate_unit))
1124                }
1125                // Currency / Time = Currency Rate (generic rate)
1126                (Some(currency_unit), Some(time_unit))
1127                    if currency_unit.unit_type() == UnitType::Currency
1128                        && time_unit.unit_type() == UnitType::Time =>
1129                {
1130                    // Currency / time = currency rate
1131                    let rate_unit = Unit::RateUnit(
1132                        Box::new(currency_unit.clone()),
1133                        Box::new(time_unit.clone()),
1134                    );
1135                    UnitValue::new(a.value / b.value, Some(rate_unit))
1136                }
1137                // Currency / Data = Currency Rate (e.g., $/GiB)
1138                (Some(currency_unit), Some(data_unit))
1139                    if currency_unit.unit_type() == UnitType::Currency
1140                        && data_unit.unit_type() == UnitType::Data =>
1141                {
1142                    // Currency / data = currency/data rate
1143                    let rate_unit = Unit::RateUnit(
1144                        Box::new(currency_unit.clone()),
1145                        Box::new(data_unit.clone()),
1146                    );
1147                    UnitValue::new(a.value / b.value, Some(rate_unit))
1148                }
1149                // Data / DataRate = Time
1150                (Some(data_unit), Some(rate_unit))
1151                    if data_unit.unit_type() == UnitType::Data
1152                        && matches!(rate_unit.unit_type(), UnitType::DataRate { .. }) =>
1153                {
1154                    // Check if this is a generic rate unit
1155                    if let Unit::RateUnit(rate_data, rate_time) = rate_unit {
1156                        // For generic rates, we need to match the data units and return the time unit
1157                        if data_unit.unit_type() == rate_data.unit_type() {
1158                            // Convert both to base units
1159                            let data_base = data_unit.to_base_value(a.value);
1160                            let rate_data_base = rate_data.to_base_value(b.value);
1161                            if rate_data_base.abs() < FLOAT_EPSILON {
1162                                return false;
1163                            }
1164                            let time_value = data_base / rate_data_base;
1165                            UnitValue::new(time_value, Some(rate_time.as_ref().clone()))
1166                        } else {
1167                            return false;
1168                        }
1169                    } else {
1170                        // Standard per-second rate handling
1171                        let data_in_bytes = data_unit.to_base_value(a.value);
1172                        let rate_in_bytes_per_sec = rate_unit.to_base_value(b.value);
1173                        if rate_in_bytes_per_sec.abs() < FLOAT_EPSILON {
1174                            return false;
1175                        }
1176                        let time_in_seconds = data_in_bytes / rate_in_bytes_per_sec;
1177                        UnitValue::new(time_in_seconds, Some(Unit::Second))
1178                    }
1179                }
1180                // Data / BitRate = Time (need to convert between bits and bytes)
1181                (Some(data_unit), Some(rate_unit))
1182                    if data_unit.unit_type() == UnitType::Data
1183                        && rate_unit.unit_type() == UnitType::BitRate =>
1184                {
1185                    // Convert data to bytes and rate to bits per second
1186                    let data_in_bytes = data_unit.to_base_value(a.value);
1187                    let rate_in_bits_per_sec = rate_unit.to_base_value(b.value);
1188                    if rate_in_bits_per_sec.abs() < FLOAT_EPSILON {
1189                        return false;
1190                    }
1191                    // Convert bytes to bits (1 byte = 8 bits)
1192                    let data_in_bits = data_in_bytes * 8.0;
1193                    let time_in_seconds = data_in_bits / rate_in_bits_per_sec;
1194                    UnitValue::new(time_in_seconds, Some(Unit::Second))
1195                }
1196                // Bit / DataRate = Time (need to convert between bits and bytes)
1197                (Some(data_unit), Some(rate_unit))
1198                    if data_unit.unit_type() == UnitType::Bit
1199                        && matches!(rate_unit.unit_type(), UnitType::DataRate { .. }) =>
1200                {
1201                    // Convert data to bits and rate to bytes per second
1202                    let data_in_bits = data_unit.to_base_value(a.value);
1203                    let rate_in_bytes_per_sec = rate_unit.to_base_value(b.value);
1204                    if rate_in_bytes_per_sec.abs() < FLOAT_EPSILON {
1205                        return false;
1206                    }
1207                    // Convert bytes to bits (1 byte = 8 bits)
1208                    let rate_in_bits_per_sec = rate_in_bytes_per_sec * 8.0;
1209                    let time_in_seconds = data_in_bits / rate_in_bits_per_sec;
1210                    UnitValue::new(time_in_seconds, Some(Unit::Second))
1211                }
1212                // Bit / BitRate = Time
1213                (Some(data_unit), Some(rate_unit))
1214                    if data_unit.unit_type() == UnitType::Bit
1215                        && rate_unit.unit_type() == UnitType::BitRate =>
1216                {
1217                    // Convert data to bits and rate to bits per second
1218                    let data_in_bits = data_unit.to_base_value(a.value);
1219                    let rate_in_bits_per_sec = rate_unit.to_base_value(b.value);
1220                    if rate_in_bits_per_sec.abs() < FLOAT_EPSILON {
1221                        return false;
1222                    }
1223                    let time_in_seconds = data_in_bits / rate_in_bits_per_sec;
1224                    UnitValue::new(time_in_seconds, Some(Unit::Second))
1225                }
1226                (Some(rate_unit), Some(time_unit))
1227                    if rate_unit.unit_type() == UnitType::RequestRate
1228                        && time_unit.unit_type() == UnitType::Time =>
1229                {
1230                    // RequestRate / time = RequestRate (rate per unit time)
1231                    // This is a more complex case - dividing a rate by time
1232                    // For now, we'll treat this as invalid
1233                    return false;
1234                }
1235                // Compatible units divided = dimensionless ratio
1236                (Some(unit_a), Some(unit_b)) => {
1237                    // For currencies, only allow division of the exact same currency
1238                    if unit_a.unit_type() == UnitType::Currency && unit_a != unit_b {
1239                        return false; // Cannot divide different currencies without exchange rates
1240                    }
1241
1242                    // Check if units are compatible (same unit type or bit/data conversion)
1243                    let compatible = unit_a.unit_type() == unit_b.unit_type()
1244                        || (unit_a.unit_type() == UnitType::Bit
1245                            && unit_b.unit_type() == UnitType::Data)
1246                        || (unit_a.unit_type() == UnitType::Data
1247                            && unit_b.unit_type() == UnitType::Bit);
1248
1249                    if compatible {
1250                        // Convert both to base values and divide to get dimensionless ratio
1251                        let mut base_a = unit_a.to_base_value(a.value);
1252                        let mut base_b = unit_b.to_base_value(b.value);
1253
1254                        // Handle bit/byte conversions: normalize to same base (bits)
1255                        if unit_a.unit_type() == UnitType::Data
1256                            && unit_b.unit_type() == UnitType::Bit
1257                        {
1258                            base_a *= 8.0; // Convert bytes to bits
1259                        } else if unit_a.unit_type() == UnitType::Bit
1260                            && unit_b.unit_type() == UnitType::Data
1261                        {
1262                            base_b *= 8.0; // Convert bytes to bits
1263                        }
1264
1265                        if base_b.abs() < FLOAT_EPSILON {
1266                            return false;
1267                        }
1268                        let ratio = base_a / base_b;
1269                        UnitValue::new(ratio, None) // No unit = dimensionless
1270                    } else {
1271                        return false; // Incompatible unit types
1272                    }
1273                }
1274                (Some(unit), None) => {
1275                    // unit / number = unit
1276                    if b.value.abs() < FLOAT_EPSILON {
1277                        return false;
1278                    }
1279                    UnitValue::new(a.value / b.value, Some(unit.clone()))
1280                }
1281                (None, None) => {
1282                    if b.value.abs() < FLOAT_EPSILON {
1283                        return false;
1284                    }
1285                    UnitValue::new(a.value / b.value, None)
1286                }
1287                _ => return false,
1288            }
1289        }
1290        Token::Power => {
1291            // Exponentiation: only allowed for dimensionless values
1292            match (&a.unit, &b.unit) {
1293                (None, None) => {
1294                    // Both dimensionless - standard exponentiation
1295                    UnitValue::new(a.value.powf(b.value), None)
1296                }
1297                (Some(_unit), None) => {
1298                    // Base has unit, exponent is dimensionless
1299                    // Only allowed for certain cases (like square/cube)
1300                    if b.value == 2.0 || b.value == 3.0 {
1301                        // For now, disallow units with exponentiation
1302                        // Future: could support area/volume units
1303                        return false;
1304                    } else {
1305                        return false;
1306                    }
1307                }
1308                _ => return false, // Can't raise units to powers or use units as exponents
1309            }
1310        }
1311        _ => return false,
1312    };
1313
1314    stack.push(result);
1315    true
1316}
1317
1318/// Helper function to add two UnitValues with proper unit handling
1319fn add_unit_values(a: &UnitValue, b: &UnitValue) -> Option<UnitValue> {
1320    match (&a.unit, &b.unit) {
1321        (Some(unit_a), Some(unit_b)) => {
1322            if unit_a.is_compatible_for_addition(unit_b) {
1323                let base_a = unit_a.to_base_value(a.value);
1324                let base_b = unit_b.to_base_value(b.value);
1325                let result_base = base_a + base_b;
1326
1327                // Choose the smaller unit (larger value) for the result
1328                let result_unit = if unit_a.to_base_value(1.0) < unit_b.to_base_value(1.0) {
1329                    unit_a
1330                } else {
1331                    unit_b
1332                };
1333                let result_value = result_unit.clone().from_base_value(result_base);
1334                Some(UnitValue::new(result_value, Some(result_unit.clone())))
1335            } else {
1336                None // Can't add incompatible units
1337            }
1338        }
1339        (None, None) => Some(UnitValue::new(a.value + b.value, None)),
1340        (Some(unit), None) => {
1341            // When adding a unit value to a dimensionless value, keep the unit
1342            // This allows sum_above() to work correctly when mixing numbers and unit values
1343            Some(UnitValue::new(a.value + b.value, Some(unit.clone())))
1344        }
1345        (None, Some(unit)) => {
1346            // When adding a dimensionless value to a unit value, keep the unit
1347            Some(UnitValue::new(a.value + b.value, Some(unit.clone())))
1348        }
1349    }
1350}
1351
1352/// Apply a function with context support (for functions like sum_above)
1353fn apply_function_with_context(
1354    stack: &mut Vec<UnitValue>,
1355    func_name: &str,
1356    previous_results: &[Option<String>],
1357    current_line: usize,
1358) -> bool {
1359    let result = match func_name {
1360        "sqrt" => {
1361            if stack.is_empty() {
1362                return false;
1363            }
1364            let arg = stack.pop().unwrap();
1365
1366            // Only allow sqrt for dimensionless values
1367            match &arg.unit {
1368                None => {
1369                    if arg.value < 0.0 {
1370                        return false; // Can't take square root of negative number
1371                    }
1372                    UnitValue::new(arg.value.sqrt(), None)
1373                }
1374                Some(_) => {
1375                    // For now, don't allow sqrt of values with units
1376                    // Future: could support area -> length conversions
1377                    return false;
1378                }
1379            }
1380        }
1381        "sum_above" => {
1382            // sum_above() doesn't take arguments from stack
1383            // It sums all the results from lines above the current line
1384            let mut total = UnitValue::new(0.0, None);
1385            let mut has_values = false;
1386
1387            // Sum all previous results that can be summed
1388            for (i, result_str) in previous_results.iter().enumerate() {
1389                if i >= current_line {
1390                    break; // Don't include current line or lines below
1391                }
1392
1393                if let Some(result_str) = result_str {
1394                    if let Some(unit_value) = parse_result_string(result_str) {
1395                        // Try to add this value to the total
1396                        if let Some(new_total) = add_unit_values(&total, &unit_value) {
1397                            total = new_total;
1398                            has_values = true;
1399                        }
1400                        // If we can't add this value, skip it (different unit types)
1401                    }
1402                }
1403            }
1404
1405            if !has_values {
1406                // If no values could be summed, return 0
1407                total = UnitValue::new(0.0, None);
1408            }
1409
1410            total
1411        }
1412        _ => return false, // Unknown function
1413    };
1414
1415    stack.push(result);
1416    true
1417}