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 n < -512 || n > 511 {
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 n < -(1i64 << 39) || n > (1i64 << 39) - 1 {
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| c >= '0' && c <= '7') {
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 n < -(1i64 << 29) || n > (1i64 << 29) - 1 {
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 dec < -512 || dec > 511 {
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| c >= '0' && c <= '7') {
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 dec < -512 || dec > 511 {
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 dec < -(1i64 << 29) || dec > (1i64 << 29) - 1 {
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| c >= '0' && c <= '7') {
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)
1243fn erf_approx(x: f64) -> f64 {
1244    let ax = x.abs();
1245
1246    // For small x, use series expansion
1247    if ax < 0.5 {
1248        // Coefficients for erf(x) = x * P(x^2) / Q(x^2)
1249        const P: [f64; 5] = [
1250            3.20937758913846947e+03,
1251            3.77485237685302021e+02,
1252            1.13864154151050156e+02,
1253            3.16112374387056560e+00,
1254            1.85777706184603153e-01,
1255        ];
1256        const Q: [f64; 5] = [
1257            2.84423748127893300e+03,
1258            1.28261652607737228e+03,
1259            2.44024637934444173e+02,
1260            2.36012909523441209e+01,
1261            1.00000000000000000e+00,
1262        ];
1263
1264        let x2 = x * x;
1265        let p_val = P[4];
1266        let p_val = p_val * x2 + P[3];
1267        let p_val = p_val * x2 + P[2];
1268        let p_val = p_val * x2 + P[1];
1269        let p_val = p_val * x2 + P[0];
1270
1271        let q_val = Q[4];
1272        let q_val = q_val * x2 + Q[3];
1273        let q_val = q_val * x2 + Q[2];
1274        let q_val = q_val * x2 + Q[1];
1275        let q_val = q_val * x2 + Q[0];
1276
1277        return x * p_val / q_val;
1278    }
1279
1280    // For x in [0.5, 4], use erfc approximation and compute erf = 1 - erfc
1281    if ax < 4.0 {
1282        let erfc_val = erfc_mid(ax);
1283        return if x > 0.0 {
1284            1.0 - erfc_val
1285        } else {
1286            erfc_val - 1.0
1287        };
1288    }
1289
1290    // For large x, erf(x) ≈ ±1
1291    let erfc_val = erfc_large(ax);
1292    if x > 0.0 {
1293        1.0 - erfc_val
1294    } else {
1295        erfc_val - 1.0
1296    }
1297}
1298
1299/// erfc for x in [0.5, 4]
1300fn erfc_mid(x: f64) -> f64 {
1301    const P: [f64; 9] = [
1302        1.23033935479799725e+03,
1303        2.05107837782607147e+03,
1304        1.71204761263407058e+03,
1305        8.81952221241769090e+02,
1306        2.98635138197400131e+02,
1307        6.61191906371416295e+01,
1308        8.88314979438837594e+00,
1309        5.64188496988670089e-01,
1310        2.15311535474403846e-08,
1311    ];
1312    const Q: [f64; 9] = [
1313        1.23033935480374942e+03,
1314        3.43936767414372164e+03,
1315        4.36261909014324716e+03,
1316        3.29079923573345963e+03,
1317        1.62138957456669019e+03,
1318        5.37181101862009858e+02,
1319        1.17693950891312499e+02,
1320        1.57449261107098347e+01,
1321        1.00000000000000000e+00,
1322    ];
1323
1324    let p_val = P[8];
1325    let p_val = p_val * x + P[7];
1326    let p_val = p_val * x + P[6];
1327    let p_val = p_val * x + P[5];
1328    let p_val = p_val * x + P[4];
1329    let p_val = p_val * x + P[3];
1330    let p_val = p_val * x + P[2];
1331    let p_val = p_val * x + P[1];
1332    let p_val = p_val * x + P[0];
1333
1334    let q_val = Q[8];
1335    let q_val = q_val * x + Q[7];
1336    let q_val = q_val * x + Q[6];
1337    let q_val = q_val * x + Q[5];
1338    let q_val = q_val * x + Q[4];
1339    let q_val = q_val * x + Q[3];
1340    let q_val = q_val * x + Q[2];
1341    let q_val = q_val * x + Q[1];
1342    let q_val = q_val * x + Q[0];
1343
1344    (-x * x).exp() * p_val / q_val
1345}
1346
1347/// erfc for x >= 4
1348fn erfc_large(x: f64) -> f64 {
1349    const P: [f64; 6] = [
1350        6.58749161529837803e-04,
1351        1.60837851487422766e-02,
1352        1.25781726111229246e-01,
1353        3.60344899949804439e-01,
1354        3.05326634961232344e-01,
1355        1.63153871373020978e-02,
1356    ];
1357    const Q: [f64; 6] = [
1358        2.33520497626869185e-03,
1359        6.05183413124413191e-02,
1360        5.27905102951428412e-01,
1361        1.87295284992346047e+00,
1362        2.56852019228982242e+00,
1363        1.00000000000000000e+00,
1364    ];
1365
1366    let x2 = x * x;
1367    let inv_x2 = 1.0 / x2;
1368
1369    let p_val = P[5];
1370    let p_val = p_val * inv_x2 + P[4];
1371    let p_val = p_val * inv_x2 + P[3];
1372    let p_val = p_val * inv_x2 + P[2];
1373    let p_val = p_val * inv_x2 + P[1];
1374    let p_val = p_val * inv_x2 + P[0];
1375
1376    let q_val = Q[5];
1377    let q_val = q_val * inv_x2 + Q[4];
1378    let q_val = q_val * inv_x2 + Q[3];
1379    let q_val = q_val * inv_x2 + Q[2];
1380    let q_val = q_val * inv_x2 + Q[1];
1381    let q_val = q_val * inv_x2 + Q[0];
1382
1383    // 1/sqrt(pi) = 0.5641895835477563
1384    const FRAC_1_SQRT_PI: f64 = 0.5641895835477563;
1385    (-x2).exp() / x * (FRAC_1_SQRT_PI + inv_x2 * p_val / q_val)
1386}
1387
1388/// Direct erfc computation for ERFC function
1389fn erfc_direct(x: f64) -> f64 {
1390    if x < 0.0 {
1391        return 2.0 - erfc_direct(-x);
1392    }
1393    if x < 0.5 {
1394        return 1.0 - erf_approx(x);
1395    }
1396    if x < 4.0 {
1397        return erfc_mid(x);
1398    }
1399    erfc_large(x)
1400}
1401
1402/// ERF(lower_limit, [upper_limit]) - Returns the error function integrated between lower_limit and upper_limit
1403/// If only lower_limit is provided, returns erf(lower_limit)
1404/// If both are provided, returns erf(upper_limit) - erf(lower_limit)
1405#[derive(Debug)]
1406pub struct ErfFn;
1407impl Function for ErfFn {
1408    func_caps!(PURE);
1409    fn name(&self) -> &'static str {
1410        "ERF"
1411    }
1412    fn min_args(&self) -> usize {
1413        1
1414    }
1415    fn variadic(&self) -> bool {
1416        true
1417    }
1418    fn arg_schema(&self) -> &'static [ArgSchema] {
1419        &ARG_NUM_LENIENT_TWO[..]
1420    }
1421    fn eval<'a, 'b, 'c>(
1422        &self,
1423        args: &'c [ArgumentHandle<'a, 'b>],
1424        _ctx: &dyn FunctionContext<'b>,
1425    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1426        let lower = match args[0].value()?.into_literal() {
1427            LiteralValue::Error(e) => {
1428                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1429            }
1430            other => coerce_num(&other)?,
1431        };
1432
1433        let result = if args.len() > 1 {
1434            // ERF(lower, upper) = erf(upper) - erf(lower)
1435            let upper = match args[1].value()?.into_literal() {
1436                LiteralValue::Error(e) => {
1437                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1438                }
1439                other => coerce_num(&other)?,
1440            };
1441            erf_approx(upper) - erf_approx(lower)
1442        } else {
1443            // ERF(x) = erf(x)
1444            erf_approx(lower)
1445        };
1446
1447        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1448            result,
1449        )))
1450    }
1451}
1452
1453/// ERFC(x) - Returns the complementary error function = 1 - erf(x)
1454#[derive(Debug)]
1455pub struct ErfcFn;
1456impl Function for ErfcFn {
1457    func_caps!(PURE);
1458    fn name(&self) -> &'static str {
1459        "ERFC"
1460    }
1461    fn min_args(&self) -> usize {
1462        1
1463    }
1464    fn arg_schema(&self) -> &'static [ArgSchema] {
1465        &ARG_ANY_ONE[..]
1466    }
1467    fn eval<'a, 'b, 'c>(
1468        &self,
1469        args: &'c [ArgumentHandle<'a, 'b>],
1470        _ctx: &dyn FunctionContext<'b>,
1471    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1472        let x = match args[0].value()?.into_literal() {
1473            LiteralValue::Error(e) => {
1474                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1475            }
1476            other => coerce_num(&other)?,
1477        };
1478
1479        let result = erfc_direct(x);
1480        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1481            result,
1482        )))
1483    }
1484}
1485
1486/// ERF.PRECISE(x) - Returns the error function (same as ERF with one argument)
1487#[derive(Debug)]
1488pub struct ErfPreciseFn;
1489impl Function for ErfPreciseFn {
1490    func_caps!(PURE);
1491    fn name(&self) -> &'static str {
1492        "ERF.PRECISE"
1493    }
1494    fn min_args(&self) -> usize {
1495        1
1496    }
1497    fn arg_schema(&self) -> &'static [ArgSchema] {
1498        &ARG_ANY_ONE[..]
1499    }
1500    fn eval<'a, 'b, 'c>(
1501        &self,
1502        args: &'c [ArgumentHandle<'a, 'b>],
1503        _ctx: &dyn FunctionContext<'b>,
1504    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1505        let x = match args[0].value()?.into_literal() {
1506            LiteralValue::Error(e) => {
1507                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1508            }
1509            other => coerce_num(&other)?,
1510        };
1511
1512        let result = erf_approx(x);
1513        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
1514            result,
1515        )))
1516    }
1517}
1518
1519/* ─────────────────────────── Complex Number Functions ──────────────────────────── */
1520
1521/// Parse a complex number string like "3+4i", "3-4i", "5i", "3", "-2j", etc.
1522/// Returns (real, imaginary, suffix) where suffix is 'i' or 'j'
1523fn parse_complex(s: &str) -> Result<(f64, f64, char), ExcelError> {
1524    let s = s.trim();
1525    if s.is_empty() {
1526        return Err(ExcelError::new_num());
1527    }
1528
1529    // Determine the suffix (i or j)
1530    let suffix = if s.ends_with('i') || s.ends_with('I') {
1531        'i'
1532    } else if s.ends_with('j') || s.ends_with('J') {
1533        'j'
1534    } else {
1535        // No imaginary suffix - must be purely real
1536        let real: f64 = s.parse().map_err(|_| ExcelError::new_num())?;
1537        return Ok((real, 0.0, 'i'));
1538    };
1539
1540    // Remove the suffix for parsing
1541    let s = &s[..s.len() - 1];
1542
1543    // Handle pure imaginary cases like "i", "-i", "4i"
1544    if s.is_empty() || s == "+" {
1545        return Ok((0.0, 1.0, suffix));
1546    }
1547    if s == "-" {
1548        return Ok((0.0, -1.0, suffix));
1549    }
1550
1551    // Find the last + or - that separates real and imaginary parts
1552    // We need to skip the first character (could be a sign) and find operators
1553    let mut split_pos = None;
1554    let bytes = s.as_bytes();
1555
1556    for i in (1..bytes.len()).rev() {
1557        let c = bytes[i] as char;
1558        if c == '+' || c == '-' {
1559            // Make sure this isn't part of an exponent (e.g., "1e-5")
1560            if i > 0 {
1561                let prev = bytes[i - 1] as char;
1562                if prev == 'e' || prev == 'E' {
1563                    continue;
1564                }
1565            }
1566            split_pos = Some(i);
1567            break;
1568        }
1569    }
1570
1571    match split_pos {
1572        Some(pos) => {
1573            // We have both real and imaginary parts
1574            let real_str = &s[..pos];
1575            let imag_str = &s[pos..];
1576
1577            let real: f64 = if real_str.is_empty() {
1578                0.0
1579            } else {
1580                real_str.parse().map_err(|_| ExcelError::new_num())?
1581            };
1582
1583            // Handle imaginary part (could be "+", "-", "+5", "-5", etc.)
1584            let imag: f64 = if imag_str == "+" {
1585                1.0
1586            } else if imag_str == "-" {
1587                -1.0
1588            } else {
1589                imag_str.parse().map_err(|_| ExcelError::new_num())?
1590            };
1591
1592            Ok((real, imag, suffix))
1593        }
1594        None => {
1595            // Pure imaginary number (no real part), e.g., "5" (before suffix was removed)
1596            let imag: f64 = s.parse().map_err(|_| ExcelError::new_num())?;
1597            Ok((0.0, imag, suffix))
1598        }
1599    }
1600}
1601
1602/// Clean up floating point noise by rounding values very close to integers
1603fn clean_float(val: f64) -> f64 {
1604    let rounded = val.round();
1605    if (val - rounded).abs() < 1e-10 {
1606        rounded
1607    } else {
1608        val
1609    }
1610}
1611
1612/// Format a complex number as a string
1613fn format_complex(real: f64, imag: f64, suffix: char) -> String {
1614    // Clean up floating point noise
1615    let real = clean_float(real);
1616    let imag = clean_float(imag);
1617
1618    // Handle special cases for cleaner output
1619    let real_is_zero = real.abs() < 1e-15;
1620    let imag_is_zero = imag.abs() < 1e-15;
1621
1622    if real_is_zero && imag_is_zero {
1623        return "0".to_string();
1624    }
1625
1626    if imag_is_zero {
1627        // Purely real
1628        if real == real.trunc() && real.abs() < 1e15 {
1629            return format!("{}", real as i64);
1630        }
1631        return format!("{}", real);
1632    }
1633
1634    if real_is_zero {
1635        // Purely imaginary
1636        if (imag - 1.0).abs() < 1e-15 {
1637            return format!("{}", suffix);
1638        }
1639        if (imag + 1.0).abs() < 1e-15 {
1640            return format!("-{}", suffix);
1641        }
1642        if imag == imag.trunc() && imag.abs() < 1e15 {
1643            return format!("{}{}", imag as i64, suffix);
1644        }
1645        return format!("{}{}", imag, suffix);
1646    }
1647
1648    // Both parts are non-zero
1649    let real_str = if real == real.trunc() && real.abs() < 1e15 {
1650        format!("{}", real as i64)
1651    } else {
1652        format!("{}", real)
1653    };
1654
1655    let imag_str = if (imag - 1.0).abs() < 1e-15 {
1656        format!("+{}", suffix)
1657    } else if (imag + 1.0).abs() < 1e-15 {
1658        format!("-{}", suffix)
1659    } else if imag > 0.0 {
1660        if imag == imag.trunc() && imag.abs() < 1e15 {
1661            format!("+{}{}", imag as i64, suffix)
1662        } else {
1663            format!("+{}{}", imag, suffix)
1664        }
1665    } else if imag == imag.trunc() && imag.abs() < 1e15 {
1666        format!("{}{}", imag as i64, suffix)
1667    } else {
1668        format!("{}{}", imag, suffix)
1669    };
1670
1671    format!("{}{}", real_str, imag_str)
1672}
1673
1674/// Coerce a LiteralValue to a complex number string
1675fn coerce_complex_str(v: &LiteralValue) -> Result<String, ExcelError> {
1676    match v {
1677        LiteralValue::Text(s) => Ok(s.clone()),
1678        LiteralValue::Int(i) => Ok(i.to_string()),
1679        LiteralValue::Number(n) => Ok(n.to_string()),
1680        LiteralValue::Error(e) => Err(e.clone()),
1681        _ => Err(ExcelError::new_value()),
1682    }
1683}
1684
1685/// Three-argument schema for COMPLEX function
1686static ARG_COMPLEX_THREE: std::sync::LazyLock<Vec<ArgSchema>> =
1687    std::sync::LazyLock::new(|| vec![ArgSchema::any(), ArgSchema::any(), ArgSchema::any()]);
1688
1689/// COMPLEX(real_num, i_num, [suffix]) - Converts real and imaginary coefficients into a complex number
1690#[derive(Debug)]
1691pub struct ComplexFn;
1692impl Function for ComplexFn {
1693    func_caps!(PURE);
1694    fn name(&self) -> &'static str {
1695        "COMPLEX"
1696    }
1697    fn min_args(&self) -> usize {
1698        2
1699    }
1700    fn variadic(&self) -> bool {
1701        true
1702    }
1703    fn arg_schema(&self) -> &'static [ArgSchema] {
1704        &ARG_COMPLEX_THREE[..]
1705    }
1706    fn eval<'a, 'b, 'c>(
1707        &self,
1708        args: &'c [ArgumentHandle<'a, 'b>],
1709        _ctx: &dyn FunctionContext<'b>,
1710    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1711        let real = match args[0].value()?.into_literal() {
1712            LiteralValue::Error(e) => {
1713                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1714            }
1715            other => coerce_num(&other)?,
1716        };
1717
1718        let imag = match args[1].value()?.into_literal() {
1719            LiteralValue::Error(e) => {
1720                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1721            }
1722            other => coerce_num(&other)?,
1723        };
1724
1725        let suffix = if args.len() > 2 {
1726            match args[2].value()?.into_literal() {
1727                LiteralValue::Error(e) => {
1728                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1729                }
1730                LiteralValue::Text(s) => {
1731                    let s = s.to_lowercase();
1732                    if s == "i" {
1733                        'i'
1734                    } else if s == "j" {
1735                        'j'
1736                    } else if s.is_empty() {
1737                        'i' // Default to 'i' for empty string
1738                    } else {
1739                        return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1740                            ExcelError::new_value(),
1741                        )));
1742                    }
1743                }
1744                LiteralValue::Empty => 'i',
1745                _ => {
1746                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1747                        ExcelError::new_value(),
1748                    )));
1749                }
1750            }
1751        } else {
1752            'i'
1753        };
1754
1755        let result = format_complex(real, imag, suffix);
1756        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1757    }
1758}
1759
1760/// IMREAL(inumber) - Returns the real coefficient of a complex number
1761#[derive(Debug)]
1762pub struct ImRealFn;
1763impl Function for ImRealFn {
1764    func_caps!(PURE);
1765    fn name(&self) -> &'static str {
1766        "IMREAL"
1767    }
1768    fn min_args(&self) -> usize {
1769        1
1770    }
1771    fn arg_schema(&self) -> &'static [ArgSchema] {
1772        &ARG_ANY_ONE[..]
1773    }
1774    fn eval<'a, 'b, 'c>(
1775        &self,
1776        args: &'c [ArgumentHandle<'a, 'b>],
1777        _ctx: &dyn FunctionContext<'b>,
1778    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1779        let inumber = match args[0].value()?.into_literal() {
1780            LiteralValue::Error(e) => {
1781                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1782            }
1783            other => match coerce_complex_str(&other) {
1784                Ok(s) => s,
1785                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1786            },
1787        };
1788
1789        let (real, _, _) = match parse_complex(&inumber) {
1790            Ok(c) => c,
1791            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1792        };
1793
1794        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(real)))
1795    }
1796}
1797
1798/// IMAGINARY(inumber) - Returns the imaginary coefficient of a complex number
1799#[derive(Debug)]
1800pub struct ImaginaryFn;
1801impl Function for ImaginaryFn {
1802    func_caps!(PURE);
1803    fn name(&self) -> &'static str {
1804        "IMAGINARY"
1805    }
1806    fn min_args(&self) -> usize {
1807        1
1808    }
1809    fn arg_schema(&self) -> &'static [ArgSchema] {
1810        &ARG_ANY_ONE[..]
1811    }
1812    fn eval<'a, 'b, 'c>(
1813        &self,
1814        args: &'c [ArgumentHandle<'a, 'b>],
1815        _ctx: &dyn FunctionContext<'b>,
1816    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1817        let inumber = match args[0].value()?.into_literal() {
1818            LiteralValue::Error(e) => {
1819                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1820            }
1821            other => match coerce_complex_str(&other) {
1822                Ok(s) => s,
1823                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1824            },
1825        };
1826
1827        let (_, imag, _) = match parse_complex(&inumber) {
1828            Ok(c) => c,
1829            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1830        };
1831
1832        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(imag)))
1833    }
1834}
1835
1836/// IMABS(inumber) - Returns the absolute value (modulus) of a complex number
1837#[derive(Debug)]
1838pub struct ImAbsFn;
1839impl Function for ImAbsFn {
1840    func_caps!(PURE);
1841    fn name(&self) -> &'static str {
1842        "IMABS"
1843    }
1844    fn min_args(&self) -> usize {
1845        1
1846    }
1847    fn arg_schema(&self) -> &'static [ArgSchema] {
1848        &ARG_ANY_ONE[..]
1849    }
1850    fn eval<'a, 'b, 'c>(
1851        &self,
1852        args: &'c [ArgumentHandle<'a, 'b>],
1853        _ctx: &dyn FunctionContext<'b>,
1854    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1855        let inumber = match args[0].value()?.into_literal() {
1856            LiteralValue::Error(e) => {
1857                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1858            }
1859            other => match coerce_complex_str(&other) {
1860                Ok(s) => s,
1861                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1862            },
1863        };
1864
1865        let (real, imag, _) = match parse_complex(&inumber) {
1866            Ok(c) => c,
1867            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1868        };
1869
1870        let abs = (real * real + imag * imag).sqrt();
1871        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(abs)))
1872    }
1873}
1874
1875/// IMARGUMENT(inumber) - Returns the argument theta (angle in radians) of a complex number
1876#[derive(Debug)]
1877pub struct ImArgumentFn;
1878impl Function for ImArgumentFn {
1879    func_caps!(PURE);
1880    fn name(&self) -> &'static str {
1881        "IMARGUMENT"
1882    }
1883    fn min_args(&self) -> usize {
1884        1
1885    }
1886    fn arg_schema(&self) -> &'static [ArgSchema] {
1887        &ARG_ANY_ONE[..]
1888    }
1889    fn eval<'a, 'b, 'c>(
1890        &self,
1891        args: &'c [ArgumentHandle<'a, 'b>],
1892        _ctx: &dyn FunctionContext<'b>,
1893    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1894        let inumber = match args[0].value()?.into_literal() {
1895            LiteralValue::Error(e) => {
1896                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1897            }
1898            other => match coerce_complex_str(&other) {
1899                Ok(s) => s,
1900                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1901            },
1902        };
1903
1904        let (real, imag, _) = match parse_complex(&inumber) {
1905            Ok(c) => c,
1906            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1907        };
1908
1909        // Excel returns #DIV/0! for IMARGUMENT(0)
1910        if real.abs() < 1e-15 && imag.abs() < 1e-15 {
1911            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
1912                ExcelError::new_div(),
1913            )));
1914        }
1915
1916        let arg = imag.atan2(real);
1917        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(arg)))
1918    }
1919}
1920
1921/// IMCONJUGATE(inumber) - Returns the complex conjugate of a complex number
1922#[derive(Debug)]
1923pub struct ImConjugateFn;
1924impl Function for ImConjugateFn {
1925    func_caps!(PURE);
1926    fn name(&self) -> &'static str {
1927        "IMCONJUGATE"
1928    }
1929    fn min_args(&self) -> usize {
1930        1
1931    }
1932    fn arg_schema(&self) -> &'static [ArgSchema] {
1933        &ARG_ANY_ONE[..]
1934    }
1935    fn eval<'a, 'b, 'c>(
1936        &self,
1937        args: &'c [ArgumentHandle<'a, 'b>],
1938        _ctx: &dyn FunctionContext<'b>,
1939    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1940        let inumber = match args[0].value()?.into_literal() {
1941            LiteralValue::Error(e) => {
1942                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
1943            }
1944            other => match coerce_complex_str(&other) {
1945                Ok(s) => s,
1946                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1947            },
1948        };
1949
1950        let (real, imag, suffix) = match parse_complex(&inumber) {
1951            Ok(c) => c,
1952            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
1953        };
1954
1955        let result = format_complex(real, -imag, suffix);
1956        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
1957    }
1958}
1959
1960/// Helper to check if two complex numbers have compatible suffixes
1961fn check_suffix_compatibility(s1: char, s2: char) -> Result<char, ExcelError> {
1962    // If both have the same suffix, use it
1963    // If one is from a purely real number (default 'i'), use the other's suffix
1964    // Excel doesn't allow mixing 'i' and 'j' when both are explicit
1965    if s1 == s2 {
1966        Ok(s1)
1967    } else {
1968        // For simplicity, treat 'i' as the default and allow mixed
1969        // In strict Excel mode, this would error
1970        Ok(s1)
1971    }
1972}
1973
1974/// IMSUM(inumber1, [inumber2], ...) - Returns the sum of complex numbers
1975#[derive(Debug)]
1976pub struct ImSumFn;
1977impl Function for ImSumFn {
1978    func_caps!(PURE);
1979    fn name(&self) -> &'static str {
1980        "IMSUM"
1981    }
1982    fn min_args(&self) -> usize {
1983        1
1984    }
1985    fn variadic(&self) -> bool {
1986        true
1987    }
1988    fn arg_schema(&self) -> &'static [ArgSchema] {
1989        &ARG_ANY_ONE[..]
1990    }
1991    fn eval<'a, 'b, 'c>(
1992        &self,
1993        args: &'c [ArgumentHandle<'a, 'b>],
1994        _ctx: &dyn FunctionContext<'b>,
1995    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
1996        let mut sum_real = 0.0;
1997        let mut sum_imag = 0.0;
1998        let mut result_suffix = 'i';
1999        let mut first = true;
2000
2001        for arg in args {
2002            let inumber = match arg.value()?.into_literal() {
2003                LiteralValue::Error(e) => {
2004                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2005                }
2006                other => match coerce_complex_str(&other) {
2007                    Ok(s) => s,
2008                    Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2009                },
2010            };
2011
2012            let (real, imag, suffix) = match parse_complex(&inumber) {
2013                Ok(c) => c,
2014                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2015            };
2016
2017            if first {
2018                result_suffix = suffix;
2019                first = false;
2020            } else {
2021                result_suffix = check_suffix_compatibility(result_suffix, suffix)?;
2022            }
2023
2024            sum_real += real;
2025            sum_imag += imag;
2026        }
2027
2028        let result = format_complex(sum_real, sum_imag, result_suffix);
2029        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2030    }
2031}
2032
2033/// IMSUB(inumber1, inumber2) - Returns the difference of two complex numbers
2034#[derive(Debug)]
2035pub struct ImSubFn;
2036impl Function for ImSubFn {
2037    func_caps!(PURE);
2038    fn name(&self) -> &'static str {
2039        "IMSUB"
2040    }
2041    fn min_args(&self) -> usize {
2042        2
2043    }
2044    fn arg_schema(&self) -> &'static [ArgSchema] {
2045        &ARG_ANY_TWO[..]
2046    }
2047    fn eval<'a, 'b, 'c>(
2048        &self,
2049        args: &'c [ArgumentHandle<'a, 'b>],
2050        _ctx: &dyn FunctionContext<'b>,
2051    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2052        let inumber1 = match args[0].value()?.into_literal() {
2053            LiteralValue::Error(e) => {
2054                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2055            }
2056            other => match coerce_complex_str(&other) {
2057                Ok(s) => s,
2058                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2059            },
2060        };
2061
2062        let inumber2 = match args[1].value()?.into_literal() {
2063            LiteralValue::Error(e) => {
2064                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2065            }
2066            other => match coerce_complex_str(&other) {
2067                Ok(s) => s,
2068                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2069            },
2070        };
2071
2072        let (real1, imag1, suffix1) = match parse_complex(&inumber1) {
2073            Ok(c) => c,
2074            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2075        };
2076
2077        let (real2, imag2, suffix2) = match parse_complex(&inumber2) {
2078            Ok(c) => c,
2079            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2080        };
2081
2082        let result_suffix = check_suffix_compatibility(suffix1, suffix2)?;
2083        let result = format_complex(real1 - real2, imag1 - imag2, result_suffix);
2084        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2085    }
2086}
2087
2088/// IMPRODUCT(inumber1, [inumber2], ...) - Returns the product of complex numbers
2089#[derive(Debug)]
2090pub struct ImProductFn;
2091impl Function for ImProductFn {
2092    func_caps!(PURE);
2093    fn name(&self) -> &'static str {
2094        "IMPRODUCT"
2095    }
2096    fn min_args(&self) -> usize {
2097        1
2098    }
2099    fn variadic(&self) -> bool {
2100        true
2101    }
2102    fn arg_schema(&self) -> &'static [ArgSchema] {
2103        &ARG_ANY_ONE[..]
2104    }
2105    fn eval<'a, 'b, 'c>(
2106        &self,
2107        args: &'c [ArgumentHandle<'a, 'b>],
2108        _ctx: &dyn FunctionContext<'b>,
2109    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2110        let mut prod_real = 1.0;
2111        let mut prod_imag = 0.0;
2112        let mut result_suffix = 'i';
2113        let mut first = true;
2114
2115        for arg in args {
2116            let inumber = match arg.value()?.into_literal() {
2117                LiteralValue::Error(e) => {
2118                    return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2119                }
2120                other => match coerce_complex_str(&other) {
2121                    Ok(s) => s,
2122                    Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2123                },
2124            };
2125
2126            let (real, imag, suffix) = match parse_complex(&inumber) {
2127                Ok(c) => c,
2128                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2129            };
2130
2131            if first {
2132                result_suffix = suffix;
2133                prod_real = real;
2134                prod_imag = imag;
2135                first = false;
2136            } else {
2137                result_suffix = check_suffix_compatibility(result_suffix, suffix)?;
2138                // (a + bi) * (c + di) = (ac - bd) + (ad + bc)i
2139                let new_real = prod_real * real - prod_imag * imag;
2140                let new_imag = prod_real * imag + prod_imag * real;
2141                prod_real = new_real;
2142                prod_imag = new_imag;
2143            }
2144        }
2145
2146        let result = format_complex(prod_real, prod_imag, result_suffix);
2147        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2148    }
2149}
2150
2151/// IMDIV(inumber1, inumber2) - Returns the quotient of two complex numbers
2152#[derive(Debug)]
2153pub struct ImDivFn;
2154impl Function for ImDivFn {
2155    func_caps!(PURE);
2156    fn name(&self) -> &'static str {
2157        "IMDIV"
2158    }
2159    fn min_args(&self) -> usize {
2160        2
2161    }
2162    fn arg_schema(&self) -> &'static [ArgSchema] {
2163        &ARG_ANY_TWO[..]
2164    }
2165    fn eval<'a, 'b, 'c>(
2166        &self,
2167        args: &'c [ArgumentHandle<'a, 'b>],
2168        _ctx: &dyn FunctionContext<'b>,
2169    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2170        let inumber1 = match args[0].value()?.into_literal() {
2171            LiteralValue::Error(e) => {
2172                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2173            }
2174            other => match coerce_complex_str(&other) {
2175                Ok(s) => s,
2176                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2177            },
2178        };
2179
2180        let inumber2 = match args[1].value()?.into_literal() {
2181            LiteralValue::Error(e) => {
2182                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2183            }
2184            other => match coerce_complex_str(&other) {
2185                Ok(s) => s,
2186                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2187            },
2188        };
2189
2190        let (a, b, suffix1) = match parse_complex(&inumber1) {
2191            Ok(c) => c,
2192            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2193        };
2194
2195        let (c, d, suffix2) = match parse_complex(&inumber2) {
2196            Ok(c) => c,
2197            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2198        };
2199
2200        // Division by zero check - returns #DIV/0! for Excel compatibility
2201        let denom = c * c + d * d;
2202        if denom.abs() < 1e-15 {
2203            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2204                ExcelError::new_div(),
2205            )));
2206        }
2207
2208        let result_suffix = check_suffix_compatibility(suffix1, suffix2)?;
2209
2210        // (a + bi) / (c + di) = ((ac + bd) + (bc - ad)i) / (c^2 + d^2)
2211        let real = (a * c + b * d) / denom;
2212        let imag = (b * c - a * d) / denom;
2213
2214        let result = format_complex(real, imag, result_suffix);
2215        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2216    }
2217}
2218
2219/// IMEXP(inumber) - Returns exponential of a complex number
2220/// e^(a+bi) = e^a * (cos(b) + i*sin(b))
2221#[derive(Debug)]
2222pub struct ImExpFn;
2223impl Function for ImExpFn {
2224    func_caps!(PURE);
2225    fn name(&self) -> &'static str {
2226        "IMEXP"
2227    }
2228    fn min_args(&self) -> usize {
2229        1
2230    }
2231    fn arg_schema(&self) -> &'static [ArgSchema] {
2232        &ARG_ANY_ONE[..]
2233    }
2234    fn eval<'a, 'b, 'c>(
2235        &self,
2236        args: &'c [ArgumentHandle<'a, 'b>],
2237        _ctx: &dyn FunctionContext<'b>,
2238    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2239        let inumber = match args[0].value()?.into_literal() {
2240            LiteralValue::Error(e) => {
2241                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2242            }
2243            other => match coerce_complex_str(&other) {
2244                Ok(s) => s,
2245                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2246            },
2247        };
2248
2249        let (a, b, suffix) = match parse_complex(&inumber) {
2250            Ok(c) => c,
2251            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2252        };
2253
2254        // e^(a+bi) = e^a * (cos(b) + i*sin(b))
2255        let exp_a = a.exp();
2256        let real = exp_a * b.cos();
2257        let imag = exp_a * b.sin();
2258
2259        let result = format_complex(real, imag, suffix);
2260        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2261    }
2262}
2263
2264/// IMLN(inumber) - Returns the natural logarithm of a complex number
2265/// ln(a+bi) = ln(|z|) + i*arg(z)
2266#[derive(Debug)]
2267pub struct ImLnFn;
2268impl Function for ImLnFn {
2269    func_caps!(PURE);
2270    fn name(&self) -> &'static str {
2271        "IMLN"
2272    }
2273    fn min_args(&self) -> usize {
2274        1
2275    }
2276    fn arg_schema(&self) -> &'static [ArgSchema] {
2277        &ARG_ANY_ONE[..]
2278    }
2279    fn eval<'a, 'b, 'c>(
2280        &self,
2281        args: &'c [ArgumentHandle<'a, 'b>],
2282        _ctx: &dyn FunctionContext<'b>,
2283    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2284        let inumber = match args[0].value()?.into_literal() {
2285            LiteralValue::Error(e) => {
2286                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2287            }
2288            other => match coerce_complex_str(&other) {
2289                Ok(s) => s,
2290                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2291            },
2292        };
2293
2294        let (a, b, suffix) = match parse_complex(&inumber) {
2295            Ok(c) => c,
2296            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2297        };
2298
2299        // ln(0) is undefined
2300        let modulus = (a * a + b * b).sqrt();
2301        if modulus < 1e-15 {
2302            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2303                ExcelError::new_num(),
2304            )));
2305        }
2306
2307        // ln(z) = ln(|z|) + i*arg(z)
2308        let real = modulus.ln();
2309        let imag = b.atan2(a);
2310
2311        let result = format_complex(real, imag, suffix);
2312        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2313    }
2314}
2315
2316/// IMLOG10(inumber) - Returns the base-10 logarithm of a complex number
2317/// log10(z) = ln(z) / ln(10)
2318#[derive(Debug)]
2319pub struct ImLog10Fn;
2320impl Function for ImLog10Fn {
2321    func_caps!(PURE);
2322    fn name(&self) -> &'static str {
2323        "IMLOG10"
2324    }
2325    fn min_args(&self) -> usize {
2326        1
2327    }
2328    fn arg_schema(&self) -> &'static [ArgSchema] {
2329        &ARG_ANY_ONE[..]
2330    }
2331    fn eval<'a, 'b, 'c>(
2332        &self,
2333        args: &'c [ArgumentHandle<'a, 'b>],
2334        _ctx: &dyn FunctionContext<'b>,
2335    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2336        let inumber = match args[0].value()?.into_literal() {
2337            LiteralValue::Error(e) => {
2338                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2339            }
2340            other => match coerce_complex_str(&other) {
2341                Ok(s) => s,
2342                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2343            },
2344        };
2345
2346        let (a, b, suffix) = match parse_complex(&inumber) {
2347            Ok(c) => c,
2348            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2349        };
2350
2351        // log10(0) is undefined
2352        let modulus = (a * a + b * b).sqrt();
2353        if modulus < 1e-15 {
2354            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2355                ExcelError::new_num(),
2356            )));
2357        }
2358
2359        // log10(z) = ln(z) / ln(10) = (ln(|z|) + i*arg(z)) / ln(10)
2360        let ln10 = 10.0_f64.ln();
2361        let real = modulus.ln() / ln10;
2362        let imag = b.atan2(a) / ln10;
2363
2364        let result = format_complex(real, imag, suffix);
2365        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2366    }
2367}
2368
2369/// IMLOG2(inumber) - Returns the base-2 logarithm of a complex number
2370/// log2(z) = ln(z) / ln(2)
2371#[derive(Debug)]
2372pub struct ImLog2Fn;
2373impl Function for ImLog2Fn {
2374    func_caps!(PURE);
2375    fn name(&self) -> &'static str {
2376        "IMLOG2"
2377    }
2378    fn min_args(&self) -> usize {
2379        1
2380    }
2381    fn arg_schema(&self) -> &'static [ArgSchema] {
2382        &ARG_ANY_ONE[..]
2383    }
2384    fn eval<'a, 'b, 'c>(
2385        &self,
2386        args: &'c [ArgumentHandle<'a, 'b>],
2387        _ctx: &dyn FunctionContext<'b>,
2388    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2389        let inumber = match args[0].value()?.into_literal() {
2390            LiteralValue::Error(e) => {
2391                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2392            }
2393            other => match coerce_complex_str(&other) {
2394                Ok(s) => s,
2395                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2396            },
2397        };
2398
2399        let (a, b, suffix) = match parse_complex(&inumber) {
2400            Ok(c) => c,
2401            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2402        };
2403
2404        // log2(0) is undefined
2405        let modulus = (a * a + b * b).sqrt();
2406        if modulus < 1e-15 {
2407            return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2408                ExcelError::new_num(),
2409            )));
2410        }
2411
2412        // log2(z) = ln(z) / ln(2) = (ln(|z|) + i*arg(z)) / ln(2)
2413        let ln2 = 2.0_f64.ln();
2414        let real = modulus.ln() / ln2;
2415        let imag = b.atan2(a) / ln2;
2416
2417        let result = format_complex(real, imag, suffix);
2418        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2419    }
2420}
2421
2422/// IMPOWER(inumber, n) - Returns a complex number raised to a power
2423/// z^n = |z|^n * (cos(n*theta) + i*sin(n*theta))
2424#[derive(Debug)]
2425pub struct ImPowerFn;
2426impl Function for ImPowerFn {
2427    func_caps!(PURE);
2428    fn name(&self) -> &'static str {
2429        "IMPOWER"
2430    }
2431    fn min_args(&self) -> usize {
2432        2
2433    }
2434    fn arg_schema(&self) -> &'static [ArgSchema] {
2435        &ARG_ANY_TWO[..]
2436    }
2437    fn eval<'a, 'b, 'c>(
2438        &self,
2439        args: &'c [ArgumentHandle<'a, 'b>],
2440        _ctx: &dyn FunctionContext<'b>,
2441    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2442        let inumber = match args[0].value()?.into_literal() {
2443            LiteralValue::Error(e) => {
2444                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2445            }
2446            other => match coerce_complex_str(&other) {
2447                Ok(s) => s,
2448                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2449            },
2450        };
2451
2452        let n = match args[1].value()?.into_literal() {
2453            LiteralValue::Error(e) => {
2454                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2455            }
2456            other => coerce_num(&other)?,
2457        };
2458
2459        let (a, b, suffix) = match parse_complex(&inumber) {
2460            Ok(c) => c,
2461            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2462        };
2463
2464        let modulus = (a * a + b * b).sqrt();
2465        let theta = b.atan2(a);
2466
2467        // Handle 0^n cases
2468        if modulus < 1e-15 {
2469            if n > 0.0 {
2470                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(
2471                    "0".to_string(),
2472                )));
2473            } else {
2474                // 0^0 or 0^negative is undefined
2475                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2476                    ExcelError::new_num(),
2477                )));
2478            }
2479        }
2480
2481        // z^n = |z|^n * (cos(n*theta) + i*sin(n*theta))
2482        let r_n = modulus.powf(n);
2483        let n_theta = n * theta;
2484        let real = r_n * n_theta.cos();
2485        let imag = r_n * n_theta.sin();
2486
2487        let result = format_complex(real, imag, suffix);
2488        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2489    }
2490}
2491
2492/// IMSQRT(inumber) - Returns the square root of a complex number
2493/// sqrt(z) = sqrt(|z|) * (cos(theta/2) + i*sin(theta/2))
2494#[derive(Debug)]
2495pub struct ImSqrtFn;
2496impl Function for ImSqrtFn {
2497    func_caps!(PURE);
2498    fn name(&self) -> &'static str {
2499        "IMSQRT"
2500    }
2501    fn min_args(&self) -> usize {
2502        1
2503    }
2504    fn arg_schema(&self) -> &'static [ArgSchema] {
2505        &ARG_ANY_ONE[..]
2506    }
2507    fn eval<'a, 'b, 'c>(
2508        &self,
2509        args: &'c [ArgumentHandle<'a, 'b>],
2510        _ctx: &dyn FunctionContext<'b>,
2511    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2512        let inumber = match args[0].value()?.into_literal() {
2513            LiteralValue::Error(e) => {
2514                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2515            }
2516            other => match coerce_complex_str(&other) {
2517                Ok(s) => s,
2518                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2519            },
2520        };
2521
2522        let (a, b, suffix) = match parse_complex(&inumber) {
2523            Ok(c) => c,
2524            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2525        };
2526
2527        let modulus = (a * a + b * b).sqrt();
2528        let theta = b.atan2(a);
2529
2530        // sqrt(z) = sqrt(|z|) * (cos(theta/2) + i*sin(theta/2))
2531        let sqrt_r = modulus.sqrt();
2532        let half_theta = theta / 2.0;
2533        let real = sqrt_r * half_theta.cos();
2534        let imag = sqrt_r * half_theta.sin();
2535
2536        let result = format_complex(real, imag, suffix);
2537        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2538    }
2539}
2540
2541/// IMSIN(inumber) - Returns the sine of a complex number
2542/// sin(a+bi) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
2543#[derive(Debug)]
2544pub struct ImSinFn;
2545impl Function for ImSinFn {
2546    func_caps!(PURE);
2547    fn name(&self) -> &'static str {
2548        "IMSIN"
2549    }
2550    fn min_args(&self) -> usize {
2551        1
2552    }
2553    fn arg_schema(&self) -> &'static [ArgSchema] {
2554        &ARG_ANY_ONE[..]
2555    }
2556    fn eval<'a, 'b, 'c>(
2557        &self,
2558        args: &'c [ArgumentHandle<'a, 'b>],
2559        _ctx: &dyn FunctionContext<'b>,
2560    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2561        let inumber = match args[0].value()?.into_literal() {
2562            LiteralValue::Error(e) => {
2563                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2564            }
2565            other => match coerce_complex_str(&other) {
2566                Ok(s) => s,
2567                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2568            },
2569        };
2570
2571        let (a, b, suffix) = match parse_complex(&inumber) {
2572            Ok(c) => c,
2573            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2574        };
2575
2576        // sin(a+bi) = sin(a)*cosh(b) + i*cos(a)*sinh(b)
2577        let real = a.sin() * b.cosh();
2578        let imag = a.cos() * b.sinh();
2579
2580        let result = format_complex(real, imag, suffix);
2581        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2582    }
2583}
2584
2585/// IMCOS(inumber) - Returns the cosine of a complex number
2586/// cos(a+bi) = cos(a)*cosh(b) - i*sin(a)*sinh(b)
2587#[derive(Debug)]
2588pub struct ImCosFn;
2589impl Function for ImCosFn {
2590    func_caps!(PURE);
2591    fn name(&self) -> &'static str {
2592        "IMCOS"
2593    }
2594    fn min_args(&self) -> usize {
2595        1
2596    }
2597    fn arg_schema(&self) -> &'static [ArgSchema] {
2598        &ARG_ANY_ONE[..]
2599    }
2600    fn eval<'a, 'b, 'c>(
2601        &self,
2602        args: &'c [ArgumentHandle<'a, 'b>],
2603        _ctx: &dyn FunctionContext<'b>,
2604    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2605        let inumber = match args[0].value()?.into_literal() {
2606            LiteralValue::Error(e) => {
2607                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2608            }
2609            other => match coerce_complex_str(&other) {
2610                Ok(s) => s,
2611                Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2612            },
2613        };
2614
2615        let (a, b, suffix) = match parse_complex(&inumber) {
2616            Ok(c) => c,
2617            Err(e) => return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2618        };
2619
2620        // cos(a+bi) = cos(a)*cosh(b) - i*sin(a)*sinh(b)
2621        let real = a.cos() * b.cosh();
2622        let imag = -a.sin() * b.sinh();
2623
2624        let result = format_complex(real, imag, suffix);
2625        Ok(crate::traits::CalcValue::Scalar(LiteralValue::Text(result)))
2626    }
2627}
2628
2629/* ─────────────────────────── Unit Conversion (CONVERT) ──────────────────────────── */
2630
2631/// Unit categories for CONVERT function
2632#[derive(Clone, Copy, PartialEq, Eq, Debug)]
2633enum UnitCategory {
2634    Length,
2635    Mass,
2636    Temperature,
2637}
2638
2639/// Information about a unit
2640struct UnitInfo {
2641    category: UnitCategory,
2642    /// Conversion factor to base unit (meters for length, grams for mass)
2643    /// For temperature, this is special-cased
2644    to_base: f64,
2645}
2646
2647/// Get unit info for a given unit string
2648fn get_unit_info(unit: &str) -> Option<UnitInfo> {
2649    // Length units (base: meter)
2650    match unit {
2651        // Metric length
2652        "m" => Some(UnitInfo {
2653            category: UnitCategory::Length,
2654            to_base: 1.0,
2655        }),
2656        "km" => Some(UnitInfo {
2657            category: UnitCategory::Length,
2658            to_base: 1000.0,
2659        }),
2660        "cm" => Some(UnitInfo {
2661            category: UnitCategory::Length,
2662            to_base: 0.01,
2663        }),
2664        "mm" => Some(UnitInfo {
2665            category: UnitCategory::Length,
2666            to_base: 0.001,
2667        }),
2668        // Imperial length
2669        "mi" => Some(UnitInfo {
2670            category: UnitCategory::Length,
2671            to_base: 1609.344,
2672        }),
2673        "ft" => Some(UnitInfo {
2674            category: UnitCategory::Length,
2675            to_base: 0.3048,
2676        }),
2677        "in" => Some(UnitInfo {
2678            category: UnitCategory::Length,
2679            to_base: 0.0254,
2680        }),
2681        "yd" => Some(UnitInfo {
2682            category: UnitCategory::Length,
2683            to_base: 0.9144,
2684        }),
2685        "Nmi" => Some(UnitInfo {
2686            category: UnitCategory::Length,
2687            to_base: 1852.0,
2688        }),
2689
2690        // Mass units (base: gram)
2691        "g" => Some(UnitInfo {
2692            category: UnitCategory::Mass,
2693            to_base: 1.0,
2694        }),
2695        "kg" => Some(UnitInfo {
2696            category: UnitCategory::Mass,
2697            to_base: 1000.0,
2698        }),
2699        "mg" => Some(UnitInfo {
2700            category: UnitCategory::Mass,
2701            to_base: 0.001,
2702        }),
2703        "lbm" => Some(UnitInfo {
2704            category: UnitCategory::Mass,
2705            to_base: 453.59237,
2706        }),
2707        "oz" => Some(UnitInfo {
2708            category: UnitCategory::Mass,
2709            to_base: 28.349523125,
2710        }),
2711        "ozm" => Some(UnitInfo {
2712            category: UnitCategory::Mass,
2713            to_base: 28.349523125,
2714        }),
2715        "ton" => Some(UnitInfo {
2716            category: UnitCategory::Mass,
2717            to_base: 907184.74,
2718        }),
2719
2720        // Temperature units (special handling)
2721        "C" | "cel" => Some(UnitInfo {
2722            category: UnitCategory::Temperature,
2723            to_base: 0.0, // Special-cased
2724        }),
2725        "F" | "fah" => Some(UnitInfo {
2726            category: UnitCategory::Temperature,
2727            to_base: 0.0, // Special-cased
2728        }),
2729        "K" | "kel" => Some(UnitInfo {
2730            category: UnitCategory::Temperature,
2731            to_base: 0.0, // Special-cased
2732        }),
2733
2734        _ => None,
2735    }
2736}
2737
2738/// Normalize temperature unit name
2739fn normalize_temp_unit(unit: &str) -> &str {
2740    match unit {
2741        "C" | "cel" => "C",
2742        "F" | "fah" => "F",
2743        "K" | "kel" => "K",
2744        _ => unit,
2745    }
2746}
2747
2748/// Convert temperature between units
2749fn convert_temperature(value: f64, from: &str, to: &str) -> f64 {
2750    let from = normalize_temp_unit(from);
2751    let to = normalize_temp_unit(to);
2752
2753    if from == to {
2754        return value;
2755    }
2756
2757    // First convert to Celsius
2758    let celsius = match from {
2759        "C" => value,
2760        "F" => (value - 32.0) * 5.0 / 9.0,
2761        "K" => value - 273.15,
2762        _ => value,
2763    };
2764
2765    // Then convert from Celsius to target
2766    match to {
2767        "C" => celsius,
2768        "F" => celsius * 9.0 / 5.0 + 32.0,
2769        "K" => celsius + 273.15,
2770        _ => celsius,
2771    }
2772}
2773
2774/// Convert a value between units
2775fn convert_units(value: f64, from: &str, to: &str) -> Result<f64, ExcelError> {
2776    let from_info = get_unit_info(from).ok_or_else(ExcelError::new_na)?;
2777    let to_info = get_unit_info(to).ok_or_else(ExcelError::new_na)?;
2778
2779    // Check category compatibility
2780    if from_info.category != to_info.category {
2781        return Err(ExcelError::new_na());
2782    }
2783
2784    // Handle temperature specially
2785    if from_info.category == UnitCategory::Temperature {
2786        return Ok(convert_temperature(value, from, to));
2787    }
2788
2789    // For other units: convert to base, then to target
2790    let base_value = value * from_info.to_base;
2791    Ok(base_value / to_info.to_base)
2792}
2793
2794/// CONVERT(number, from_unit, to_unit) - Converts between measurement units
2795#[derive(Debug)]
2796pub struct ConvertFn;
2797impl Function for ConvertFn {
2798    func_caps!(PURE);
2799    fn name(&self) -> &'static str {
2800        "CONVERT"
2801    }
2802    fn min_args(&self) -> usize {
2803        3
2804    }
2805    fn arg_schema(&self) -> &'static [ArgSchema] {
2806        &ARG_COMPLEX_THREE[..]
2807    }
2808    fn eval<'a, 'b, 'c>(
2809        &self,
2810        args: &'c [ArgumentHandle<'a, 'b>],
2811        _ctx: &dyn FunctionContext<'b>,
2812    ) -> Result<crate::traits::CalcValue<'b>, ExcelError> {
2813        // Get the number value
2814        let value = match args[0].value()?.into_literal() {
2815            LiteralValue::Error(e) => {
2816                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2817            }
2818            other => coerce_num(&other)?,
2819        };
2820
2821        // Get from_unit
2822        let from_unit = match args[1].value()?.into_literal() {
2823            LiteralValue::Error(e) => {
2824                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2825            }
2826            LiteralValue::Text(s) => s,
2827            _ => {
2828                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2829                    ExcelError::new_na(),
2830                )));
2831            }
2832        };
2833
2834        // Get to_unit
2835        let to_unit = match args[2].value()?.into_literal() {
2836            LiteralValue::Error(e) => {
2837                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e)));
2838            }
2839            LiteralValue::Text(s) => s,
2840            _ => {
2841                return Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(
2842                    ExcelError::new_na(),
2843                )));
2844            }
2845        };
2846
2847        match convert_units(value, &from_unit, &to_unit) {
2848            Ok(result) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Number(
2849                result,
2850            ))),
2851            Err(e) => Ok(crate::traits::CalcValue::Scalar(LiteralValue::Error(e))),
2852        }
2853    }
2854}
2855
2856pub fn register_builtins() {
2857    use std::sync::Arc;
2858    crate::function_registry::register_function(Arc::new(BitAndFn));
2859    crate::function_registry::register_function(Arc::new(BitOrFn));
2860    crate::function_registry::register_function(Arc::new(BitXorFn));
2861    crate::function_registry::register_function(Arc::new(BitLShiftFn));
2862    crate::function_registry::register_function(Arc::new(BitRShiftFn));
2863    crate::function_registry::register_function(Arc::new(Bin2DecFn));
2864    crate::function_registry::register_function(Arc::new(Dec2BinFn));
2865    crate::function_registry::register_function(Arc::new(Hex2DecFn));
2866    crate::function_registry::register_function(Arc::new(Dec2HexFn));
2867    crate::function_registry::register_function(Arc::new(Oct2DecFn));
2868    crate::function_registry::register_function(Arc::new(Dec2OctFn));
2869    crate::function_registry::register_function(Arc::new(Bin2HexFn));
2870    crate::function_registry::register_function(Arc::new(Hex2BinFn));
2871    crate::function_registry::register_function(Arc::new(Bin2OctFn));
2872    crate::function_registry::register_function(Arc::new(Oct2BinFn));
2873    crate::function_registry::register_function(Arc::new(Hex2OctFn));
2874    crate::function_registry::register_function(Arc::new(Oct2HexFn));
2875    crate::function_registry::register_function(Arc::new(DeltaFn));
2876    crate::function_registry::register_function(Arc::new(GestepFn));
2877    crate::function_registry::register_function(Arc::new(ErfFn));
2878    crate::function_registry::register_function(Arc::new(ErfcFn));
2879    crate::function_registry::register_function(Arc::new(ErfPreciseFn));
2880    // Complex number functions
2881    crate::function_registry::register_function(Arc::new(ComplexFn));
2882    crate::function_registry::register_function(Arc::new(ImRealFn));
2883    crate::function_registry::register_function(Arc::new(ImaginaryFn));
2884    crate::function_registry::register_function(Arc::new(ImAbsFn));
2885    crate::function_registry::register_function(Arc::new(ImArgumentFn));
2886    crate::function_registry::register_function(Arc::new(ImConjugateFn));
2887    crate::function_registry::register_function(Arc::new(ImSumFn));
2888    crate::function_registry::register_function(Arc::new(ImSubFn));
2889    crate::function_registry::register_function(Arc::new(ImProductFn));
2890    crate::function_registry::register_function(Arc::new(ImDivFn));
2891    // Complex number math functions
2892    crate::function_registry::register_function(Arc::new(ImExpFn));
2893    crate::function_registry::register_function(Arc::new(ImLnFn));
2894    crate::function_registry::register_function(Arc::new(ImLog10Fn));
2895    crate::function_registry::register_function(Arc::new(ImLog2Fn));
2896    crate::function_registry::register_function(Arc::new(ImPowerFn));
2897    crate::function_registry::register_function(Arc::new(ImSqrtFn));
2898    crate::function_registry::register_function(Arc::new(ImSinFn));
2899    crate::function_registry::register_function(Arc::new(ImCosFn));
2900    // Unit conversion
2901    crate::function_registry::register_function(Arc::new(ConvertFn));
2902}