Skip to main content

formualizer_eval/builtins/
engineering.rs

1//! Engineering functions
2//! Bitwise: BITAND, BITOR, BITXOR, BITLSHIFT, BITRSHIFT
3
4use super::utils::{ARG_ANY_TWO, ARG_NUM_LENIENT_TWO, coerce_num};
5use crate::args::ArgSchema;
6use crate::function::Function;
7use crate::traits::{ArgumentHandle, FunctionContext};
8use formualizer_common::{ExcelError, LiteralValue};
9use formualizer_macros::func_caps;
10
11/// Helper to convert to integer for bitwise operations
12/// Excel's bitwise functions only work with non-negative integers up to 2^48
13fn to_bitwise_int(v: &LiteralValue) -> Result<i64, ExcelError> {
14    let n = coerce_num(v)?;
15    if n < 0.0 || n != n.trunc() || n >= 281474976710656.0 {
16        // 2^48
17        return Err(ExcelError::new_num());
18    }
19    Ok(n as i64)
20}
21
22/* ─────────────────────────── BITAND ──────────────────────────── */
23
24/// BITAND(number1, number2) - Returns bitwise AND of two numbers
25#[derive(Debug)]
26pub struct BitAndFn;
27impl Function for BitAndFn {
28    func_caps!(PURE);
29    fn name(&self) -> &'static str {
30        "BITAND"
31    }
32    fn min_args(&self) -> usize {
33        2
34    }
35    fn arg_schema(&self) -> &'static [ArgSchema] {
36        &ARG_NUM_LENIENT_TWO[..]
37    }
38    fn eval<'a, 'b, 'c>(
39        &self,
40        args: &'c [ArgumentHandle<'a, 'b>],
41        _ctx: &dyn FunctionContext<'b>,
42    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
43        let a = match args[0].value()?.into_literal() {
44            LiteralValue::Error(e) => {
45                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
46            }
47            other => match to_bitwise_int(&other) {
48                Ok(n) => n,
49                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
50            },
51        };
52        let b = match args[1].value()?.into_literal() {
53            LiteralValue::Error(e) => {
54                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
55            }
56            other => match to_bitwise_int(&other) {
57                Ok(n) => n,
58                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
59            },
60        };
61        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
62            (a & b) as f64,
63        )))
64    }
65}
66
67/* ─────────────────────────── BITOR ──────────────────────────── */
68
69/// BITOR(number1, number2) - Returns bitwise OR of two numbers
70#[derive(Debug)]
71pub struct BitOrFn;
72impl Function for BitOrFn {
73    func_caps!(PURE);
74    fn name(&self) -> &'static str {
75        "BITOR"
76    }
77    fn min_args(&self) -> usize {
78        2
79    }
80    fn arg_schema(&self) -> &'static [ArgSchema] {
81        &ARG_NUM_LENIENT_TWO[..]
82    }
83    fn eval<'a, 'b, 'c>(
84        &self,
85        args: &'c [ArgumentHandle<'a, 'b>],
86        _ctx: &dyn FunctionContext<'b>,
87    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
88        let a = match args[0].value()?.into_literal() {
89            LiteralValue::Error(e) => {
90                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
91            }
92            other => match to_bitwise_int(&other) {
93                Ok(n) => n,
94                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
95            },
96        };
97        let b = match args[1].value()?.into_literal() {
98            LiteralValue::Error(e) => {
99                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
100            }
101            other => match to_bitwise_int(&other) {
102                Ok(n) => n,
103                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
104            },
105        };
106        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
107            (a | b) as f64,
108        )))
109    }
110}
111
112/* ─────────────────────────── BITXOR ──────────────────────────── */
113
114/// BITXOR(number1, number2) - Returns bitwise XOR of two numbers
115#[derive(Debug)]
116pub struct BitXorFn;
117impl Function for BitXorFn {
118    func_caps!(PURE);
119    fn name(&self) -> &'static str {
120        "BITXOR"
121    }
122    fn min_args(&self) -> usize {
123        2
124    }
125    fn arg_schema(&self) -> &'static [ArgSchema] {
126        &ARG_NUM_LENIENT_TWO[..]
127    }
128    fn eval<'a, 'b, 'c>(
129        &self,
130        args: &'c [ArgumentHandle<'a, 'b>],
131        _ctx: &dyn FunctionContext<'b>,
132    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
133        let a = match args[0].value()?.into_literal() {
134            LiteralValue::Error(e) => {
135                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
136            }
137            other => match to_bitwise_int(&other) {
138                Ok(n) => n,
139                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
140            },
141        };
142        let b = match args[1].value()?.into_literal() {
143            LiteralValue::Error(e) => {
144                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
145            }
146            other => match to_bitwise_int(&other) {
147                Ok(n) => n,
148                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
149            },
150        };
151        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
152            (a ^ b) as f64,
153        )))
154    }
155}
156
157/* ─────────────────────────── BITLSHIFT ──────────────────────────── */
158
159/// BITLSHIFT(number, shift_amount) - Returns number shifted left by shift_amount bits
160#[derive(Debug)]
161pub struct BitLShiftFn;
162impl Function for BitLShiftFn {
163    func_caps!(PURE);
164    fn name(&self) -> &'static str {
165        "BITLSHIFT"
166    }
167    fn min_args(&self) -> usize {
168        2
169    }
170    fn arg_schema(&self) -> &'static [ArgSchema] {
171        &ARG_NUM_LENIENT_TWO[..]
172    }
173    fn eval<'a, 'b, 'c>(
174        &self,
175        args: &'c [ArgumentHandle<'a, 'b>],
176        _ctx: &dyn FunctionContext<'b>,
177    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
178        let n = match args[0].value()?.into_literal() {
179            LiteralValue::Error(e) => {
180                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
181            }
182            other => match to_bitwise_int(&other) {
183                Ok(n) => n,
184                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
185            },
186        };
187        let shift = match args[1].value()?.into_literal() {
188            LiteralValue::Error(e) => {
189                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
190            }
191            other => coerce_num(&other)? as i32,
192        };
193
194        // Negative shift means right shift
195        let result = if shift >= 0 {
196            if shift >= 48 {
197                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
198                    ExcelError::new_num(),
199                )));
200            }
201            let shifted = n << shift;
202            // Check if result exceeds 48-bit limit
203            if shifted >= 281474976710656 {
204                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
205                    ExcelError::new_num(),
206                )));
207            }
208            shifted
209        } else {
210            let rshift = (-shift) as u32;
211            if rshift >= 48 { 0 } else { n >> rshift }
212        };
213
214        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
215            result as f64,
216        )))
217    }
218}
219
220/* ─────────────────────────── BITRSHIFT ──────────────────────────── */
221
222/// BITRSHIFT(number, shift_amount) - Returns number shifted right by shift_amount bits
223#[derive(Debug)]
224pub struct BitRShiftFn;
225impl Function for BitRShiftFn {
226    func_caps!(PURE);
227    fn name(&self) -> &'static str {
228        "BITRSHIFT"
229    }
230    fn min_args(&self) -> usize {
231        2
232    }
233    fn arg_schema(&self) -> &'static [ArgSchema] {
234        &ARG_NUM_LENIENT_TWO[..]
235    }
236    fn eval<'a, 'b, 'c>(
237        &self,
238        args: &'c [ArgumentHandle<'a, 'b>],
239        _ctx: &dyn FunctionContext<'b>,
240    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
241        let n = match args[0].value()?.into_literal() {
242            LiteralValue::Error(e) => {
243                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
244            }
245            other => match to_bitwise_int(&other) {
246                Ok(n) => n,
247                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
248            },
249        };
250        let shift = match args[1].value()?.into_literal() {
251            LiteralValue::Error(e) => {
252                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
253            }
254            other => coerce_num(&other)? as i32,
255        };
256
257        // Negative shift means left shift
258        let result = if shift >= 0 {
259            if shift >= 48 { 0 } else { n >> shift }
260        } else {
261            let lshift = (-shift) as u32;
262            if lshift >= 48 {
263                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
264                    ExcelError::new_num(),
265                )));
266            }
267            let shifted = n << lshift;
268            // Check if result exceeds 48-bit limit
269            if shifted >= 281474976710656 {
270                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
271                    ExcelError::new_num(),
272                )));
273            }
274            shifted
275        };
276
277        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
278            result as f64,
279        )))
280    }
281}
282
283/* ─────────────────────────── Base Conversion Functions ──────────────────────────── */
284
285use super::utils::ARG_ANY_ONE;
286
287/// Helper to coerce value to text for base conversion
288fn coerce_base_text(v: &LiteralValue) -> Result<String, ExcelError> {
289    match v {
290        LiteralValue::Text(s) => Ok(s.clone()),
291        LiteralValue::Int(i) => Ok(i.to_string()),
292        LiteralValue::Number(n) => Ok((*n as i64).to_string()),
293        LiteralValue::Error(e) => Err(e.clone()),
294        _ => Err(ExcelError::new_value()),
295    }
296}
297
298/// BIN2DEC(number) - Converts binary number to decimal
299#[derive(Debug)]
300pub struct Bin2DecFn;
301impl Function for Bin2DecFn {
302    func_caps!(PURE);
303    fn name(&self) -> &'static str {
304        "BIN2DEC"
305    }
306    fn min_args(&self) -> usize {
307        1
308    }
309    fn arg_schema(&self) -> &'static [ArgSchema] {
310        &ARG_ANY_ONE[..]
311    }
312    fn eval<'a, 'b, 'c>(
313        &self,
314        args: &'c [ArgumentHandle<'a, 'b>],
315        _ctx: &dyn FunctionContext<'b>,
316    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
317        let text = match args[0].value()?.into_literal() {
318            LiteralValue::Error(e) => {
319                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
320            }
321            other => match coerce_base_text(&other) {
322                Ok(s) => s,
323                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
324            },
325        };
326
327        // Excel accepts 10-character binary (with sign bit)
328        if text.len() > 10 || !text.chars().all(|c| c == '0' || c == '1') {
329            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
330                ExcelError::new_num(),
331            )));
332        }
333
334        // Handle two's complement for negative numbers (10 bits, first bit is sign)
335        let result = if text.len() == 10 && text.starts_with('1') {
336            // Negative number in two's complement
337            let val = i64::from_str_radix(&text, 2).unwrap_or(0);
338            val - 1024 // 2^10
339        } else {
340            i64::from_str_radix(&text, 2).unwrap_or(0)
341        };
342
343        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
344            result as f64,
345        )))
346    }
347}
348
349/// DEC2BIN(number, [places]) - Converts decimal to binary
350#[derive(Debug)]
351pub struct Dec2BinFn;
352impl Function for Dec2BinFn {
353    func_caps!(PURE);
354    fn name(&self) -> &'static str {
355        "DEC2BIN"
356    }
357    fn min_args(&self) -> usize {
358        1
359    }
360    fn variadic(&self) -> bool {
361        true
362    }
363    fn arg_schema(&self) -> &'static [ArgSchema] {
364        &ARG_NUM_LENIENT_TWO[..]
365    }
366    fn eval<'a, 'b, 'c>(
367        &self,
368        args: &'c [ArgumentHandle<'a, 'b>],
369        _ctx: &dyn FunctionContext<'b>,
370    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
371        let n = match args[0].value()?.into_literal() {
372            LiteralValue::Error(e) => {
373                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
374            }
375            other => coerce_num(&other)? as i64,
376        };
377
378        // Excel limits: -512 to 511
379        if !(-512..=511).contains(&n) {
380            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
381                ExcelError::new_num(),
382            )));
383        }
384
385        let places = if args.len() > 1 {
386            match args[1].value()?.into_literal() {
387                LiteralValue::Error(e) => {
388                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
389                }
390                other => Some(coerce_num(&other)? as usize),
391            }
392        } else {
393            None
394        };
395
396        let binary = if n >= 0 {
397            format!("{:b}", n)
398        } else {
399            // Two's complement with 10 bits
400            format!("{:010b}", (n + 1024) as u64)
401        };
402
403        let result = if let Some(p) = places {
404            if p < binary.len() || p > 10 {
405                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
406                    ExcelError::new_num(),
407                )));
408            }
409            format!("{:0>width$}", binary, width = p)
410        } else {
411            binary
412        };
413
414        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
415    }
416}
417
418/// HEX2DEC(number) - Converts hexadecimal to decimal
419#[derive(Debug)]
420pub struct Hex2DecFn;
421impl Function for Hex2DecFn {
422    func_caps!(PURE);
423    fn name(&self) -> &'static str {
424        "HEX2DEC"
425    }
426    fn min_args(&self) -> usize {
427        1
428    }
429    fn arg_schema(&self) -> &'static [ArgSchema] {
430        &ARG_ANY_ONE[..]
431    }
432    fn eval<'a, 'b, 'c>(
433        &self,
434        args: &'c [ArgumentHandle<'a, 'b>],
435        _ctx: &dyn FunctionContext<'b>,
436    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
437        let text = match args[0].value()?.into_literal() {
438            LiteralValue::Error(e) => {
439                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
440            }
441            other => match coerce_base_text(&other) {
442                Ok(s) => s.to_uppercase(),
443                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
444            },
445        };
446
447        // Excel accepts 10-character hex
448        if text.len() > 10 || !text.chars().all(|c| c.is_ascii_hexdigit()) {
449            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
450                ExcelError::new_num(),
451            )));
452        }
453
454        let result = if text.len() == 10 && text.starts_with(|c| c >= '8') {
455            // Negative number in two's complement (40 bits)
456            let val = i64::from_str_radix(&text, 16).unwrap_or(0);
457            val - (1i64 << 40)
458        } else {
459            i64::from_str_radix(&text, 16).unwrap_or(0)
460        };
461
462        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
463            result as f64,
464        )))
465    }
466}
467
468/// DEC2HEX(number, [places]) - Converts decimal to hexadecimal
469#[derive(Debug)]
470pub struct Dec2HexFn;
471impl Function for Dec2HexFn {
472    func_caps!(PURE);
473    fn name(&self) -> &'static str {
474        "DEC2HEX"
475    }
476    fn min_args(&self) -> usize {
477        1
478    }
479    fn variadic(&self) -> bool {
480        true
481    }
482    fn arg_schema(&self) -> &'static [ArgSchema] {
483        &ARG_NUM_LENIENT_TWO[..]
484    }
485    fn eval<'a, 'b, 'c>(
486        &self,
487        args: &'c [ArgumentHandle<'a, 'b>],
488        _ctx: &dyn FunctionContext<'b>,
489    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
490        let n = match args[0].value()?.into_literal() {
491            LiteralValue::Error(e) => {
492                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
493            }
494            other => coerce_num(&other)? as i64,
495        };
496
497        // Excel limits
498        if !(-(1i64 << 39)..=(1i64 << 39) - 1).contains(&n) {
499            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
500                ExcelError::new_num(),
501            )));
502        }
503
504        let places = if args.len() > 1 {
505            match args[1].value()?.into_literal() {
506                LiteralValue::Error(e) => {
507                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
508                }
509                other => Some(coerce_num(&other)? as usize),
510            }
511        } else {
512            None
513        };
514
515        let hex = if n >= 0 {
516            format!("{:X}", n)
517        } else {
518            // Two's complement with 10 hex digits (40 bits)
519            format!("{:010X}", (n + (1i64 << 40)) as u64)
520        };
521
522        let result = if let Some(p) = places {
523            if p < hex.len() || p > 10 {
524                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
525                    ExcelError::new_num(),
526                )));
527            }
528            format!("{:0>width$}", hex, width = p)
529        } else {
530            hex
531        };
532
533        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
534    }
535}
536
537/// OCT2DEC(number) - Converts octal to decimal
538#[derive(Debug)]
539pub struct Oct2DecFn;
540impl Function for Oct2DecFn {
541    func_caps!(PURE);
542    fn name(&self) -> &'static str {
543        "OCT2DEC"
544    }
545    fn min_args(&self) -> usize {
546        1
547    }
548    fn arg_schema(&self) -> &'static [ArgSchema] {
549        &ARG_ANY_ONE[..]
550    }
551    fn eval<'a, 'b, 'c>(
552        &self,
553        args: &'c [ArgumentHandle<'a, 'b>],
554        _ctx: &dyn FunctionContext<'b>,
555    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
556        let text = match args[0].value()?.into_literal() {
557            LiteralValue::Error(e) => {
558                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
559            }
560            other => match coerce_base_text(&other) {
561                Ok(s) => s,
562                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
563            },
564        };
565
566        // Excel accepts 10-character octal (30 bits)
567        if text.len() > 10 || !text.chars().all(|c| ('0'..='7').contains(&c)) {
568            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
569                ExcelError::new_num(),
570            )));
571        }
572
573        let result = if text.len() == 10 && text.starts_with(|c| c >= '4') {
574            // Negative number in two's complement (30 bits)
575            let val = i64::from_str_radix(&text, 8).unwrap_or(0);
576            val - (1i64 << 30)
577        } else {
578            i64::from_str_radix(&text, 8).unwrap_or(0)
579        };
580
581        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
582            result as f64,
583        )))
584    }
585}
586
587/// DEC2OCT(number, [places]) - Converts decimal to octal
588#[derive(Debug)]
589pub struct Dec2OctFn;
590impl Function for Dec2OctFn {
591    func_caps!(PURE);
592    fn name(&self) -> &'static str {
593        "DEC2OCT"
594    }
595    fn min_args(&self) -> usize {
596        1
597    }
598    fn variadic(&self) -> bool {
599        true
600    }
601    fn arg_schema(&self) -> &'static [ArgSchema] {
602        &ARG_NUM_LENIENT_TWO[..]
603    }
604    fn eval<'a, 'b, 'c>(
605        &self,
606        args: &'c [ArgumentHandle<'a, 'b>],
607        _ctx: &dyn FunctionContext<'b>,
608    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
609        let n = match args[0].value()?.into_literal() {
610            LiteralValue::Error(e) => {
611                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
612            }
613            other => coerce_num(&other)? as i64,
614        };
615
616        // Excel limits: -536870912 to 536870911
617        if !(-(1i64 << 29)..=(1i64 << 29) - 1).contains(&n) {
618            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
619                ExcelError::new_num(),
620            )));
621        }
622
623        let places = if args.len() > 1 {
624            match args[1].value()?.into_literal() {
625                LiteralValue::Error(e) => {
626                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
627                }
628                other => Some(coerce_num(&other)? as usize),
629            }
630        } else {
631            None
632        };
633
634        let octal = if n >= 0 {
635            format!("{:o}", n)
636        } else {
637            // Two's complement with 10 octal digits (30 bits)
638            format!("{:010o}", (n + (1i64 << 30)) as u64)
639        };
640
641        let result = if let Some(p) = places {
642            if p < octal.len() || p > 10 {
643                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
644                    ExcelError::new_num(),
645                )));
646            }
647            format!("{:0>width$}", octal, width = p)
648        } else {
649            octal
650        };
651
652        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
653    }
654}
655
656/* ─────────────────────────── Cross-Base Conversions ──────────────────────────── */
657
658/// BIN2HEX(number, [places]) - Converts binary to hexadecimal
659#[derive(Debug)]
660pub struct Bin2HexFn;
661impl Function for Bin2HexFn {
662    func_caps!(PURE);
663    fn name(&self) -> &'static str {
664        "BIN2HEX"
665    }
666    fn min_args(&self) -> usize {
667        1
668    }
669    fn variadic(&self) -> bool {
670        true
671    }
672    fn arg_schema(&self) -> &'static [ArgSchema] {
673        &ARG_NUM_LENIENT_TWO[..]
674    }
675    fn eval<'a, 'b, 'c>(
676        &self,
677        args: &'c [ArgumentHandle<'a, 'b>],
678        _ctx: &dyn FunctionContext<'b>,
679    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
680        let text = match args[0].value()?.into_literal() {
681            LiteralValue::Error(e) => {
682                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
683            }
684            other => match coerce_base_text(&other) {
685                Ok(s) => s,
686                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
687            },
688        };
689
690        if text.len() > 10 || !text.chars().all(|c| c == '0' || c == '1') {
691            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
692                ExcelError::new_num(),
693            )));
694        }
695
696        // Convert binary to decimal first
697        let dec = if text.len() == 10 && text.starts_with('1') {
698            let val = i64::from_str_radix(&text, 2).unwrap_or(0);
699            val - 1024
700        } else {
701            i64::from_str_radix(&text, 2).unwrap_or(0)
702        };
703
704        let places = if args.len() > 1 {
705            match args[1].value()?.into_literal() {
706                LiteralValue::Error(e) => {
707                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
708                }
709                other => Some(coerce_num(&other)? as usize),
710            }
711        } else {
712            None
713        };
714
715        let hex = if dec >= 0 {
716            format!("{:X}", dec)
717        } else {
718            format!("{:010X}", (dec + (1i64 << 40)) as u64)
719        };
720
721        let result = if let Some(p) = places {
722            if p < hex.len() || p > 10 {
723                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
724                    ExcelError::new_num(),
725                )));
726            }
727            format!("{:0>width$}", hex, width = p)
728        } else {
729            hex
730        };
731
732        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
733    }
734}
735
736/// HEX2BIN(number, [places]) - Converts hexadecimal to binary
737#[derive(Debug)]
738pub struct Hex2BinFn;
739impl Function for Hex2BinFn {
740    func_caps!(PURE);
741    fn name(&self) -> &'static str {
742        "HEX2BIN"
743    }
744    fn min_args(&self) -> usize {
745        1
746    }
747    fn variadic(&self) -> bool {
748        true
749    }
750    fn arg_schema(&self) -> &'static [ArgSchema] {
751        &ARG_ANY_TWO[..]
752    }
753    fn eval<'a, 'b, 'c>(
754        &self,
755        args: &'c [ArgumentHandle<'a, 'b>],
756        _ctx: &dyn FunctionContext<'b>,
757    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
758        let text = match args[0].value()?.into_literal() {
759            LiteralValue::Error(e) => {
760                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
761            }
762            other => match coerce_base_text(&other) {
763                Ok(s) => s.to_uppercase(),
764                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
765            },
766        };
767
768        if text.len() > 10 || !text.chars().all(|c| c.is_ascii_hexdigit()) {
769            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
770                ExcelError::new_num(),
771            )));
772        }
773
774        // Convert hex to decimal first
775        let dec = if text.len() == 10 && text.starts_with(|c| c >= '8') {
776            let val = i64::from_str_radix(&text, 16).unwrap_or(0);
777            val - (1i64 << 40)
778        } else {
779            i64::from_str_radix(&text, 16).unwrap_or(0)
780        };
781
782        // Check range for binary output (-512 to 511)
783        if !(-512..=511).contains(&dec) {
784            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
785                ExcelError::new_num(),
786            )));
787        }
788
789        let places = if args.len() > 1 {
790            match args[1].value()?.into_literal() {
791                LiteralValue::Error(e) => {
792                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
793                }
794                other => Some(coerce_num(&other)? as usize),
795            }
796        } else {
797            None
798        };
799
800        let binary = if dec >= 0 {
801            format!("{:b}", dec)
802        } else {
803            format!("{:010b}", (dec + 1024) as u64)
804        };
805
806        let result = if let Some(p) = places {
807            if p < binary.len() || p > 10 {
808                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
809                    ExcelError::new_num(),
810                )));
811            }
812            format!("{:0>width$}", binary, width = p)
813        } else {
814            binary
815        };
816
817        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
818    }
819}
820
821/// BIN2OCT(number, [places]) - Converts binary to octal
822#[derive(Debug)]
823pub struct Bin2OctFn;
824impl Function for Bin2OctFn {
825    func_caps!(PURE);
826    fn name(&self) -> &'static str {
827        "BIN2OCT"
828    }
829    fn min_args(&self) -> usize {
830        1
831    }
832    fn variadic(&self) -> bool {
833        true
834    }
835    fn arg_schema(&self) -> &'static [ArgSchema] {
836        &ARG_NUM_LENIENT_TWO[..]
837    }
838    fn eval<'a, 'b, 'c>(
839        &self,
840        args: &'c [ArgumentHandle<'a, 'b>],
841        _ctx: &dyn FunctionContext<'b>,
842    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
843        let text = match args[0].value()?.into_literal() {
844            LiteralValue::Error(e) => {
845                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
846            }
847            other => match coerce_base_text(&other) {
848                Ok(s) => s,
849                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
850            },
851        };
852
853        if text.len() > 10 || !text.chars().all(|c| c == '0' || c == '1') {
854            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
855                ExcelError::new_num(),
856            )));
857        }
858
859        let dec = if text.len() == 10 && text.starts_with('1') {
860            let val = i64::from_str_radix(&text, 2).unwrap_or(0);
861            val - 1024
862        } else {
863            i64::from_str_radix(&text, 2).unwrap_or(0)
864        };
865
866        let places = if args.len() > 1 {
867            match args[1].value()?.into_literal() {
868                LiteralValue::Error(e) => {
869                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
870                }
871                other => Some(coerce_num(&other)? as usize),
872            }
873        } else {
874            None
875        };
876
877        let octal = if dec >= 0 {
878            format!("{:o}", dec)
879        } else {
880            format!("{:010o}", (dec + (1i64 << 30)) as u64)
881        };
882
883        let result = if let Some(p) = places {
884            if p < octal.len() || p > 10 {
885                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
886                    ExcelError::new_num(),
887                )));
888            }
889            format!("{:0>width$}", octal, width = p)
890        } else {
891            octal
892        };
893
894        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
895    }
896}
897
898/// OCT2BIN(number, [places]) - Converts octal to binary
899#[derive(Debug)]
900pub struct Oct2BinFn;
901impl Function for Oct2BinFn {
902    func_caps!(PURE);
903    fn name(&self) -> &'static str {
904        "OCT2BIN"
905    }
906    fn min_args(&self) -> usize {
907        1
908    }
909    fn variadic(&self) -> bool {
910        true
911    }
912    fn arg_schema(&self) -> &'static [ArgSchema] {
913        &ARG_NUM_LENIENT_TWO[..]
914    }
915    fn eval<'a, 'b, 'c>(
916        &self,
917        args: &'c [ArgumentHandle<'a, 'b>],
918        _ctx: &dyn FunctionContext<'b>,
919    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
920        let text = match args[0].value()?.into_literal() {
921            LiteralValue::Error(e) => {
922                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
923            }
924            other => match coerce_base_text(&other) {
925                Ok(s) => s,
926                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
927            },
928        };
929
930        if text.len() > 10 || !text.chars().all(|c| ('0'..='7').contains(&c)) {
931            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
932                ExcelError::new_num(),
933            )));
934        }
935
936        let dec = if text.len() == 10 && text.starts_with(|c| c >= '4') {
937            let val = i64::from_str_radix(&text, 8).unwrap_or(0);
938            val - (1i64 << 30)
939        } else {
940            i64::from_str_radix(&text, 8).unwrap_or(0)
941        };
942
943        // Check range for binary output (-512 to 511)
944        if !(-512..=511).contains(&dec) {
945            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
946                ExcelError::new_num(),
947            )));
948        }
949
950        let places = if args.len() > 1 {
951            match args[1].value()?.into_literal() {
952                LiteralValue::Error(e) => {
953                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
954                }
955                other => Some(coerce_num(&other)? as usize),
956            }
957        } else {
958            None
959        };
960
961        let binary = if dec >= 0 {
962            format!("{:b}", dec)
963        } else {
964            format!("{:010b}", (dec + 1024) as u64)
965        };
966
967        let result = if let Some(p) = places {
968            if p < binary.len() || p > 10 {
969                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
970                    ExcelError::new_num(),
971                )));
972            }
973            format!("{:0>width$}", binary, width = p)
974        } else {
975            binary
976        };
977
978        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
979    }
980}
981
982/// HEX2OCT(number, [places]) - Converts hexadecimal to octal
983#[derive(Debug)]
984pub struct Hex2OctFn;
985impl Function for Hex2OctFn {
986    func_caps!(PURE);
987    fn name(&self) -> &'static str {
988        "HEX2OCT"
989    }
990    fn min_args(&self) -> usize {
991        1
992    }
993    fn variadic(&self) -> bool {
994        true
995    }
996    fn arg_schema(&self) -> &'static [ArgSchema] {
997        &ARG_ANY_TWO[..]
998    }
999    fn eval<'a, 'b, 'c>(
1000        &self,
1001        args: &'c [ArgumentHandle<'a, 'b>],
1002        _ctx: &dyn FunctionContext<'b>,
1003    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1004        let text = match args[0].value()?.into_literal() {
1005            LiteralValue::Error(e) => {
1006                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1007            }
1008            other => match coerce_base_text(&other) {
1009                Ok(s) => s.to_uppercase(),
1010                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1011            },
1012        };
1013
1014        if text.len() > 10 || !text.chars().all(|c| c.is_ascii_hexdigit()) {
1015            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1016                ExcelError::new_num(),
1017            )));
1018        }
1019
1020        let dec = if text.len() == 10 && text.starts_with(|c| c >= '8') {
1021            let val = i64::from_str_radix(&text, 16).unwrap_or(0);
1022            val - (1i64 << 40)
1023        } else {
1024            i64::from_str_radix(&text, 16).unwrap_or(0)
1025        };
1026
1027        // Check range for octal output
1028        if !(-(1i64 << 29)..=(1i64 << 29) - 1).contains(&dec) {
1029            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1030                ExcelError::new_num(),
1031            )));
1032        }
1033
1034        let places = if args.len() > 1 {
1035            match args[1].value()?.into_literal() {
1036                LiteralValue::Error(e) => {
1037                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1038                }
1039                other => Some(coerce_num(&other)? as usize),
1040            }
1041        } else {
1042            None
1043        };
1044
1045        let octal = if dec >= 0 {
1046            format!("{:o}", dec)
1047        } else {
1048            format!("{:010o}", (dec + (1i64 << 30)) as u64)
1049        };
1050
1051        let result = if let Some(p) = places {
1052            if p < octal.len() || p > 10 {
1053                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1054                    ExcelError::new_num(),
1055                )));
1056            }
1057            format!("{:0>width$}", octal, width = p)
1058        } else {
1059            octal
1060        };
1061
1062        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1063    }
1064}
1065
1066/// OCT2HEX(number, [places]) - Converts octal to hexadecimal
1067#[derive(Debug)]
1068pub struct Oct2HexFn;
1069impl Function for Oct2HexFn {
1070    func_caps!(PURE);
1071    fn name(&self) -> &'static str {
1072        "OCT2HEX"
1073    }
1074    fn min_args(&self) -> usize {
1075        1
1076    }
1077    fn variadic(&self) -> bool {
1078        true
1079    }
1080    fn arg_schema(&self) -> &'static [ArgSchema] {
1081        &ARG_NUM_LENIENT_TWO[..]
1082    }
1083    fn eval<'a, 'b, 'c>(
1084        &self,
1085        args: &'c [ArgumentHandle<'a, 'b>],
1086        _ctx: &dyn FunctionContext<'b>,
1087    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1088        let text = match args[0].value()?.into_literal() {
1089            LiteralValue::Error(e) => {
1090                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1091            }
1092            other => match coerce_base_text(&other) {
1093                Ok(s) => s,
1094                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1095            },
1096        };
1097
1098        if text.len() > 10 || !text.chars().all(|c| ('0'..='7').contains(&c)) {
1099            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1100                ExcelError::new_num(),
1101            )));
1102        }
1103
1104        let dec = if text.len() == 10 && text.starts_with(|c| c >= '4') {
1105            let val = i64::from_str_radix(&text, 8).unwrap_or(0);
1106            val - (1i64 << 30)
1107        } else {
1108            i64::from_str_radix(&text, 8).unwrap_or(0)
1109        };
1110
1111        let places = if args.len() > 1 {
1112            match args[1].value()?.into_literal() {
1113                LiteralValue::Error(e) => {
1114                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1115                }
1116                other => Some(coerce_num(&other)? as usize),
1117            }
1118        } else {
1119            None
1120        };
1121
1122        let hex = if dec >= 0 {
1123            format!("{:X}", dec)
1124        } else {
1125            format!("{:010X}", (dec + (1i64 << 40)) as u64)
1126        };
1127
1128        let result = if let Some(p) = places {
1129            if p < hex.len() || p > 10 {
1130                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1131                    ExcelError::new_num(),
1132                )));
1133            }
1134            format!("{:0>width$}", hex, width = p)
1135        } else {
1136            hex
1137        };
1138
1139        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1140    }
1141}
1142
1143/* ─────────────────────────── Engineering Comparison Functions ──────────────────────────── */
1144
1145/// DELTA(number1, [number2]) - Tests whether two values are equal
1146#[derive(Debug)]
1147pub struct DeltaFn;
1148impl Function for DeltaFn {
1149    func_caps!(PURE);
1150    fn name(&self) -> &'static str {
1151        "DELTA"
1152    }
1153    fn min_args(&self) -> usize {
1154        1
1155    }
1156    fn variadic(&self) -> bool {
1157        true
1158    }
1159    fn arg_schema(&self) -> &'static [ArgSchema] {
1160        &ARG_NUM_LENIENT_TWO[..]
1161    }
1162    fn eval<'a, 'b, 'c>(
1163        &self,
1164        args: &'c [ArgumentHandle<'a, 'b>],
1165        _ctx: &dyn FunctionContext<'b>,
1166    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1167        let n1 = match args[0].value()?.into_literal() {
1168            LiteralValue::Error(e) => {
1169                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1170            }
1171            other => coerce_num(&other)?,
1172        };
1173        let n2 = if args.len() > 1 {
1174            match args[1].value()?.into_literal() {
1175                LiteralValue::Error(e) => {
1176                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1177                }
1178                other => coerce_num(&other)?,
1179            }
1180        } else {
1181            0.0
1182        };
1183
1184        let result = if (n1 - n2).abs() < 1e-12 { 1.0 } else { 0.0 };
1185        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1186            result,
1187        )))
1188    }
1189}
1190
1191/// GESTEP(number, [step]) - Tests whether a number is >= step value
1192#[derive(Debug)]
1193pub struct GestepFn;
1194impl Function for GestepFn {
1195    func_caps!(PURE);
1196    fn name(&self) -> &'static str {
1197        "GESTEP"
1198    }
1199    fn min_args(&self) -> usize {
1200        1
1201    }
1202    fn variadic(&self) -> bool {
1203        true
1204    }
1205    fn arg_schema(&self) -> &'static [ArgSchema] {
1206        &ARG_NUM_LENIENT_TWO[..]
1207    }
1208    fn eval<'a, 'b, 'c>(
1209        &self,
1210        args: &'c [ArgumentHandle<'a, 'b>],
1211        _ctx: &dyn FunctionContext<'b>,
1212    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1213        let n = match args[0].value()?.into_literal() {
1214            LiteralValue::Error(e) => {
1215                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1216            }
1217            other => coerce_num(&other)?,
1218        };
1219        let step = if args.len() > 1 {
1220            match args[1].value()?.into_literal() {
1221                LiteralValue::Error(e) => {
1222                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1223                }
1224                other => coerce_num(&other)?,
1225            }
1226        } else {
1227            0.0
1228        };
1229
1230        let result = if n >= step { 1.0 } else { 0.0 };
1231        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1232            result,
1233        )))
1234    }
1235}
1236
1237/* ─────────────────────────── Error Function ──────────────────────────── */
1238
1239/// Approximation of the error function erf(x)
1240/// Uses the approximation: erf(x) = 1 - (a1*t + a2*t^2 + a3*t^3 + a4*t^4 + a5*t^5) * exp(-x^2)
1241/// High-precision error function using Cody's rational approximation
1242/// Achieves precision of about 1e-15 (double precision)
1243#[allow(clippy::excessive_precision)]
1244fn erf_approx(x: f64) -> f64 {
1245    let ax = x.abs();
1246
1247    // For small x, use series expansion
1248    if ax < 0.5 {
1249        // Coefficients for erf(x) = x * P(x^2) / Q(x^2)
1250        const P: [f64; 5] = [
1251            3.20937758913846947e+03,
1252            3.77485237685302021e+02,
1253            1.13864154151050156e+02,
1254            3.16112374387056560e+00,
1255            1.85777706184603153e-01,
1256        ];
1257        const Q: [f64; 5] = [
1258            2.84423748127893300e+03,
1259            1.28261652607737228e+03,
1260            2.44024637934444173e+02,
1261            2.36012909523441209e+01,
1262            1.00000000000000000e+00,
1263        ];
1264
1265        let x2 = x * x;
1266        let p_val = P[4];
1267        let p_val = p_val * x2 + P[3];
1268        let p_val = p_val * x2 + P[2];
1269        let p_val = p_val * x2 + P[1];
1270        let p_val = p_val * x2 + P[0];
1271
1272        let q_val = Q[4];
1273        let q_val = q_val * x2 + Q[3];
1274        let q_val = q_val * x2 + Q[2];
1275        let q_val = q_val * x2 + Q[1];
1276        let q_val = q_val * x2 + Q[0];
1277
1278        return x * p_val / q_val;
1279    }
1280
1281    // For x in [0.5, 4], use erfc approximation and compute erf = 1 - erfc
1282    if ax < 4.0 {
1283        let erfc_val = erfc_mid(ax);
1284        return if x > 0.0 {
1285            1.0 - erfc_val
1286        } else {
1287            erfc_val - 1.0
1288        };
1289    }
1290
1291    // For large x, erf(x) ≈ ±1
1292    let erfc_val = erfc_large(ax);
1293    if x > 0.0 {
1294        1.0 - erfc_val
1295    } else {
1296        erfc_val - 1.0
1297    }
1298}
1299
1300/// erfc for x in [0.5, 4]
1301#[allow(clippy::excessive_precision)]
1302fn erfc_mid(x: f64) -> f64 {
1303    const P: [f64; 9] = [
1304        1.23033935479799725e+03,
1305        2.05107837782607147e+03,
1306        1.71204761263407058e+03,
1307        8.81952221241769090e+02,
1308        2.98635138197400131e+02,
1309        6.61191906371416295e+01,
1310        8.88314979438837594e+00,
1311        5.64188496988670089e-01,
1312        2.15311535474403846e-08,
1313    ];
1314    const Q: [f64; 9] = [
1315        1.23033935480374942e+03,
1316        3.43936767414372164e+03,
1317        4.36261909014324716e+03,
1318        3.29079923573345963e+03,
1319        1.62138957456669019e+03,
1320        5.37181101862009858e+02,
1321        1.17693950891312499e+02,
1322        1.57449261107098347e+01,
1323        1.00000000000000000e+00,
1324    ];
1325
1326    let p_val = P[8];
1327    let p_val = p_val * x + P[7];
1328    let p_val = p_val * x + P[6];
1329    let p_val = p_val * x + P[5];
1330    let p_val = p_val * x + P[4];
1331    let p_val = p_val * x + P[3];
1332    let p_val = p_val * x + P[2];
1333    let p_val = p_val * x + P[1];
1334    let p_val = p_val * x + P[0];
1335
1336    let q_val = Q[8];
1337    let q_val = q_val * x + Q[7];
1338    let q_val = q_val * x + Q[6];
1339    let q_val = q_val * x + Q[5];
1340    let q_val = q_val * x + Q[4];
1341    let q_val = q_val * x + Q[3];
1342    let q_val = q_val * x + Q[2];
1343    let q_val = q_val * x + Q[1];
1344    let q_val = q_val * x + Q[0];
1345
1346    (-x * x).exp() * p_val / q_val
1347}
1348
1349/// erfc for x >= 4
1350#[allow(clippy::excessive_precision)]
1351fn erfc_large(x: f64) -> f64 {
1352    const P: [f64; 6] = [
1353        6.58749161529837803e-04,
1354        1.60837851487422766e-02,
1355        1.25781726111229246e-01,
1356        3.60344899949804439e-01,
1357        3.05326634961232344e-01,
1358        1.63153871373020978e-02,
1359    ];
1360    const Q: [f64; 6] = [
1361        2.33520497626869185e-03,
1362        6.05183413124413191e-02,
1363        5.27905102951428412e-01,
1364        1.87295284992346047e+00,
1365        2.56852019228982242e+00,
1366        1.00000000000000000e+00,
1367    ];
1368
1369    let x2 = x * x;
1370    let inv_x2 = 1.0 / x2;
1371
1372    let p_val = P[5];
1373    let p_val = p_val * inv_x2 + P[4];
1374    let p_val = p_val * inv_x2 + P[3];
1375    let p_val = p_val * inv_x2 + P[2];
1376    let p_val = p_val * inv_x2 + P[1];
1377    let p_val = p_val * inv_x2 + P[0];
1378
1379    let q_val = Q[5];
1380    let q_val = q_val * inv_x2 + Q[4];
1381    let q_val = q_val * inv_x2 + Q[3];
1382    let q_val = q_val * inv_x2 + Q[2];
1383    let q_val = q_val * inv_x2 + Q[1];
1384    let q_val = q_val * inv_x2 + Q[0];
1385
1386    // 1/sqrt(pi) = 0.5641895835477563
1387    const FRAC_1_SQRT_PI: f64 = 0.5641895835477563;
1388    (-x2).exp() / x * (FRAC_1_SQRT_PI + inv_x2 * p_val / q_val)
1389}
1390
1391/// Direct erfc computation for ERFC function
1392fn erfc_direct(x: f64) -> f64 {
1393    if x < 0.0 {
1394        return 2.0 - erfc_direct(-x);
1395    }
1396    if x < 0.5 {
1397        return 1.0 - erf_approx(x);
1398    }
1399    if x < 4.0 {
1400        return erfc_mid(x);
1401    }
1402    erfc_large(x)
1403}
1404
1405/// ERF(lower_limit, [upper_limit]) - Returns the error function integrated between lower_limit and upper_limit
1406/// If only lower_limit is provided, returns erf(lower_limit)
1407/// If both are provided, returns erf(upper_limit) - erf(lower_limit)
1408#[derive(Debug)]
1409pub struct ErfFn;
1410impl Function for ErfFn {
1411    func_caps!(PURE);
1412    fn name(&self) -> &'static str {
1413        "ERF"
1414    }
1415    fn min_args(&self) -> usize {
1416        1
1417    }
1418    fn variadic(&self) -> bool {
1419        true
1420    }
1421    fn arg_schema(&self) -> &'static [ArgSchema] {
1422        &ARG_NUM_LENIENT_TWO[..]
1423    }
1424    fn eval<'a, 'b, 'c>(
1425        &self,
1426        args: &'c [ArgumentHandle<'a, 'b>],
1427        _ctx: &dyn FunctionContext<'b>,
1428    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1429        let lower = match args[0].value()?.into_literal() {
1430            LiteralValue::Error(e) => {
1431                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1432            }
1433            other => coerce_num(&other)?,
1434        };
1435
1436        let result = if args.len() > 1 {
1437            // ERF(lower, upper) = erf(upper) - erf(lower)
1438            let upper = match args[1].value()?.into_literal() {
1439                LiteralValue::Error(e) => {
1440                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1441                }
1442                other => coerce_num(&other)?,
1443            };
1444            erf_approx(upper) - erf_approx(lower)
1445        } else {
1446            // ERF(x) = erf(x)
1447            erf_approx(lower)
1448        };
1449
1450        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1451            result,
1452        )))
1453    }
1454}
1455
1456/// ERFC(x) - Returns the complementary error function = 1 - erf(x)
1457#[derive(Debug)]
1458pub struct ErfcFn;
1459impl Function for ErfcFn {
1460    func_caps!(PURE);
1461    fn name(&self) -> &'static str {
1462        "ERFC"
1463    }
1464    fn min_args(&self) -> usize {
1465        1
1466    }
1467    fn arg_schema(&self) -> &'static [ArgSchema] {
1468        &ARG_ANY_ONE[..]
1469    }
1470    fn eval<'a, 'b, 'c>(
1471        &self,
1472        args: &'c [ArgumentHandle<'a, 'b>],
1473        _ctx: &dyn FunctionContext<'b>,
1474    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1475        let x = match args[0].value()?.into_literal() {
1476            LiteralValue::Error(e) => {
1477                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1478            }
1479            other => coerce_num(&other)?,
1480        };
1481
1482        let result = erfc_direct(x);
1483        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1484            result,
1485        )))
1486    }
1487}
1488
1489/// ERF.PRECISE(x) - Returns the error function (same as ERF with one argument)
1490#[derive(Debug)]
1491pub struct ErfPreciseFn;
1492impl Function for ErfPreciseFn {
1493    func_caps!(PURE);
1494    fn name(&self) -> &'static str {
1495        "ERF.PRECISE"
1496    }
1497    fn min_args(&self) -> usize {
1498        1
1499    }
1500    fn arg_schema(&self) -> &'static [ArgSchema] {
1501        &ARG_ANY_ONE[..]
1502    }
1503    fn eval<'a, 'b, 'c>(
1504        &self,
1505        args: &'c [ArgumentHandle<'a, 'b>],
1506        _ctx: &dyn FunctionContext<'b>,
1507    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1508        let x = match args[0].value()?.into_literal() {
1509            LiteralValue::Error(e) => {
1510                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1511            }
1512            other => coerce_num(&other)?,
1513        };
1514
1515        let result = erf_approx(x);
1516        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1517            result,
1518        )))
1519    }
1520}
1521
1522/* ─────────────────────────── Complex Number Functions ──────────────────────────── */
1523
1524/// Parse a complex number string like "3+4i", "3-4i", "5i", "3", "-2j", etc.
1525/// Returns (real, imaginary, suffix) where suffix is 'i' or 'j'
1526fn parse_complex(s: &str) -> Result<(f64, f64, char), ExcelError> {
1527    let s = s.trim();
1528    if s.is_empty() {
1529        return Err(ExcelError::new_num());
1530    }
1531
1532    // Determine the suffix (i or j)
1533    let suffix = if s.ends_with('i') || s.ends_with('I') {
1534        'i'
1535    } else if s.ends_with('j') || s.ends_with('J') {
1536        'j'
1537    } else {
1538        // No imaginary suffix - must be purely real
1539        let real: f64 = s.parse().map_err(|_| ExcelError::new_num())?;
1540        return Ok((real, 0.0, 'i'));
1541    };
1542
1543    // Remove the suffix for parsing
1544    let s = &s[..s.len() - 1];
1545
1546    // Handle pure imaginary cases like "i", "-i", "4i"
1547    if s.is_empty() || s == "+" {
1548        return Ok((0.0, 1.0, suffix));
1549    }
1550    if s == "-" {
1551        return Ok((0.0, -1.0, suffix));
1552    }
1553
1554    // Find the last + or - that separates real and imaginary parts
1555    // We need to skip the first character (could be a sign) and find operators
1556    let mut split_pos = None;
1557    let bytes = s.as_bytes();
1558
1559    for i in (1..bytes.len()).rev() {
1560        let c = bytes[i] as char;
1561        if c == '+' || c == '-' {
1562            // Make sure this isn't part of an exponent (e.g., "1e-5")
1563            if i > 0 {
1564                let prev = bytes[i - 1] as char;
1565                if prev == 'e' || prev == 'E' {
1566                    continue;
1567                }
1568            }
1569            split_pos = Some(i);
1570            break;
1571        }
1572    }
1573
1574    match split_pos {
1575        Some(pos) => {
1576            // We have both real and imaginary parts
1577            let real_str = &s[..pos];
1578            let imag_str = &s[pos..];
1579
1580            let real: f64 = if real_str.is_empty() {
1581                0.0
1582            } else {
1583                real_str.parse().map_err(|_| ExcelError::new_num())?
1584            };
1585
1586            // Handle imaginary part (could be "+", "-", "+5", "-5", etc.)
1587            let imag: f64 = if imag_str == "+" {
1588                1.0
1589            } else if imag_str == "-" {
1590                -1.0
1591            } else {
1592                imag_str.parse().map_err(|_| ExcelError::new_num())?
1593            };
1594
1595            Ok((real, imag, suffix))
1596        }
1597        None => {
1598            // Pure imaginary number (no real part), e.g., "5" (before suffix was removed)
1599            let imag: f64 = s.parse().map_err(|_| ExcelError::new_num())?;
1600            Ok((0.0, imag, suffix))
1601        }
1602    }
1603}
1604
1605/// Clean up floating point noise by rounding values very close to integers
1606fn clean_float(val: f64) -> f64 {
1607    let rounded = val.round();
1608    if (val - rounded).abs() < 1e-10 {
1609        rounded
1610    } else {
1611        val
1612    }
1613}
1614
1615/// Format a complex number as a string
1616fn format_complex(real: f64, imag: f64, suffix: char) -> String {
1617    // Clean up floating point noise
1618    let real = clean_float(real);
1619    let imag = clean_float(imag);
1620
1621    // Handle special cases for cleaner output
1622    let real_is_zero = real.abs() < 1e-15;
1623    let imag_is_zero = imag.abs() < 1e-15;
1624
1625    if real_is_zero && imag_is_zero {
1626        return "0".to_string();
1627    }
1628
1629    if imag_is_zero {
1630        // Purely real
1631        if real == real.trunc() && real.abs() < 1e15 {
1632            return format!("{}", real as i64);
1633        }
1634        return format!("{}", real);
1635    }
1636
1637    if real_is_zero {
1638        // Purely imaginary
1639        if (imag - 1.0).abs() < 1e-15 {
1640            return format!("{}", suffix);
1641        }
1642        if (imag + 1.0).abs() < 1e-15 {
1643            return format!("-{}", suffix);
1644        }
1645        if imag == imag.trunc() && imag.abs() < 1e15 {
1646            return format!("{}{}", imag as i64, suffix);
1647        }
1648        return format!("{}{}", imag, suffix);
1649    }
1650
1651    // Both parts are non-zero
1652    let real_str = if real == real.trunc() && real.abs() < 1e15 {
1653        format!("{}", real as i64)
1654    } else {
1655        format!("{}", real)
1656    };
1657
1658    let imag_str = if (imag - 1.0).abs() < 1e-15 {
1659        format!("+{}", suffix)
1660    } else if (imag + 1.0).abs() < 1e-15 {
1661        format!("-{}", suffix)
1662    } else if imag > 0.0 {
1663        if imag == imag.trunc() && imag.abs() < 1e15 {
1664            format!("+{}{}", imag as i64, suffix)
1665        } else {
1666            format!("+{}{}", imag, suffix)
1667        }
1668    } else if imag == imag.trunc() && imag.abs() < 1e15 {
1669        format!("{}{}", imag as i64, suffix)
1670    } else {
1671        format!("{}{}", imag, suffix)
1672    };
1673
1674    format!("{}{}", real_str, imag_str)
1675}
1676
1677/// Coerce a LiteralValue to a complex number string
1678fn coerce_complex_str(v: &LiteralValue) -> Result<String, ExcelError> {
1679    match v {
1680        LiteralValue::Text(s) => Ok(s.clone()),
1681        LiteralValue::Int(i) => Ok(i.to_string()),
1682        LiteralValue::Number(n) => Ok(n.to_string()),
1683        LiteralValue::Error(e) => Err(e.clone()),
1684        _ => Err(ExcelError::new_value()),
1685    }
1686}
1687
1688/// Three-argument schema for COMPLEX function
1689static ARG_COMPLEX_THREE: std::sync::LazyLock<Vec<ArgSchema>> =
1690    std::sync::LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::any(), ArgSchema::any()]);
1691
1692/// COMPLEX(real_num, i_num, [suffix]) - Converts real and imaginary coefficients into a complex number
1693#[derive(Debug)]
1694pub struct ComplexFn;
1695impl Function for ComplexFn {
1696    func_caps!(PURE);
1697    fn name(&self) -> &'static str {
1698        "COMPLEX"
1699    }
1700    fn min_args(&self) -> usize {
1701        2
1702    }
1703    fn variadic(&self) -> bool {
1704        true
1705    }
1706    fn arg_schema(&self) -> &'static [ArgSchema] {
1707        &ARG_COMPLEX_THREE[..]
1708    }
1709    fn eval<'a, 'b, 'c>(
1710        &self,
1711        args: &'c [ArgumentHandle<'a, 'b>],
1712        _ctx: &dyn FunctionContext<'b>,
1713    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1714        let real = match args[0].value()?.into_literal() {
1715            LiteralValue::Error(e) => {
1716                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1717            }
1718            other => coerce_num(&other)?,
1719        };
1720
1721        let imag = match args[1].value()?.into_literal() {
1722            LiteralValue::Error(e) => {
1723                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1724            }
1725            other => coerce_num(&other)?,
1726        };
1727
1728        let suffix = if args.len() > 2 {
1729            match args[2].value()?.into_literal() {
1730                LiteralValue::Error(e) => {
1731                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1732                }
1733                LiteralValue::Text(s) => {
1734                    let s = s.to_lowercase();
1735                    if s == "i" {
1736                        'i'
1737                    } else if s == "j" {
1738                        'j'
1739                    } else if s.is_empty() {
1740                        'i' // Default to 'i' for empty string
1741                    } else {
1742                        return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1743                            ExcelError::new_value(),
1744                        )));
1745                    }
1746                }
1747                LiteralValue::Empty => 'i',
1748                _ => {
1749                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1750                        ExcelError::new_value(),
1751                    )));
1752                }
1753            }
1754        } else {
1755            'i'
1756        };
1757
1758        let result = format_complex(real, imag, suffix);
1759        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1760    }
1761}
1762
1763/// IMREAL(inumber) - Returns the real coefficient of a complex number
1764#[derive(Debug)]
1765pub struct ImRealFn;
1766impl Function for ImRealFn {
1767    func_caps!(PURE);
1768    fn name(&self) -> &'static str {
1769        "IMREAL"
1770    }
1771    fn min_args(&self) -> usize {
1772        1
1773    }
1774    fn arg_schema(&self) -> &'static [ArgSchema] {
1775        &ARG_ANY_ONE[..]
1776    }
1777    fn eval<'a, 'b, 'c>(
1778        &self,
1779        args: &'c [ArgumentHandle<'a, 'b>],
1780        _ctx: &dyn FunctionContext<'b>,
1781    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1782        let inumber = match args[0].value()?.into_literal() {
1783            LiteralValue::Error(e) => {
1784                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1785            }
1786            other => match coerce_complex_str(&other) {
1787                Ok(s) => s,
1788                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1789            },
1790        };
1791
1792        let (real, _, _) = match parse_complex(&inumber) {
1793            Ok(c) => c,
1794            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1795        };
1796
1797        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(real)))
1798    }
1799}
1800
1801/// IMAGINARY(inumber) - Returns the imaginary coefficient of a complex number
1802#[derive(Debug)]
1803pub struct ImaginaryFn;
1804impl Function for ImaginaryFn {
1805    func_caps!(PURE);
1806    fn name(&self) -> &'static str {
1807        "IMAGINARY"
1808    }
1809    fn min_args(&self) -> usize {
1810        1
1811    }
1812    fn arg_schema(&self) -> &'static [ArgSchema] {
1813        &ARG_ANY_ONE[..]
1814    }
1815    fn eval<'a, 'b, 'c>(
1816        &self,
1817        args: &'c [ArgumentHandle<'a, 'b>],
1818        _ctx: &dyn FunctionContext<'b>,
1819    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1820        let inumber = match args[0].value()?.into_literal() {
1821            LiteralValue::Error(e) => {
1822                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1823            }
1824            other => match coerce_complex_str(&other) {
1825                Ok(s) => s,
1826                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1827            },
1828        };
1829
1830        let (_, imag, _) = match parse_complex(&inumber) {
1831            Ok(c) => c,
1832            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1833        };
1834
1835        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(imag)))
1836    }
1837}
1838
1839/// IMABS(inumber) - Returns the absolute value (modulus) of a complex number
1840#[derive(Debug)]
1841pub struct ImAbsFn;
1842impl Function for ImAbsFn {
1843    func_caps!(PURE);
1844    fn name(&self) -> &'static str {
1845        "IMABS"
1846    }
1847    fn min_args(&self) -> usize {
1848        1
1849    }
1850    fn arg_schema(&self) -> &'static [ArgSchema] {
1851        &ARG_ANY_ONE[..]
1852    }
1853    fn eval<'a, 'b, 'c>(
1854        &self,
1855        args: &'c [ArgumentHandle<'a, 'b>],
1856        _ctx: &dyn FunctionContext<'b>,
1857    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1858        let inumber = match args[0].value()?.into_literal() {
1859            LiteralValue::Error(e) => {
1860                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1861            }
1862            other => match coerce_complex_str(&other) {
1863                Ok(s) => s,
1864                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1865            },
1866        };
1867
1868        let (real, imag, _) = match parse_complex(&inumber) {
1869            Ok(c) => c,
1870            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1871        };
1872
1873        let abs = (real * real + imag * imag).sqrt();
1874        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(abs)))
1875    }
1876}
1877
1878/// IMARGUMENT(inumber) - Returns the argument theta (angle in radians) of a complex number
1879#[derive(Debug)]
1880pub struct ImArgumentFn;
1881impl Function for ImArgumentFn {
1882    func_caps!(PURE);
1883    fn name(&self) -> &'static str {
1884        "IMARGUMENT"
1885    }
1886    fn min_args(&self) -> usize {
1887        1
1888    }
1889    fn arg_schema(&self) -> &'static [ArgSchema] {
1890        &ARG_ANY_ONE[..]
1891    }
1892    fn eval<'a, 'b, 'c>(
1893        &self,
1894        args: &'c [ArgumentHandle<'a, 'b>],
1895        _ctx: &dyn FunctionContext<'b>,
1896    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1897        let inumber = match args[0].value()?.into_literal() {
1898            LiteralValue::Error(e) => {
1899                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1900            }
1901            other => match coerce_complex_str(&other) {
1902                Ok(s) => s,
1903                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1904            },
1905        };
1906
1907        let (real, imag, _) = match parse_complex(&inumber) {
1908            Ok(c) => c,
1909            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1910        };
1911
1912        // Excel returns #DIV/0! for IMARGUMENT(0)
1913        if real.abs() < 1e-15 && imag.abs() < 1e-15 {
1914            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1915                ExcelError::new_div(),
1916            )));
1917        }
1918
1919        let arg = imag.atan2(real);
1920        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(arg)))
1921    }
1922}
1923
1924/// IMCONJUGATE(inumber) - Returns the complex conjugate of a complex number
1925#[derive(Debug)]
1926pub struct ImConjugateFn;
1927impl Function for ImConjugateFn {
1928    func_caps!(PURE);
1929    fn name(&self) -> &'static str {
1930        "IMCONJUGATE"
1931    }
1932    fn min_args(&self) -> usize {
1933        1
1934    }
1935    fn arg_schema(&self) -> &'static [ArgSchema] {
1936        &ARG_ANY_ONE[..]
1937    }
1938    fn eval<'a, 'b, 'c>(
1939        &self,
1940        args: &'c [ArgumentHandle<'a, 'b>],
1941        _ctx: &dyn FunctionContext<'b>,
1942    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1943        let inumber = match args[0].value()?.into_literal() {
1944            LiteralValue::Error(e) => {
1945                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1946            }
1947            other => match coerce_complex_str(&other) {
1948                Ok(s) => s,
1949                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1950            },
1951        };
1952
1953        let (real, imag, suffix) = match parse_complex(&inumber) {
1954            Ok(c) => c,
1955            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1956        };
1957
1958        let result = format_complex(real, -imag, suffix);
1959        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1960    }
1961}
1962
1963/// Helper to check if two complex numbers have compatible suffixes
1964fn check_suffix_compatibility(s1: char, s2: char) -> Result<char, ExcelError> {
1965    // If both have the same suffix, use it
1966    // If one is from a purely real number (default 'i'), use the other's suffix
1967    // Excel doesn't allow mixing 'i' and 'j' when both are explicit
1968    if s1 == s2 {
1969        Ok(s1)
1970    } else {
1971        // For simplicity, treat 'i' as the default and allow mixed
1972        // In strict Excel mode, this would error
1973        Ok(s1)
1974    }
1975}
1976
1977/// IMSUM(inumber1, [inumber2], ...) - Returns the sum of complex numbers
1978#[derive(Debug)]
1979pub struct ImSumFn;
1980impl Function for ImSumFn {
1981    func_caps!(PURE);
1982    fn name(&self) -> &'static str {
1983        "IMSUM"
1984    }
1985    fn min_args(&self) -> usize {
1986        1
1987    }
1988    fn variadic(&self) -> bool {
1989        true
1990    }
1991    fn arg_schema(&self) -> &'static [ArgSchema] {
1992        &ARG_ANY_ONE[..]
1993    }
1994    fn eval<'a, 'b, 'c>(
1995        &self,
1996        args: &'c [ArgumentHandle<'a, 'b>],
1997        _ctx: &dyn FunctionContext<'b>,
1998    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1999        let mut sum_real = 0.0;
2000        let mut sum_imag = 0.0;
2001        let mut result_suffix = 'i';
2002        let mut first = true;
2003
2004        for arg in args {
2005            let inumber = match arg.value()?.into_literal() {
2006                LiteralValue::Error(e) => {
2007                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2008                }
2009                other => match coerce_complex_str(&other) {
2010                    Ok(s) => s,
2011                    Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2012                },
2013            };
2014
2015            let (real, imag, suffix) = match parse_complex(&inumber) {
2016                Ok(c) => c,
2017                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2018            };
2019
2020            if first {
2021                result_suffix = suffix;
2022                first = false;
2023            } else {
2024                result_suffix = check_suffix_compatibility(result_suffix, suffix)?;
2025            }
2026
2027            sum_real += real;
2028            sum_imag += imag;
2029        }
2030
2031        let result = format_complex(sum_real, sum_imag, result_suffix);
2032        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2033    }
2034}
2035
2036/// IMSUB(inumber1, inumber2) - Returns the difference of two complex numbers
2037#[derive(Debug)]
2038pub struct ImSubFn;
2039impl Function for ImSubFn {
2040    func_caps!(PURE);
2041    fn name(&self) -> &'static str {
2042        "IMSUB"
2043    }
2044    fn min_args(&self) -> usize {
2045        2
2046    }
2047    fn arg_schema(&self) -> &'static [ArgSchema] {
2048        &ARG_ANY_TWO[..]
2049    }
2050    fn eval<'a, 'b, 'c>(
2051        &self,
2052        args: &'c [ArgumentHandle<'a, 'b>],
2053        _ctx: &dyn FunctionContext<'b>,
2054    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2055        let inumber1 = match args[0].value()?.into_literal() {
2056            LiteralValue::Error(e) => {
2057                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2058            }
2059            other => match coerce_complex_str(&other) {
2060                Ok(s) => s,
2061                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2062            },
2063        };
2064
2065        let inumber2 = match args[1].value()?.into_literal() {
2066            LiteralValue::Error(e) => {
2067                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2068            }
2069            other => match coerce_complex_str(&other) {
2070                Ok(s) => s,
2071                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2072            },
2073        };
2074
2075        let (real1, imag1, suffix1) = match parse_complex(&inumber1) {
2076            Ok(c) => c,
2077            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2078        };
2079
2080        let (real2, imag2, suffix2) = match parse_complex(&inumber2) {
2081            Ok(c) => c,
2082            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2083        };
2084
2085        let result_suffix = check_suffix_compatibility(suffix1, suffix2)?;
2086        let result = format_complex(real1 - real2, imag1 - imag2, result_suffix);
2087        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2088    }
2089}
2090
2091/// IMPRODUCT(inumber1, [inumber2], ...) - Returns the product of complex numbers
2092#[derive(Debug)]
2093pub struct ImProductFn;
2094impl Function for ImProductFn {
2095    func_caps!(PURE);
2096    fn name(&self) -> &'static str {
2097        "IMPRODUCT"
2098    }
2099    fn min_args(&self) -> usize {
2100        1
2101    }
2102    fn variadic(&self) -> bool {
2103        true
2104    }
2105    fn arg_schema(&self) -> &'static [ArgSchema] {
2106        &ARG_ANY_ONE[..]
2107    }
2108    fn eval<'a, 'b, 'c>(
2109        &self,
2110        args: &'c [ArgumentHandle<'a, 'b>],
2111        _ctx: &dyn FunctionContext<'b>,
2112    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2113        let mut prod_real = 1.0;
2114        let mut prod_imag = 0.0;
2115        let mut result_suffix = 'i';
2116        let mut first = true;
2117
2118        for arg in args {
2119            let inumber = match arg.value()?.into_literal() {
2120                LiteralValue::Error(e) => {
2121                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2122                }
2123                other => match coerce_complex_str(&other) {
2124                    Ok(s) => s,
2125                    Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2126                },
2127            };
2128
2129            let (real, imag, suffix) = match parse_complex(&inumber) {
2130                Ok(c) => c,
2131                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2132            };
2133
2134            if first {
2135                result_suffix = suffix;
2136                prod_real = real;
2137                prod_imag = imag;
2138                first = false;
2139            } else {
2140                result_suffix = check_suffix_compatibility(result_suffix, suffix)?;
2141                // (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
2142                let new_real = prod_real * real - prod_imag * imag;
2143                let new_imag = prod_real * imag + prod_imag * real;
2144                prod_real = new_real;
2145                prod_imag = new_imag;
2146            }
2147        }
2148
2149        let result = format_complex(prod_real, prod_imag, result_suffix);
2150        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2151    }
2152}
2153
2154/// IMDIV(inumber1, inumber2) - Returns the quotient of two complex numbers
2155#[derive(Debug)]
2156pub struct ImDivFn;
2157impl Function for ImDivFn {
2158    func_caps!(PURE);
2159    fn name(&self) -> &'static str {
2160        "IMDIV"
2161    }
2162    fn min_args(&self) -> usize {
2163        2
2164    }
2165    fn arg_schema(&self) -> &'static [ArgSchema] {
2166        &ARG_ANY_TWO[..]
2167    }
2168    fn eval<'a, 'b, 'c>(
2169        &self,
2170        args: &'c [ArgumentHandle<'a, 'b>],
2171        _ctx: &dyn FunctionContext<'b>,
2172    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2173        let inumber1 = match args[0].value()?.into_literal() {
2174            LiteralValue::Error(e) => {
2175                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2176            }
2177            other => match coerce_complex_str(&other) {
2178                Ok(s) => s,
2179                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2180            },
2181        };
2182
2183        let inumber2 = match args[1].value()?.into_literal() {
2184            LiteralValue::Error(e) => {
2185                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2186            }
2187            other => match coerce_complex_str(&other) {
2188                Ok(s) => s,
2189                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2190            },
2191        };
2192
2193        let (a, b, suffix1) = match parse_complex(&inumber1) {
2194            Ok(c) => c,
2195            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2196        };
2197
2198        let (c, d, suffix2) = match parse_complex(&inumber2) {
2199            Ok(c) => c,
2200            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2201        };
2202
2203        // Division by zero check - returns #DIV/0! for Excel compatibility
2204        let denom = c * c + d * d;
2205        if denom.abs() < 1e-15 {
2206            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2207                ExcelError::new_div(),
2208            )));
2209        }
2210
2211        let result_suffix = check_suffix_compatibility(suffix1, suffix2)?;
2212
2213        // (a + bi) / (c + di) = ((ac + bd) + (bc - ad)i) / (c^2 + d^2)
2214        let real = (a * c + b * d) / denom;
2215        let imag = (b * c - a * d) / denom;
2216
2217        let result = format_complex(real, imag, result_suffix);
2218        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2219    }
2220}
2221
2222/// IMEXP(inumber) - Returns exponential of a complex number
2223/// e^(a+bi) = e^a * (cos(b) + i*sin(b))
2224#[derive(Debug)]
2225pub struct ImExpFn;
2226impl Function for ImExpFn {
2227    func_caps!(PURE);
2228    fn name(&self) -> &'static str {
2229        "IMEXP"
2230    }
2231    fn min_args(&self) -> usize {
2232        1
2233    }
2234    fn arg_schema(&self) -> &'static [ArgSchema] {
2235        &ARG_ANY_ONE[..]
2236    }
2237    fn eval<'a, 'b, 'c>(
2238        &self,
2239        args: &'c [ArgumentHandle<'a, 'b>],
2240        _ctx: &dyn FunctionContext<'b>,
2241    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2242        let inumber = match args[0].value()?.into_literal() {
2243            LiteralValue::Error(e) => {
2244                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2245            }
2246            other => match coerce_complex_str(&other) {
2247                Ok(s) => s,
2248                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2249            },
2250        };
2251
2252        let (a, b, suffix) = match parse_complex(&inumber) {
2253            Ok(c) => c,
2254            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2255        };
2256
2257        // e^(a+bi) = e^a * (cos(b) + i*sin(b))
2258        let exp_a = a.exp();
2259        let real = exp_a * b.cos();
2260        let imag = exp_a * b.sin();
2261
2262        let result = format_complex(real, imag, suffix);
2263        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2264    }
2265}
2266
2267/// IMLN(inumber) - Returns the natural logarithm of a complex number
2268/// ln(a+bi) = ln(|z|) + i*arg(z)
2269#[derive(Debug)]
2270pub struct ImLnFn;
2271impl Function for ImLnFn {
2272    func_caps!(PURE);
2273    fn name(&self) -> &'static str {
2274        "IMLN"
2275    }
2276    fn min_args(&self) -> usize {
2277        1
2278    }
2279    fn arg_schema(&self) -> &'static [ArgSchema] {
2280        &ARG_ANY_ONE[..]
2281    }
2282    fn eval<'a, 'b, 'c>(
2283        &self,
2284        args: &'c [ArgumentHandle<'a, 'b>],
2285        _ctx: &dyn FunctionContext<'b>,
2286    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2287        let inumber = match args[0].value()?.into_literal() {
2288            LiteralValue::Error(e) => {
2289                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2290            }
2291            other => match coerce_complex_str(&other) {
2292                Ok(s) => s,
2293                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2294            },
2295        };
2296
2297        let (a, b, suffix) = match parse_complex(&inumber) {
2298            Ok(c) => c,
2299            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2300        };
2301
2302        // ln(0) is undefined
2303        let modulus = (a * a + b * b).sqrt();
2304        if modulus < 1e-15 {
2305            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2306                ExcelError::new_num(),
2307            )));
2308        }
2309
2310        // ln(z) = ln(|z|) + i*arg(z)
2311        let real = modulus.ln();
2312        let imag = b.atan2(a);
2313
2314        let result = format_complex(real, imag, suffix);
2315        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2316    }
2317}
2318
2319/// IMLOG10(inumber) - Returns the base-10 logarithm of a complex number
2320/// log10(z) = ln(z) / ln(10)
2321#[derive(Debug)]
2322pub struct ImLog10Fn;
2323impl Function for ImLog10Fn {
2324    func_caps!(PURE);
2325    fn name(&self) -> &'static str {
2326        "IMLOG10"
2327    }
2328    fn min_args(&self) -> usize {
2329        1
2330    }
2331    fn arg_schema(&self) -> &'static [ArgSchema] {
2332        &ARG_ANY_ONE[..]
2333    }
2334    fn eval<'a, 'b, 'c>(
2335        &self,
2336        args: &'c [ArgumentHandle<'a, 'b>],
2337        _ctx: &dyn FunctionContext<'b>,
2338    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2339        let inumber = match args[0].value()?.into_literal() {
2340            LiteralValue::Error(e) => {
2341                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2342            }
2343            other => match coerce_complex_str(&other) {
2344                Ok(s) => s,
2345                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2346            },
2347        };
2348
2349        let (a, b, suffix) = match parse_complex(&inumber) {
2350            Ok(c) => c,
2351            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2352        };
2353
2354        // log10(0) is undefined
2355        let modulus = (a * a + b * b).sqrt();
2356        if modulus < 1e-15 {
2357            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2358                ExcelError::new_num(),
2359            )));
2360        }
2361
2362        // log10(z) = ln(z) / ln(10) = (ln(|z|) + i*arg(z)) / ln(10)
2363        let ln10 = 10.0_f64.ln();
2364        let real = modulus.ln() / ln10;
2365        let imag = b.atan2(a) / ln10;
2366
2367        let result = format_complex(real, imag, suffix);
2368        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2369    }
2370}
2371
2372/// IMLOG2(inumber) - Returns the base-2 logarithm of a complex number
2373/// log2(z) = ln(z) / ln(2)
2374#[derive(Debug)]
2375pub struct ImLog2Fn;
2376impl Function for ImLog2Fn {
2377    func_caps!(PURE);
2378    fn name(&self) -> &'static str {
2379        "IMLOG2"
2380    }
2381    fn min_args(&self) -> usize {
2382        1
2383    }
2384    fn arg_schema(&self) -> &'static [ArgSchema] {
2385        &ARG_ANY_ONE[..]
2386    }
2387    fn eval<'a, 'b, 'c>(
2388        &self,
2389        args: &'c [ArgumentHandle<'a, 'b>],
2390        _ctx: &dyn FunctionContext<'b>,
2391    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2392        let inumber = match args[0].value()?.into_literal() {
2393            LiteralValue::Error(e) => {
2394                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2395            }
2396            other => match coerce_complex_str(&other) {
2397                Ok(s) => s,
2398                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2399            },
2400        };
2401
2402        let (a, b, suffix) = match parse_complex(&inumber) {
2403            Ok(c) => c,
2404            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2405        };
2406
2407        // log2(0) is undefined
2408        let modulus = (a * a + b * b).sqrt();
2409        if modulus < 1e-15 {
2410            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2411                ExcelError::new_num(),
2412            )));
2413        }
2414
2415        // log2(z) = ln(z) / ln(2) = (ln(|z|) + i*arg(z)) / ln(2)
2416        let ln2 = 2.0_f64.ln();
2417        let real = modulus.ln() / ln2;
2418        let imag = b.atan2(a) / ln2;
2419
2420        let result = format_complex(real, imag, suffix);
2421        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2422    }
2423}
2424
2425/// IMPOWER(inumber, n) - Returns a complex number raised to a power
2426/// z^n = |z|^n * (cos(n*theta) + i*sin(n*theta))
2427#[derive(Debug)]
2428pub struct ImPowerFn;
2429impl Function for ImPowerFn {
2430    func_caps!(PURE);
2431    fn name(&self) -> &'static str {
2432        "IMPOWER"
2433    }
2434    fn min_args(&self) -> usize {
2435        2
2436    }
2437    fn arg_schema(&self) -> &'static [ArgSchema] {
2438        &ARG_ANY_TWO[..]
2439    }
2440    fn eval<'a, 'b, 'c>(
2441        &self,
2442        args: &'c [ArgumentHandle<'a, 'b>],
2443        _ctx: &dyn FunctionContext<'b>,
2444    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2445        let inumber = match args[0].value()?.into_literal() {
2446            LiteralValue::Error(e) => {
2447                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2448            }
2449            other => match coerce_complex_str(&other) {
2450                Ok(s) => s,
2451                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2452            },
2453        };
2454
2455        let n = match args[1].value()?.into_literal() {
2456            LiteralValue::Error(e) => {
2457                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2458            }
2459            other => coerce_num(&other)?,
2460        };
2461
2462        let (a, b, suffix) = match parse_complex(&inumber) {
2463            Ok(c) => c,
2464            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2465        };
2466
2467        let modulus = (a * a + b * b).sqrt();
2468        let theta = b.atan2(a);
2469
2470        // Handle 0^n cases
2471        if modulus < 1e-15 {
2472            if n > 0.0 {
2473                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
2474                    "0".to_string(),
2475                )));
2476            } else {
2477                // 0^0 or 0^negative is undefined
2478                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2479                    ExcelError::new_num(),
2480                )));
2481            }
2482        }
2483
2484        // z^n = |z|^n * (cos(n*theta) + i*sin(n*theta))
2485        let r_n = modulus.powf(n);
2486        let n_theta = n * theta;
2487        let real = r_n * n_theta.cos();
2488        let imag = r_n * n_theta.sin();
2489
2490        let result = format_complex(real, imag, suffix);
2491        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2492    }
2493}
2494
2495/// IMSQRT(inumber) - Returns the square root of a complex number
2496/// sqrt(z) = sqrt(|z|) * (cos(theta/2) + i*sin(theta/2))
2497#[derive(Debug)]
2498pub struct ImSqrtFn;
2499impl Function for ImSqrtFn {
2500    func_caps!(PURE);
2501    fn name(&self) -> &'static str {
2502        "IMSQRT"
2503    }
2504    fn min_args(&self) -> usize {
2505        1
2506    }
2507    fn arg_schema(&self) -> &'static [ArgSchema] {
2508        &ARG_ANY_ONE[..]
2509    }
2510    fn eval<'a, 'b, 'c>(
2511        &self,
2512        args: &'c [ArgumentHandle<'a, 'b>],
2513        _ctx: &dyn FunctionContext<'b>,
2514    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2515        let inumber = match args[0].value()?.into_literal() {
2516            LiteralValue::Error(e) => {
2517                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2518            }
2519            other => match coerce_complex_str(&other) {
2520                Ok(s) => s,
2521                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2522            },
2523        };
2524
2525        let (a, b, suffix) = match parse_complex(&inumber) {
2526            Ok(c) => c,
2527            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2528        };
2529
2530        let modulus = (a * a + b * b).sqrt();
2531        let theta = b.atan2(a);
2532
2533        // sqrt(z) = sqrt(|z|) * (cos(theta/2) + i*sin(theta/2))
2534        let sqrt_r = modulus.sqrt();
2535        let half_theta = theta / 2.0;
2536        let real = sqrt_r * half_theta.cos();
2537        let imag = sqrt_r * half_theta.sin();
2538
2539        let result = format_complex(real, imag, suffix);
2540        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2541    }
2542}
2543
2544/// IMSIN(inumber) - Returns the sine of a complex number
2545/// sin(a+bi) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
2546#[derive(Debug)]
2547pub struct ImSinFn;
2548impl Function for ImSinFn {
2549    func_caps!(PURE);
2550    fn name(&self) -> &'static str {
2551        "IMSIN"
2552    }
2553    fn min_args(&self) -> usize {
2554        1
2555    }
2556    fn arg_schema(&self) -> &'static [ArgSchema] {
2557        &ARG_ANY_ONE[..]
2558    }
2559    fn eval<'a, 'b, 'c>(
2560        &self,
2561        args: &'c [ArgumentHandle<'a, 'b>],
2562        _ctx: &dyn FunctionContext<'b>,
2563    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2564        let inumber = match args[0].value()?.into_literal() {
2565            LiteralValue::Error(e) => {
2566                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2567            }
2568            other => match coerce_complex_str(&other) {
2569                Ok(s) => s,
2570                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2571            },
2572        };
2573
2574        let (a, b, suffix) = match parse_complex(&inumber) {
2575            Ok(c) => c,
2576            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2577        };
2578
2579        // sin(a+bi) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
2580        let real = a.sin() * b.cosh();
2581        let imag = a.cos() * b.sinh();
2582
2583        let result = format_complex(real, imag, suffix);
2584        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2585    }
2586}
2587
2588/// IMCOS(inumber) - Returns the cosine of a complex number
2589/// cos(a+bi) = cos(a)*cosh(b) - i*sin(a)*sinh(b)
2590#[derive(Debug)]
2591pub struct ImCosFn;
2592impl Function for ImCosFn {
2593    func_caps!(PURE);
2594    fn name(&self) -> &'static str {
2595        "IMCOS"
2596    }
2597    fn min_args(&self) -> usize {
2598        1
2599    }
2600    fn arg_schema(&self) -> &'static [ArgSchema] {
2601        &ARG_ANY_ONE[..]
2602    }
2603    fn eval<'a, 'b, 'c>(
2604        &self,
2605        args: &'c [ArgumentHandle<'a, 'b>],
2606        _ctx: &dyn FunctionContext<'b>,
2607    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2608        let inumber = match args[0].value()?.into_literal() {
2609            LiteralValue::Error(e) => {
2610                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2611            }
2612            other => match coerce_complex_str(&other) {
2613                Ok(s) => s,
2614                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2615            },
2616        };
2617
2618        let (a, b, suffix) = match parse_complex(&inumber) {
2619            Ok(c) => c,
2620            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2621        };
2622
2623        // cos(a+bi) = cos(a)*cosh(b) - i*sin(a)*sinh(b)
2624        let real = a.cos() * b.cosh();
2625        let imag = -a.sin() * b.sinh();
2626
2627        let result = format_complex(real, imag, suffix);
2628        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2629    }
2630}
2631
2632/* ─────────────────────────── Unit Conversion (CONVERT) ──────────────────────────── */
2633
2634/// Unit categories for CONVERT function
2635#[derive(Clone, Copy, PartialEq, Eq, Debug)]
2636enum UnitCategory {
2637    Length,
2638    Mass,
2639    Temperature,
2640}
2641
2642/// Information about a unit
2643struct UnitInfo {
2644    category: UnitCategory,
2645    /// Conversion factor to base unit (meters for length, grams for mass)
2646    /// For temperature, this is special-cased
2647    to_base: f64,
2648}
2649
2650/// Get unit info for a given unit string
2651fn get_unit_info(unit: &str) -> Option<UnitInfo> {
2652    // Length units (base: meter)
2653    match unit {
2654        // Metric length
2655        "m" => Some(UnitInfo {
2656            category: UnitCategory::Length,
2657            to_base: 1.0,
2658        }),
2659        "km" => Some(UnitInfo {
2660            category: UnitCategory::Length,
2661            to_base: 1000.0,
2662        }),
2663        "cm" => Some(UnitInfo {
2664            category: UnitCategory::Length,
2665            to_base: 0.01,
2666        }),
2667        "mm" => Some(UnitInfo {
2668            category: UnitCategory::Length,
2669            to_base: 0.001,
2670        }),
2671        // Imperial length
2672        "mi" => Some(UnitInfo {
2673            category: UnitCategory::Length,
2674            to_base: 1609.344,
2675        }),
2676        "ft" => Some(UnitInfo {
2677            category: UnitCategory::Length,
2678            to_base: 0.3048,
2679        }),
2680        "in" => Some(UnitInfo {
2681            category: UnitCategory::Length,
2682            to_base: 0.0254,
2683        }),
2684        "yd" => Some(UnitInfo {
2685            category: UnitCategory::Length,
2686            to_base: 0.9144,
2687        }),
2688        "Nmi" => Some(UnitInfo {
2689            category: UnitCategory::Length,
2690            to_base: 1852.0,
2691        }),
2692
2693        // Mass units (base: gram)
2694        "g" => Some(UnitInfo {
2695            category: UnitCategory::Mass,
2696            to_base: 1.0,
2697        }),
2698        "kg" => Some(UnitInfo {
2699            category: UnitCategory::Mass,
2700            to_base: 1000.0,
2701        }),
2702        "mg" => Some(UnitInfo {
2703            category: UnitCategory::Mass,
2704            to_base: 0.001,
2705        }),
2706        "lbm" => Some(UnitInfo {
2707            category: UnitCategory::Mass,
2708            to_base: 453.59237,
2709        }),
2710        "oz" => Some(UnitInfo {
2711            category: UnitCategory::Mass,
2712            to_base: 28.349523125,
2713        }),
2714        "ozm" => Some(UnitInfo {
2715            category: UnitCategory::Mass,
2716            to_base: 28.349523125,
2717        }),
2718        "ton" => Some(UnitInfo {
2719            category: UnitCategory::Mass,
2720            to_base: 907184.74,
2721        }),
2722
2723        // Temperature units (special handling)
2724        "C" | "cel" => Some(UnitInfo {
2725            category: UnitCategory::Temperature,
2726            to_base: 0.0, // Special-cased
2727        }),
2728        "F" | "fah" => Some(UnitInfo {
2729            category: UnitCategory::Temperature,
2730            to_base: 0.0, // Special-cased
2731        }),
2732        "K" | "kel" => Some(UnitInfo {
2733            category: UnitCategory::Temperature,
2734            to_base: 0.0, // Special-cased
2735        }),
2736
2737        _ => None,
2738    }
2739}
2740
2741/// Normalize temperature unit name
2742fn normalize_temp_unit(unit: &str) -> &str {
2743    match unit {
2744        "C" | "cel" => "C",
2745        "F" | "fah" => "F",
2746        "K" | "kel" => "K",
2747        _ => unit,
2748    }
2749}
2750
2751/// Convert temperature between units
2752fn convert_temperature(value: f64, from: &str, to: &str) -> f64 {
2753    let from = normalize_temp_unit(from);
2754    let to = normalize_temp_unit(to);
2755
2756    if from == to {
2757        return value;
2758    }
2759
2760    // First convert to Celsius
2761    let celsius = match from {
2762        "C" => value,
2763        "F" => (value - 32.0) * 5.0 / 9.0,
2764        "K" => value - 273.15,
2765        _ => value,
2766    };
2767
2768    // Then convert from Celsius to target
2769    match to {
2770        "C" => celsius,
2771        "F" => celsius * 9.0 / 5.0 + 32.0,
2772        "K" => celsius + 273.15,
2773        _ => celsius,
2774    }
2775}
2776
2777/// Convert a value between units
2778fn convert_units(value: f64, from: &str, to: &str) -> Result<f64, ExcelError> {
2779    let from_info = get_unit_info(from).ok_or_else(ExcelError::new_na)?;
2780    let to_info = get_unit_info(to).ok_or_else(ExcelError::new_na)?;
2781
2782    // Check category compatibility
2783    if from_info.category != to_info.category {
2784        return Err(ExcelError::new_na());
2785    }
2786
2787    // Handle temperature specially
2788    if from_info.category == UnitCategory::Temperature {
2789        return Ok(convert_temperature(value, from, to));
2790    }
2791
2792    // For other units: convert to base, then to target
2793    let base_value = value * from_info.to_base;
2794    Ok(base_value / to_info.to_base)
2795}
2796
2797/// CONVERT(number, from_unit, to_unit) - Converts between measurement units
2798#[derive(Debug)]
2799pub struct ConvertFn;
2800impl Function for ConvertFn {
2801    func_caps!(PURE);
2802    fn name(&self) -> &'static str {
2803        "CONVERT"
2804    }
2805    fn min_args(&self) -> usize {
2806        3
2807    }
2808    fn arg_schema(&self) -> &'static [ArgSchema] {
2809        &ARG_COMPLEX_THREE[..]
2810    }
2811    fn eval<'a, 'b, 'c>(
2812        &self,
2813        args: &'c [ArgumentHandle<'a, 'b>],
2814        _ctx: &dyn FunctionContext<'b>,
2815    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2816        // Get the number value
2817        let value = match args[0].value()?.into_literal() {
2818            LiteralValue::Error(e) => {
2819                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2820            }
2821            other => coerce_num(&other)?,
2822        };
2823
2824        // Get from_unit
2825        let from_unit = match args[1].value()?.into_literal() {
2826            LiteralValue::Error(e) => {
2827                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2828            }
2829            LiteralValue::Text(s) => s,
2830            _ => {
2831                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2832                    ExcelError::new_na(),
2833                )));
2834            }
2835        };
2836
2837        // Get to_unit
2838        let to_unit = match args[2].value()?.into_literal() {
2839            LiteralValue::Error(e) => {
2840                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2841            }
2842            LiteralValue::Text(s) => s,
2843            _ => {
2844                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2845                    ExcelError::new_na(),
2846                )));
2847            }
2848        };
2849
2850        match convert_units(value, &from_unit, &to_unit) {
2851            Ok(result) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2852                result,
2853            ))),
2854            Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2855        }
2856    }
2857}
2858
2859pub fn register_builtins() {
2860    use std::sync::Arc;
2861    crate::function_registry::register_function(Arc::new(BitAndFn));
2862    crate::function_registry::register_function(Arc::new(BitOrFn));
2863    crate::function_registry::register_function(Arc::new(BitXorFn));
2864    crate::function_registry::register_function(Arc::new(BitLShiftFn));
2865    crate::function_registry::register_function(Arc::new(BitRShiftFn));
2866    crate::function_registry::register_function(Arc::new(Bin2DecFn));
2867    crate::function_registry::register_function(Arc::new(Dec2BinFn));
2868    crate::function_registry::register_function(Arc::new(Hex2DecFn));
2869    crate::function_registry::register_function(Arc::new(Dec2HexFn));
2870    crate::function_registry::register_function(Arc::new(Oct2DecFn));
2871    crate::function_registry::register_function(Arc::new(Dec2OctFn));
2872    crate::function_registry::register_function(Arc::new(Bin2HexFn));
2873    crate::function_registry::register_function(Arc::new(Hex2BinFn));
2874    crate::function_registry::register_function(Arc::new(Bin2OctFn));
2875    crate::function_registry::register_function(Arc::new(Oct2BinFn));
2876    crate::function_registry::register_function(Arc::new(Hex2OctFn));
2877    crate::function_registry::register_function(Arc::new(Oct2HexFn));
2878    crate::function_registry::register_function(Arc::new(DeltaFn));
2879    crate::function_registry::register_function(Arc::new(GestepFn));
2880    crate::function_registry::register_function(Arc::new(ErfFn));
2881    crate::function_registry::register_function(Arc::new(ErfcFn));
2882    crate::function_registry::register_function(Arc::new(ErfPreciseFn));
2883    // Complex number functions
2884    crate::function_registry::register_function(Arc::new(ComplexFn));
2885    crate::function_registry::register_function(Arc::new(ImRealFn));
2886    crate::function_registry::register_function(Arc::new(ImaginaryFn));
2887    crate::function_registry::register_function(Arc::new(ImAbsFn));
2888    crate::function_registry::register_function(Arc::new(ImArgumentFn));
2889    crate::function_registry::register_function(Arc::new(ImConjugateFn));
2890    crate::function_registry::register_function(Arc::new(ImSumFn));
2891    crate::function_registry::register_function(Arc::new(ImSubFn));
2892    crate::function_registry::register_function(Arc::new(ImProductFn));
2893    crate::function_registry::register_function(Arc::new(ImDivFn));
2894    // Complex number math functions
2895    crate::function_registry::register_function(Arc::new(ImExpFn));
2896    crate::function_registry::register_function(Arc::new(ImLnFn));
2897    crate::function_registry::register_function(Arc::new(ImLog10Fn));
2898    crate::function_registry::register_function(Arc::new(ImLog2Fn));
2899    crate::function_registry::register_function(Arc::new(ImPowerFn));
2900    crate::function_registry::register_function(Arc::new(ImSqrtFn));
2901    crate::function_registry::register_function(Arc::new(ImSinFn));
2902    crate::function_registry::register_function(Arc::new(ImCosFn));
2903    // Unit conversion
2904    crate::function_registry::register_function(Arc::new(ConvertFn));
2905}