1use crate::cell::CellValue;
6use crate::error::Result;
7use crate::formula::ast::Expr;
8use crate::formula::eval::{coerce_to_number, coerce_to_string, Evaluator};
9use crate::formula::functions::check_arg_count;
10
11pub fn fn_bin2dec(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
13 check_arg_count("BIN2DEC", args, 1, 1)?;
14 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
15 let s = s.trim();
16 if s.len() > 10 || s.is_empty() || s.chars().any(|c| c != '0' && c != '1') {
17 return Ok(CellValue::Error("#NUM!".to_string()));
18 }
19 let val = i64::from_str_radix(s, 2).unwrap_or(0);
20 let result = if s.len() == 10 && s.starts_with('1') {
21 val - 1024
22 } else {
23 val
24 };
25 Ok(CellValue::Number(result as f64))
26}
27
28pub fn fn_bin2hex(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
30 check_arg_count("BIN2HEX", args, 1, 2)?;
31 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
32 let s = s.trim();
33 if s.len() > 10 || s.is_empty() || s.chars().any(|c| c != '0' && c != '1') {
34 return Ok(CellValue::Error("#NUM!".to_string()));
35 }
36 let val = i64::from_str_radix(s, 2).unwrap_or(0);
37 let result = if s.len() == 10 && s.starts_with('1') {
38 val - 1024
39 } else {
40 val
41 };
42 format_hex(result, args, ctx, 1)
43}
44
45pub fn fn_bin2oct(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
47 check_arg_count("BIN2OCT", args, 1, 2)?;
48 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
49 let s = s.trim();
50 if s.len() > 10 || s.is_empty() || s.chars().any(|c| c != '0' && c != '1') {
51 return Ok(CellValue::Error("#NUM!".to_string()));
52 }
53 let val = i64::from_str_radix(s, 2).unwrap_or(0);
54 let result = if s.len() == 10 && s.starts_with('1') {
55 val - 1024
56 } else {
57 val
58 };
59 format_oct(result, args, ctx, 1)
60}
61
62pub fn fn_dec2bin(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
64 check_arg_count("DEC2BIN", args, 1, 2)?;
65 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)? as i64;
66 if !(-512..=511).contains(&n) {
67 return Ok(CellValue::Error("#NUM!".to_string()));
68 }
69 format_bin(n, args, ctx, 1)
70}
71
72pub fn fn_dec2hex(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
74 check_arg_count("DEC2HEX", args, 1, 2)?;
75 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)? as i64;
76 if !(-549_755_813_888..=549_755_813_887).contains(&n) {
77 return Ok(CellValue::Error("#NUM!".to_string()));
78 }
79 format_hex(n, args, ctx, 1)
80}
81
82pub fn fn_dec2oct(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
84 check_arg_count("DEC2OCT", args, 1, 2)?;
85 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)? as i64;
86 if !(-536_870_912..=536_870_911).contains(&n) {
87 return Ok(CellValue::Error("#NUM!".to_string()));
88 }
89 format_oct(n, args, ctx, 1)
90}
91
92pub fn fn_hex2bin(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
94 check_arg_count("HEX2BIN", args, 1, 2)?;
95 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
96 let s = s.trim();
97 if s.len() > 10 || s.is_empty() || !s.chars().all(|c| c.is_ascii_hexdigit()) {
98 return Ok(CellValue::Error("#NUM!".to_string()));
99 }
100 let val = i64::from_str_radix(s, 16).unwrap_or(0);
101 let result = if val > 0x7FFFFFFFFF {
102 val - 0x10000000000_i64
103 } else {
104 val
105 };
106 if !(-512..=511).contains(&result) {
107 return Ok(CellValue::Error("#NUM!".to_string()));
108 }
109 format_bin(result, args, ctx, 1)
110}
111
112pub fn fn_hex2dec(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
114 check_arg_count("HEX2DEC", args, 1, 1)?;
115 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
116 let s = s.trim();
117 if s.len() > 10 || s.is_empty() || !s.chars().all(|c| c.is_ascii_hexdigit()) {
118 return Ok(CellValue::Error("#NUM!".to_string()));
119 }
120 let val = i64::from_str_radix(s, 16).unwrap_or(0);
121 let result = if val > 0x7FFFFFFFFF {
122 val - 0x10000000000_i64
123 } else {
124 val
125 };
126 Ok(CellValue::Number(result as f64))
127}
128
129pub fn fn_hex2oct(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
131 check_arg_count("HEX2OCT", args, 1, 2)?;
132 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
133 let s = s.trim();
134 if s.len() > 10 || s.is_empty() || !s.chars().all(|c| c.is_ascii_hexdigit()) {
135 return Ok(CellValue::Error("#NUM!".to_string()));
136 }
137 let val = i64::from_str_radix(s, 16).unwrap_or(0);
138 let result = if val > 0x7FFFFFFFFF {
139 val - 0x10000000000_i64
140 } else {
141 val
142 };
143 if !(-536_870_912..=536_870_911).contains(&result) {
144 return Ok(CellValue::Error("#NUM!".to_string()));
145 }
146 format_oct(result, args, ctx, 1)
147}
148
149pub fn fn_oct2bin(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
151 check_arg_count("OCT2BIN", args, 1, 2)?;
152 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
153 let s = s.trim();
154 if s.len() > 10 || s.is_empty() || s.chars().any(|c| !('0'..='7').contains(&c)) {
155 return Ok(CellValue::Error("#NUM!".to_string()));
156 }
157 let val = i64::from_str_radix(s, 8).unwrap_or(0);
158 let result = if val > 0x1FFFFFFF {
160 val - 0x40000000
161 } else {
162 val
163 };
164 if !(-512..=511).contains(&result) {
165 return Ok(CellValue::Error("#NUM!".to_string()));
166 }
167 format_bin(result, args, ctx, 1)
168}
169
170pub fn fn_oct2dec(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
172 check_arg_count("OCT2DEC", args, 1, 1)?;
173 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
174 let s = s.trim();
175 if s.len() > 10 || s.is_empty() || s.chars().any(|c| !('0'..='7').contains(&c)) {
176 return Ok(CellValue::Error("#NUM!".to_string()));
177 }
178 let val = i64::from_str_radix(s, 8).unwrap_or(0);
179 let result = if val > 0x1FFFFFFF {
181 val - 0x40000000
182 } else {
183 val
184 };
185 Ok(CellValue::Number(result as f64))
186}
187
188pub fn fn_oct2hex(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
190 check_arg_count("OCT2HEX", args, 1, 2)?;
191 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
192 let s = s.trim();
193 if s.len() > 10 || s.is_empty() || s.chars().any(|c| !('0'..='7').contains(&c)) {
194 return Ok(CellValue::Error("#NUM!".to_string()));
195 }
196 let val = i64::from_str_radix(s, 8).unwrap_or(0);
197 let result = if val > 0x1FFFFFFF {
199 val - 0x40000000
200 } else {
201 val
202 };
203 format_hex(result, args, ctx, 1)
204}
205
206pub fn fn_delta(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
208 check_arg_count("DELTA", args, 1, 2)?;
209 let n1 = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
210 let n2 = if args.len() > 1 {
211 coerce_to_number(&ctx.eval_expr(&args[1])?)?
212 } else {
213 0.0
214 };
215 Ok(CellValue::Number(if n1 == n2 { 1.0 } else { 0.0 }))
217}
218
219pub fn fn_gestep(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
221 check_arg_count("GESTEP", args, 1, 2)?;
222 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
223 let step = if args.len() > 1 {
224 coerce_to_number(&ctx.eval_expr(&args[1])?)?
225 } else {
226 0.0
227 };
228 Ok(CellValue::Number(if n >= step { 1.0 } else { 0.0 }))
229}
230
231pub fn fn_erf(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
233 check_arg_count("ERF", args, 1, 2)?;
234 let lower = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
235 if args.len() > 1 {
236 let upper = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
237 Ok(CellValue::Number(erf_approx(upper) - erf_approx(lower)))
238 } else {
239 Ok(CellValue::Number(erf_approx(lower)))
240 }
241}
242
243pub fn fn_erfc(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
245 check_arg_count("ERFC", args, 1, 1)?;
246 let x = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
247 Ok(CellValue::Number(1.0 - erf_approx(x)))
248}
249
250pub fn fn_complex(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
252 check_arg_count("COMPLEX", args, 2, 3)?;
253 let real = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
254 let imag = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
255 let suffix = if args.len() > 2 {
256 let s = coerce_to_string(&ctx.eval_expr(&args[2])?);
257 if s != "i" && s != "j" {
258 return Ok(CellValue::Error("#VALUE!".to_string()));
259 }
260 s
261 } else {
262 "i".to_string()
263 };
264 Ok(CellValue::String(format_complex(real, imag, &suffix)))
265}
266
267pub fn fn_imreal(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
269 check_arg_count("IMREAL", args, 1, 1)?;
270 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
271 match parse_complex(&s) {
272 Some((real, _)) => Ok(CellValue::Number(real)),
273 None => Ok(CellValue::Error("#NUM!".to_string())),
274 }
275}
276
277pub fn fn_imaginary(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
279 check_arg_count("IMAGINARY", args, 1, 1)?;
280 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
281 match parse_complex(&s) {
282 Some((_, imag)) => Ok(CellValue::Number(imag)),
283 None => Ok(CellValue::Error("#NUM!".to_string())),
284 }
285}
286
287pub fn fn_imabs(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
289 check_arg_count("IMABS", args, 1, 1)?;
290 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
291 match parse_complex(&s) {
292 Some((r, i)) => Ok(CellValue::Number((r * r + i * i).sqrt())),
293 None => Ok(CellValue::Error("#NUM!".to_string())),
294 }
295}
296
297pub fn fn_imargument(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
299 check_arg_count("IMARGUMENT", args, 1, 1)?;
300 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
301 match parse_complex(&s) {
302 Some((r, i)) => {
303 if r == 0.0 && i == 0.0 {
304 return Ok(CellValue::Error("#DIV/0!".to_string()));
305 }
306 Ok(CellValue::Number(i.atan2(r)))
307 }
308 None => Ok(CellValue::Error("#NUM!".to_string())),
309 }
310}
311
312pub fn fn_imconjugate(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
314 check_arg_count("IMCONJUGATE", args, 1, 1)?;
315 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
316 match parse_complex(&s) {
317 Some((r, i)) => Ok(CellValue::String(format_complex(r, -i, "i"))),
318 None => Ok(CellValue::Error("#NUM!".to_string())),
319 }
320}
321
322pub fn fn_imsum(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
324 check_arg_count("IMSUM", args, 1, 255)?;
325 let mut real_sum = 0.0;
326 let mut imag_sum = 0.0;
327 let values = ctx.flatten_args_to_values(args)?;
328 for v in &values {
329 let s = coerce_to_string(v);
330 match parse_complex(&s) {
331 Some((r, i)) => {
332 real_sum += r;
333 imag_sum += i;
334 }
335 None => return Ok(CellValue::Error("#NUM!".to_string())),
336 }
337 }
338 Ok(CellValue::String(format_complex(real_sum, imag_sum, "i")))
339}
340
341pub fn fn_imsub(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
343 check_arg_count("IMSUB", args, 2, 2)?;
344 let s1 = coerce_to_string(&ctx.eval_expr(&args[0])?);
345 let s2 = coerce_to_string(&ctx.eval_expr(&args[1])?);
346 match (parse_complex(&s1), parse_complex(&s2)) {
347 (Some((r1, i1)), Some((r2, i2))) => {
348 Ok(CellValue::String(format_complex(r1 - r2, i1 - i2, "i")))
349 }
350 _ => Ok(CellValue::Error("#NUM!".to_string())),
351 }
352}
353
354pub fn fn_improduct(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
356 check_arg_count("IMPRODUCT", args, 1, 255)?;
357 let values = ctx.flatten_args_to_values(args)?;
358 let mut real = 1.0;
359 let mut imag = 0.0;
360 for v in &values {
361 let s = coerce_to_string(v);
362 match parse_complex(&s) {
363 Some((r, i)) => {
364 let new_real = real * r - imag * i;
365 let new_imag = real * i + imag * r;
366 real = new_real;
367 imag = new_imag;
368 }
369 None => return Ok(CellValue::Error("#NUM!".to_string())),
370 }
371 }
372 Ok(CellValue::String(format_complex(real, imag, "i")))
373}
374
375pub fn fn_imdiv(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
377 check_arg_count("IMDIV", args, 2, 2)?;
378 let s1 = coerce_to_string(&ctx.eval_expr(&args[0])?);
379 let s2 = coerce_to_string(&ctx.eval_expr(&args[1])?);
380 match (parse_complex(&s1), parse_complex(&s2)) {
381 (Some((r1, i1)), Some((r2, i2))) => {
382 let denom = r2 * r2 + i2 * i2;
383 if denom == 0.0 {
384 return Ok(CellValue::Error("#NUM!".to_string()));
385 }
386 let real = (r1 * r2 + i1 * i2) / denom;
387 let imag = (i1 * r2 - r1 * i2) / denom;
388 Ok(CellValue::String(format_complex(real, imag, "i")))
389 }
390 _ => Ok(CellValue::Error("#NUM!".to_string())),
391 }
392}
393
394pub fn fn_impower(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
396 check_arg_count("IMPOWER", args, 2, 2)?;
397 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
398 let n = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
399 match parse_complex(&s) {
400 Some((r, i)) => {
401 let modulus = (r * r + i * i).sqrt();
402 if modulus == 0.0 {
403 if n > 0.0 {
404 return Ok(CellValue::String("0".to_string()));
405 }
406 return Ok(CellValue::Error("#NUM!".to_string()));
407 }
408 let arg = i.atan2(r);
409 let new_mod = modulus.powf(n);
410 let new_arg = arg * n;
411 let real = new_mod * new_arg.cos();
412 let imag = new_mod * new_arg.sin();
413 Ok(CellValue::String(format_complex(real, imag, "i")))
414 }
415 None => Ok(CellValue::Error("#NUM!".to_string())),
416 }
417}
418
419pub fn fn_imsqrt(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
421 check_arg_count("IMSQRT", args, 1, 1)?;
422 let s = coerce_to_string(&ctx.eval_expr(&args[0])?);
423 match parse_complex(&s) {
424 Some((r, i)) => {
425 let modulus = (r * r + i * i).sqrt();
426 let arg = i.atan2(r);
427 let new_mod = modulus.sqrt();
428 let new_arg = arg / 2.0;
429 let real = new_mod * new_arg.cos();
430 let imag = new_mod * new_arg.sin();
431 Ok(CellValue::String(format_complex(real, imag, "i")))
432 }
433 None => Ok(CellValue::Error("#NUM!".to_string())),
434 }
435}
436
437pub fn fn_convert(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
439 check_arg_count("CONVERT", args, 3, 3)?;
440 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
441 let from = coerce_to_string(&ctx.eval_expr(&args[1])?);
442 let to = coerce_to_string(&ctx.eval_expr(&args[2])?);
443 match convert_units(n, &from, &to) {
444 Some(result) => Ok(CellValue::Number(result)),
445 None => Ok(CellValue::Error("#N/A".to_string())),
446 }
447}
448
449pub fn fn_besseli(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
451 check_arg_count("BESSELI", args, 2, 2)?;
452 let x = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
453 let n = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
454 if n < 0 {
455 return Ok(CellValue::Error("#NUM!".to_string()));
456 }
457 Ok(CellValue::Number(bessel_i(x, n as f64)))
458}
459
460pub fn fn_besselj(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
462 check_arg_count("BESSELJ", args, 2, 2)?;
463 let x = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
464 let n = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
465 if n < 0 {
466 return Ok(CellValue::Error("#NUM!".to_string()));
467 }
468 Ok(CellValue::Number(bessel_j(x, n as f64)))
469}
470
471pub fn fn_besselk(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
473 check_arg_count("BESSELK", args, 2, 2)?;
474 let x = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
475 let n = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
476 if x <= 0.0 || n < 0 {
477 return Ok(CellValue::Error("#NUM!".to_string()));
478 }
479 Ok(CellValue::Number(bessel_k(x, n)))
480}
481
482pub fn fn_bessely(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
484 check_arg_count("BESSELY", args, 2, 2)?;
485 let x = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
486 let n = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
487 if x <= 0.0 || n < 0 {
488 return Ok(CellValue::Error("#NUM!".to_string()));
489 }
490 Ok(CellValue::Number(bessel_y(x, n)))
491}
492
493fn format_bin(n: i64, args: &[Expr], ctx: &mut Evaluator, places_idx: usize) -> Result<CellValue> {
494 let s = if n >= 0 {
495 format!("{n:b}")
496 } else {
497 let bits = (n as u64) & 0x3FF;
498 format!("{bits:010b}")
499 };
500 if args.len() > places_idx {
501 let places = coerce_to_number(&ctx.eval_expr(&args[places_idx])?)? as usize;
502 if places < 1 || (n >= 0 && s.len() > places) {
503 return Ok(CellValue::Error("#NUM!".to_string()));
504 }
505 if n >= 0 {
506 return Ok(CellValue::String(format!("{:0>width$}", s, width = places)));
507 }
508 }
509 Ok(CellValue::String(s))
510}
511
512fn format_hex(n: i64, args: &[Expr], ctx: &mut Evaluator, places_idx: usize) -> Result<CellValue> {
513 let s = if n >= 0 {
514 format!("{n:X}")
515 } else {
516 let bits = (n as u64) & 0xFF_FFFF_FFFF;
517 format!("{bits:010X}")
518 };
519 if args.len() > places_idx {
520 let places = coerce_to_number(&ctx.eval_expr(&args[places_idx])?)? as usize;
521 if places < 1 || (n >= 0 && s.len() > places) {
522 return Ok(CellValue::Error("#NUM!".to_string()));
523 }
524 if n >= 0 {
525 return Ok(CellValue::String(format!("{:0>width$}", s, width = places)));
526 }
527 }
528 Ok(CellValue::String(s))
529}
530
531fn format_oct(n: i64, args: &[Expr], ctx: &mut Evaluator, places_idx: usize) -> Result<CellValue> {
532 let s = if n >= 0 {
533 format!("{n:o}")
534 } else {
535 let bits = (n as u64) & 0x3FFFFFFF;
537 format!("{bits:010o}")
538 };
539 if args.len() > places_idx {
540 let places = coerce_to_number(&ctx.eval_expr(&args[places_idx])?)? as usize;
541 if places < 1 || (n >= 0 && s.len() > places) {
542 return Ok(CellValue::Error("#NUM!".to_string()));
543 }
544 if n >= 0 {
545 return Ok(CellValue::String(format!("{:0>width$}", s, width = places)));
546 }
547 }
548 Ok(CellValue::String(s))
549}
550
551fn erf_approx(x: f64) -> f64 {
552 let sign = if x < 0.0 { -1.0 } else { 1.0 };
553 let x = x.abs();
554 let t = 1.0 / (1.0 + 0.3275911 * x);
555 let poly = t
556 * (0.254829592
557 + t * (-0.284496736 + t * (1.421413741 + t * (-1.453152027 + t * 1.061405429))));
558 sign * (1.0 - poly * (-x * x).exp())
559}
560
561fn parse_complex(s: &str) -> Option<(f64, f64)> {
562 let s = s.trim();
563 if s.is_empty() {
564 return None;
565 }
566 if let Ok(n) = s.parse::<f64>() {
567 return Some((n, 0.0));
568 }
569 if s == "i" || s == "j" {
570 return Some((0.0, 1.0));
571 }
572 if s == "-i" || s == "-j" {
573 return Some((0.0, -1.0));
574 }
575 if s == "+i" || s == "+j" {
576 return Some((0.0, 1.0));
577 }
578 let suffix = if s.ends_with('i') || s.ends_with('j') {
579 s.len() - 1
580 } else {
581 return None;
582 };
583 let body = &s[..suffix];
584 let sign_pos = body.rfind('+').or_else(|| {
585 let rp = body.rfind('-')?;
586 if rp == 0 {
587 None
588 } else {
589 Some(rp)
590 }
591 });
592 match sign_pos {
593 Some(pos) => {
594 let real_str = &body[..pos];
595 let imag_str = &body[pos..];
596 let real = if real_str.is_empty() {
597 0.0
598 } else {
599 real_str.parse::<f64>().ok()?
600 };
601 let imag = if imag_str == "+" || imag_str.is_empty() {
602 1.0
603 } else if imag_str == "-" {
604 -1.0
605 } else {
606 imag_str.parse::<f64>().ok()?
607 };
608 Some((real, imag))
609 }
610 None => {
611 let imag = if body == "+" || body.is_empty() {
612 1.0
613 } else if body == "-" {
614 -1.0
615 } else {
616 body.parse::<f64>().ok()?
617 };
618 Some((0.0, imag))
619 }
620 }
621}
622
623fn format_complex(real: f64, imag: f64, suffix: &str) -> String {
624 let real = clean_float(real);
625 let imag = clean_float(imag);
626 if imag == 0.0 {
627 return format_number(real);
628 }
629 if real == 0.0 {
630 if imag == 1.0 {
631 return suffix.to_string();
632 }
633 if imag == -1.0 {
634 return format!("-{suffix}");
635 }
636 return format!("{}{suffix}", format_number(imag));
637 }
638 let imag_str = if imag == 1.0 {
639 format!("+{suffix}")
640 } else if imag == -1.0 {
641 format!("-{suffix}")
642 } else if imag > 0.0 {
643 format!("+{}{suffix}", format_number(imag))
644 } else {
645 format!("{}{suffix}", format_number(imag))
646 };
647 format!("{}{imag_str}", format_number(real))
648}
649
650fn clean_float(v: f64) -> f64 {
651 if v.abs() < 1e-15 {
652 0.0
653 } else {
654 v
655 }
656}
657
658fn format_number(n: f64) -> String {
659 if n.fract() == 0.0 && n.is_finite() && n.abs() < 1e15 {
660 format!("{}", n as i64)
661 } else {
662 format!("{n}")
663 }
664}
665
666fn convert_units(value: f64, from: &str, to: &str) -> Option<f64> {
667 let from_base = unit_to_base_factor(from)?;
668 let to_base = unit_to_base_factor(to)?;
669 if from_base.1 != to_base.1 {
670 if from_base.1 == "C" && to_base.1 == "C" {
671 return None;
672 }
673 return None;
674 }
675 if from_base.1 == "C" {
676 let celsius = temp_to_celsius(value, from)?;
677 return celsius_to_temp(celsius, to);
678 }
679 Some(value * from_base.0 / to_base.0)
680}
681
682fn temp_to_celsius(value: f64, unit: &str) -> Option<f64> {
683 match unit {
684 "C" | "cel" => Some(value),
685 "F" | "fah" => Some((value - 32.0) * 5.0 / 9.0),
686 "K" | "kel" => Some(value - 273.15),
687 "Rank" => Some((value - 491.67) * 5.0 / 9.0),
688 "Reau" => Some(value * 5.0 / 4.0),
689 _ => None,
690 }
691}
692
693fn celsius_to_temp(celsius: f64, unit: &str) -> Option<f64> {
694 match unit {
695 "C" | "cel" => Some(celsius),
696 "F" | "fah" => Some(celsius * 9.0 / 5.0 + 32.0),
697 "K" | "kel" => Some(celsius + 273.15),
698 "Rank" => Some((celsius + 273.15) * 9.0 / 5.0),
699 "Reau" => Some(celsius * 4.0 / 5.0),
700 _ => None,
701 }
702}
703
704fn unit_to_base_factor(unit: &str) -> Option<(f64, &'static str)> {
705 match unit {
706 "g" => Some((0.001, "mass")),
708 "kg" => Some((1.0, "mass")),
709 "mg" => Some((1e-6, "mass")),
710 "lbm" => Some((0.45359237, "mass")),
711 "ozm" => Some((0.028349523125, "mass")),
712 "stone" => Some((6.35029318, "mass")),
713 "ton" => Some((907.18474, "mass")),
714 "sg" => Some((14.593903, "mass")),
715 "u" => Some((1.66053906660e-27, "mass")),
716 "grain" => Some((6.479891e-5, "mass")),
717 "cwt" | "shweight" => Some((45.359237, "mass")),
718 "uk_cwt" | "lcwt" | "hweight" => Some((50.80234544, "mass")),
719 "LTON" | "brton" => Some((1016.0469088, "mass")),
720
721 "m" => Some((1.0, "dist")),
723 "km" => Some((1000.0, "dist")),
724 "cm" => Some((0.01, "dist")),
725 "mm" => Some((0.001, "dist")),
726 "mi" => Some((1609.344, "dist")),
727 "Nmi" => Some((1852.0, "dist")),
728 "in" => Some((0.0254, "dist")),
729 "ft" => Some((0.3048, "dist")),
730 "yd" => Some((0.9144, "dist")),
731 "ang" => Some((1e-10, "dist")),
732 "ell" => Some((1.143, "dist")),
733 "ly" => Some((9.46073047258e15, "dist")),
734 "parsec" | "pc" => Some((3.08567758149e16, "dist")),
735 "Pica" | "Picapt" => Some((0.00035277778, "dist")),
736 "pica" => Some((0.00423333333, "dist")),
737 "survey_mi" => Some((1609.3472, "dist")),
738
739 "sec" | "s" => Some((1.0, "time")),
741 "min" | "mn" => Some((60.0, "time")),
742 "hr" => Some((3600.0, "time")),
743 "day" | "d" => Some((86400.0, "time")),
744 "yr" => Some((365.25 * 86400.0, "time")),
745
746 "m/s" | "m/sec" => Some((1.0, "speed")),
748 "m/h" | "m/hr" => Some((1.0 / 3600.0, "speed")),
749 "mph" => Some((0.44704, "speed")),
750 "kn" | "admkn" => Some((0.514444444, "speed")),
751
752 "ar" => Some((100.0, "area")),
754 "ha" => Some((10000.0, "area")),
755 "uk_acre" => Some((4046.8564224, "area")),
756 "us_acre" => Some((4046.8726, "area")),
757
758 "l" | "L" | "lt" => Some((1.0, "vol")),
760 "ml" => Some((0.001, "vol")),
761 "gal" => Some((3.78541178, "vol")),
762 "qt" => Some((0.946352946, "vol")),
763 "pt" | "us_pt" => Some((0.473176473, "vol")),
764 "cup" => Some((0.236588236, "vol")),
765 "oz" | "fl_oz" | "us_oz" => Some((0.0295735296, "vol")),
766 "tbs" => Some((0.0147867648, "vol")),
767 "tsp" => Some((0.00492892159, "vol")),
768 "uk_gal" => Some((4.54609, "vol")),
769 "uk_qt" => Some((1.1365225, "vol")),
770 "uk_pt" => Some((0.56826125, "vol")),
771
772 "J" | "j" => Some((1.0, "energy")),
774 "e" => Some((1e-7, "energy")),
775 "cal" => Some((4.1868, "energy")),
776 "eV" | "ev" => Some((1.602176634e-19, "energy")),
777 "HPh" | "hh" => Some((2684519.5, "energy")),
778 "Wh" | "wh" => Some((3600.0, "energy")),
779 "flb" => Some((1.3558179483, "energy")),
780 "BTU" | "btu" => Some((1055.05585262, "energy")),
781
782 "W" | "w" => Some((1.0, "power")),
784 "kW" | "kw" => Some((1000.0, "power")),
785 "HP" | "h" => Some((745.69987158, "power")),
786 "PS" => Some((735.49875, "power")),
787
788 "N" => Some((1.0, "force")),
790 "dyn" | "dy" => Some((1e-5, "force")),
791 "lbf" => Some((4.4482216152605, "force")),
792 "pond" => Some((9.80665e-3, "force")),
793
794 "Pa" | "p" => Some((1.0, "press")),
796 "atm" | "at" => Some((101325.0, "press")),
797 "mmHg" => Some((133.322, "press")),
798 "psi" => Some((6894.757, "press")),
799 "Torr" => Some((133.3224, "press")),
800
801 "C" | "cel" | "F" | "fah" | "K" | "kel" | "Rank" | "Reau" => Some((1.0, "C")),
803
804 "bit" => Some((1.0, "info")),
806 "byte" => Some((8.0, "info")),
807
808 _ => None,
809 }
810}
811
812fn bessel_y(x: f64, n: i32) -> f64 {
813 let nf = n as f64;
816 let eps = 1e-8;
817 let y_plus = {
818 let v = nf + eps;
819 let pi = std::f64::consts::PI;
820 ((v * pi).cos() * bessel_j(x, v) - bessel_j(x, -v)) / (v * pi).sin()
821 };
822 let y_minus = {
823 let v = nf - eps;
824 let pi = std::f64::consts::PI;
825 ((v * pi).cos() * bessel_j(x, v) - bessel_j(x, -v)) / (v * pi).sin()
826 };
827 (y_plus + y_minus) / 2.0
828}
829
830fn bessel_j(x: f64, v: f64) -> f64 {
832 let mut sum = 0.0;
833 for m in 0_i32..50 {
834 let sign = if m % 2 == 0 { 1.0 } else { -1.0 };
835 let numer = (x / 2.0).powf(2.0 * m as f64 + v);
836 let denom = gamma(m as f64 + 1.0) * gamma(m as f64 + v + 1.0);
837 if denom == 0.0 || !numer.is_finite() {
838 break;
839 }
840 sum += sign * numer / denom;
841 }
842 sum
843}
844
845fn bessel_k(x: f64, n: i32) -> f64 {
846 let nf = n as f64;
849 let eps = 1e-8;
850 let pi = std::f64::consts::PI;
851 let k_plus = {
852 let v = nf + eps;
853 pi / 2.0 * (bessel_i(x, -v) - bessel_i(x, v)) / (v * pi).sin()
854 };
855 let k_minus = {
856 let v = nf - eps;
857 pi / 2.0 * (bessel_i(x, -v) - bessel_i(x, v)) / (v * pi).sin()
858 };
859 (k_plus + k_minus) / 2.0
860}
861
862fn bessel_i(x: f64, v: f64) -> f64 {
864 let mut sum = 0.0;
865 for m in 0_i32..50 {
866 let numer = (x / 2.0).powf(2.0 * m as f64 + v);
867 let denom = gamma(m as f64 + 1.0) * gamma(m as f64 + v + 1.0);
868 if denom == 0.0 || !numer.is_finite() {
869 break;
870 }
871 sum += numer / denom;
872 }
873 sum
874}
875
876fn gamma(z: f64) -> f64 {
878 if z < 0.5 {
879 let pi = std::f64::consts::PI;
880 return pi / ((pi * z).sin() * gamma(1.0 - z));
881 }
882 let g = 7.0;
883 #[allow(clippy::excessive_precision)]
884 let c = [
885 0.99999999999980993,
886 676.5203681218851,
887 -1259.1392167224028,
888 771.32342877765313,
889 -176.61502916214059,
890 12.507343278686905,
891 -0.13857109526572012,
892 9.9843695780195716e-6,
893 1.5056327351493116e-7,
894 ];
895 let z = z - 1.0;
896 let mut x = c[0];
897 for (i, &coeff) in c.iter().enumerate().skip(1) {
898 x += coeff / (z + i as f64);
899 }
900 let t = z + g + 0.5;
901 (2.0 * std::f64::consts::PI).sqrt() * t.powf(z + 0.5) * (-t).exp() * x
902}
903
904#[cfg(test)]
905mod tests {
906 use crate::cell::CellValue;
907 use crate::formula::eval::{evaluate, CellSnapshot};
908 use crate::formula::parser::parse_formula;
909
910 fn eval(formula: &str) -> CellValue {
911 let snap = CellSnapshot::new("Sheet1".to_string());
912 let expr = parse_formula(formula).unwrap();
913 evaluate(&expr, &snap).unwrap()
914 }
915
916 fn assert_approx(result: CellValue, expected: f64, tol: f64) {
917 match result {
918 CellValue::Number(n) => {
919 assert!((n - expected).abs() < tol, "expected ~{expected}, got {n}");
920 }
921 other => panic!("expected number ~{expected}, got {other:?}"),
922 }
923 }
924
925 #[test]
926 fn bin2dec_positive() {
927 assert_approx(eval("BIN2DEC(\"1100100\")"), 100.0, 0.01);
928 }
929
930 #[test]
931 fn bin2dec_negative() {
932 assert_approx(eval("BIN2DEC(\"1111111111\")"), -1.0, 0.01);
933 }
934
935 #[test]
936 fn bin2hex_basic() {
937 assert_eq!(
938 eval("BIN2HEX(\"11111011\",4)"),
939 CellValue::String("00FB".to_string())
940 );
941 }
942
943 #[test]
944 fn bin2oct_basic() {
945 assert_eq!(
946 eval("BIN2OCT(\"1001\",4)"),
947 CellValue::String("0011".to_string())
948 );
949 }
950
951 #[test]
952 fn dec2bin_basic() {
953 assert_eq!(eval("DEC2BIN(9)"), CellValue::String("1001".to_string()));
954 }
955
956 #[test]
957 fn dec2bin_negative() {
958 assert_eq!(
959 eval("DEC2BIN(-100)"),
960 CellValue::String("1110011100".to_string())
961 );
962 }
963
964 #[test]
965 fn dec2bin_with_places() {
966 assert_eq!(
967 eval("DEC2BIN(9,8)"),
968 CellValue::String("00001001".to_string())
969 );
970 }
971
972 #[test]
973 fn dec2hex_basic() {
974 assert_eq!(eval("DEC2HEX(100)"), CellValue::String("64".to_string()));
975 }
976
977 #[test]
978 fn dec2hex_negative() {
979 let result = eval("DEC2HEX(-54)");
980 assert_eq!(result, CellValue::String("FFFFFFFFCA".to_string()));
981 }
982
983 #[test]
984 fn dec2oct_basic() {
985 assert_eq!(eval("DEC2OCT(58)"), CellValue::String("72".to_string()));
986 }
987
988 #[test]
989 fn dec2oct_negative() {
990 assert_eq!(
992 eval("DEC2OCT(-1)"),
993 CellValue::String("7777777777".to_string())
994 );
995 assert_eq!(
997 eval("DEC2OCT(-536870912)"),
998 CellValue::String("4000000000".to_string())
999 );
1000 }
1001
1002 #[test]
1003 fn hex2bin_basic() {
1004 assert_eq!(
1005 eval("HEX2BIN(\"F\",8)"),
1006 CellValue::String("00001111".to_string())
1007 );
1008 }
1009
1010 #[test]
1011 fn hex2dec_basic() {
1012 assert_approx(eval("HEX2DEC(\"A5\")"), 165.0, 0.01);
1013 }
1014
1015 #[test]
1016 fn hex2dec_negative() {
1017 assert_approx(eval("HEX2DEC(\"FFFFFFFFFF\")"), -1.0, 0.01);
1018 }
1019
1020 #[test]
1021 fn hex2oct_basic() {
1022 assert_eq!(
1023 eval("HEX2OCT(\"F\",3)"),
1024 CellValue::String("017".to_string())
1025 );
1026 }
1027
1028 #[test]
1029 fn oct2bin_basic() {
1030 assert_eq!(
1031 eval("OCT2BIN(\"3\",4)"),
1032 CellValue::String("0011".to_string())
1033 );
1034 }
1035
1036 #[test]
1037 fn oct2bin_negative() {
1038 assert_eq!(
1040 eval("OCT2BIN(\"7777777000\")"),
1041 CellValue::String("1000000000".to_string())
1042 );
1043 assert_eq!(
1045 eval("OCT2BIN(\"7777777776\")"),
1046 CellValue::String("1111111110".to_string())
1047 );
1048 assert_eq!(
1050 eval("OCT2BIN(\"7777777777\")"),
1051 CellValue::String("1111111111".to_string())
1052 );
1053 }
1054
1055 #[test]
1056 fn oct2dec_basic() {
1057 assert_approx(eval("OCT2DEC(\"54\")"), 44.0, 0.01);
1058 }
1059
1060 #[test]
1061 fn oct2dec_negative() {
1062 assert_approx(eval("OCT2DEC(\"7777777777\")"), -1.0, 0.01);
1064 assert_approx(eval("OCT2DEC(\"4000000000\")"), -536_870_912.0, 0.01);
1066 assert_approx(eval("OCT2DEC(\"7777777000\")"), -512.0, 0.01);
1068 }
1069
1070 #[test]
1071 fn oct2dec_positive_boundary() {
1072 assert_approx(eval("OCT2DEC(\"3777777777\")"), 536_870_911.0, 0.01);
1074 }
1075
1076 #[test]
1077 fn oct2hex_basic() {
1078 assert_eq!(
1079 eval("OCT2HEX(\"100\",4)"),
1080 CellValue::String("0040".to_string())
1081 );
1082 }
1083
1084 #[test]
1085 fn oct2hex_negative() {
1086 assert_eq!(
1088 eval("OCT2HEX(\"7777777777\")"),
1089 CellValue::String("FFFFFFFFFF".to_string())
1090 );
1091 assert_eq!(
1093 eval("OCT2HEX(\"4000000000\")"),
1094 CellValue::String("FFE0000000".to_string())
1095 );
1096 }
1097
1098 #[test]
1099 fn delta_equal() {
1100 assert_approx(eval("DELTA(5,5)"), 1.0, 0.01);
1101 }
1102
1103 #[test]
1104 fn delta_not_equal() {
1105 assert_approx(eval("DELTA(5,4)"), 0.0, 0.01);
1106 }
1107
1108 #[test]
1109 fn delta_default() {
1110 assert_approx(eval("DELTA(0)"), 1.0, 0.01);
1111 }
1112
1113 #[test]
1114 fn delta_distinct_close_values() {
1115 let a = 1.0_f64;
1119 let b = 1.0_f64 + 1e-15;
1120 assert_ne!(a, b);
1121 assert_approx(eval("DELTA(1, 1.000000000000001)"), 0.0, 0.01);
1122 }
1123
1124 #[test]
1125 fn gestep_above() {
1126 assert_approx(eval("GESTEP(5,4)"), 1.0, 0.01);
1127 }
1128
1129 #[test]
1130 fn gestep_below() {
1131 assert_approx(eval("GESTEP(3,4)"), 0.0, 0.01);
1132 }
1133
1134 #[test]
1135 fn gestep_equal() {
1136 assert_approx(eval("GESTEP(4,4)"), 1.0, 0.01);
1137 }
1138
1139 #[test]
1140 fn erf_basic() {
1141 assert_approx(eval("ERF(1)"), 0.8427, 0.001);
1142 }
1143
1144 #[test]
1145 fn erf_range() {
1146 assert_approx(eval("ERF(0,1)"), 0.8427, 0.001);
1147 }
1148
1149 #[test]
1150 fn erfc_basic() {
1151 assert_approx(eval("ERFC(1)"), 0.1573, 0.001);
1152 }
1153
1154 #[test]
1155 fn complex_basic() {
1156 assert_eq!(eval("COMPLEX(3,4)"), CellValue::String("3+4i".to_string()));
1157 }
1158
1159 #[test]
1160 fn complex_real_only() {
1161 assert_eq!(eval("COMPLEX(3,0)"), CellValue::String("3".to_string()));
1162 }
1163
1164 #[test]
1165 fn complex_imag_only() {
1166 assert_eq!(eval("COMPLEX(0,4)"), CellValue::String("4i".to_string()));
1167 }
1168
1169 #[test]
1170 fn complex_negative_imag() {
1171 assert_eq!(eval("COMPLEX(3,-4)"), CellValue::String("3-4i".to_string()));
1172 }
1173
1174 #[test]
1175 fn imreal_basic() {
1176 assert_approx(eval("IMREAL(\"3+4i\")"), 3.0, 0.01);
1177 }
1178
1179 #[test]
1180 fn imaginary_basic() {
1181 assert_approx(eval("IMAGINARY(\"3+4i\")"), 4.0, 0.01);
1182 }
1183
1184 #[test]
1185 fn imabs_basic() {
1186 assert_approx(eval("IMABS(\"3+4i\")"), 5.0, 0.01);
1187 }
1188
1189 #[test]
1190 fn imargument_basic() {
1191 assert_approx(eval("IMARGUMENT(\"3+4i\")"), (4.0_f64).atan2(3.0), 0.001);
1192 }
1193
1194 #[test]
1195 fn imconjugate_basic() {
1196 assert_eq!(
1197 eval("IMCONJUGATE(\"3+4i\")"),
1198 CellValue::String("3-4i".to_string())
1199 );
1200 }
1201
1202 #[test]
1203 fn imsum_basic() {
1204 assert_eq!(
1205 eval("IMSUM(\"3+4i\",\"1-2i\")"),
1206 CellValue::String("4+2i".to_string())
1207 );
1208 }
1209
1210 #[test]
1211 fn imsub_basic() {
1212 assert_eq!(
1213 eval("IMSUB(\"3+4i\",\"1+2i\")"),
1214 CellValue::String("2+2i".to_string())
1215 );
1216 }
1217
1218 #[test]
1219 fn improduct_basic() {
1220 assert_eq!(
1221 eval("IMPRODUCT(\"1+2i\",\"3+4i\")"),
1222 CellValue::String("-5+10i".to_string())
1223 );
1224 }
1225
1226 #[test]
1227 fn imdiv_basic() {
1228 let result = eval("IMDIV(\"2+4i\",\"1+1i\")");
1229 assert_eq!(result, CellValue::String("3+i".to_string()));
1230 }
1231
1232 #[test]
1233 fn impower_basic() {
1234 let result = eval("IMPOWER(\"2+3i\",2)");
1235 if let CellValue::String(s) = &result {
1236 let parsed = super::parse_complex(s).unwrap();
1237 assert!((parsed.0 - (-5.0)).abs() < 0.01);
1238 assert!((parsed.1 - 12.0).abs() < 0.01);
1239 } else {
1240 panic!("expected string, got {result:?}");
1241 }
1242 }
1243
1244 #[test]
1245 fn imsqrt_basic() {
1246 let result = eval("IMSQRT(\"4\")");
1247 if let CellValue::String(s) = &result {
1248 let parsed = super::parse_complex(s).unwrap();
1249 assert!((parsed.0 - 2.0).abs() < 0.01);
1250 assert!(parsed.1.abs() < 0.01);
1251 } else {
1252 panic!("expected string, got {result:?}");
1253 }
1254 }
1255
1256 #[test]
1257 fn convert_length() {
1258 assert_approx(eval("CONVERT(1,\"in\",\"cm\")"), 2.54, 0.001);
1259 }
1260
1261 #[test]
1262 fn convert_weight() {
1263 assert_approx(eval("CONVERT(1,\"lbm\",\"kg\")"), 0.453592, 0.001);
1264 }
1265
1266 #[test]
1267 fn convert_temperature() {
1268 assert_approx(eval("CONVERT(100,\"C\",\"F\")"), 212.0, 0.1);
1269 }
1270
1271 #[test]
1272 fn convert_temperature_k() {
1273 assert_approx(eval("CONVERT(0,\"C\",\"K\")"), 273.15, 0.01);
1274 }
1275
1276 #[test]
1277 fn convert_incompatible() {
1278 assert_eq!(
1279 eval("CONVERT(1,\"in\",\"kg\")"),
1280 CellValue::Error("#N/A".to_string())
1281 );
1282 }
1283
1284 #[test]
1285 fn besselj_basic() {
1286 assert_approx(eval("BESSELJ(1.9,2)"), 0.3295, 0.01);
1287 }
1288
1289 #[test]
1290 fn besseli_basic() {
1291 assert_approx(eval("BESSELI(1.5,1)"), 0.9817, 0.01);
1292 }
1293
1294 #[test]
1295 fn besselj_order_zero() {
1296 assert_approx(eval("BESSELJ(0,0)"), 1.0, 0.01);
1298 }
1299
1300 #[test]
1301 fn besselk_zero_x() {
1302 assert_eq!(eval("BESSELK(0,1)"), CellValue::Error("#NUM!".to_string()));
1303 }
1304
1305 #[test]
1306 fn besselk_integer_order() {
1307 let result = eval("BESSELK(1.5,1)");
1309 if let CellValue::Number(v) = result {
1310 assert!(v.is_finite(), "BESSELK(1.5,1) should be finite, got {v}");
1311 assert!((v - 0.2774).abs() < 0.05);
1312 } else {
1313 panic!("expected number, got {result:?}");
1314 }
1315 }
1316
1317 #[test]
1318 fn bessely_zero_x() {
1319 assert_eq!(eval("BESSELY(0,1)"), CellValue::Error("#NUM!".to_string()));
1320 }
1321
1322 #[test]
1323 fn bessely_integer_order() {
1324 let result = eval("BESSELY(2.5,1)");
1326 if let CellValue::Number(v) = result {
1327 assert!(v.is_finite(), "BESSELY(2.5,1) should be finite, got {v}");
1328 assert!((v - 0.1459).abs() < 0.05);
1329 } else {
1330 panic!("expected number, got {result:?}");
1331 }
1332 }
1333
1334 #[test]
1335 fn bessely_order_zero() {
1336 let result = eval("BESSELY(1,0)");
1338 if let CellValue::Number(v) = result {
1339 assert!(v.is_finite(), "BESSELY(1,0) should be finite, got {v}");
1340 assert!((v - 0.0883).abs() < 0.05);
1341 } else {
1342 panic!("expected number, got {result:?}");
1343 }
1344 }
1345}