Skip to main content

improv_engine/
builtins.rs

1//! Evaluation of the scalar built-in functions in
2//! [`improv_core_model::functions::all`]: one value per argument in,
3//! one value out. The dataflow maps this over aligned cells and the scalar
4//! evaluator calls it directly, so both give bit-identical results.
5//!
6//! Errors are values ([`CellValue::Err`] with an `ERROR.TYPE` code): an
7//! error argument propagates (the first one, by argument order) except into
8//! the functions that inspect errors (`ISERROR`, …); a numeric domain error
9//! is `#NUM!` and a non-finite result is `#NUM!`. Positions are 1-based and
10//! count characters (Unicode scalar values), not bytes.
11
12use crate::CellValue;
13use improv_core_model::FuncId;
14
15/// `ERROR.TYPE` codes, as stored in [`CellValue::Err`]. `0` is Improv's own
16/// generic error (`#ERR`).
17pub mod err {
18    pub const NULL: u8 = 1;
19    pub const DIV0: u8 = 2;
20    pub const VALUE: u8 = 3;
21    pub const REF: u8 = 4;
22    pub const NAME: u8 = 5;
23    pub const NUM: u8 = 6;
24    pub const NA: u8 = 7;
25}
26
27pub(crate) fn e(code: u8) -> CellValue {
28    CellValue::Err(code)
29}
30
31/// A number result: non-finite (overflow, NaN) becomes `#NUM!`.
32pub(crate) fn num(x: f64) -> CellValue {
33    if x.is_finite() {
34        CellValue::num(x)
35    } else {
36        e(err::NUM)
37    }
38}
39
40mod date;
41mod dist_extra;
42mod engineering;
43mod financial;
44mod math_extra;
45mod options;
46pub(crate) mod statistical;
47
48/// The ids that see error arguments instead of propagating them (`ISERROR`
49/// and the other information functions that ask about a value's kind).
50fn inspects_errors(id: u32) -> bool {
51    matches!(id, 50 | 51 | 52 | 55) || date::inspects_errors(id)
52}
53
54/// Evaluate built-in `func` on `args` (already checked against the registry's
55/// arity and types). An unknown id is `#NAME?`.
56pub fn eval(func: FuncId, args: &[CellValue]) -> CellValue {
57    if !inspects_errors(func.0) {
58        if let Some(bad) = args.iter().find(|a| matches!(a, CellValue::Err(_))) {
59            return bad.clone();
60        }
61    }
62    match eval_ok(func.0, args) {
63        Ok(v) => v,
64        Err(code) => e(code),
65    }
66}
67
68pub(crate) type R = Result<CellValue, u8>;
69
70pub(crate) fn n(args: &[CellValue], i: usize) -> Result<f64, u8> {
71    match args.get(i) {
72        Some(CellValue::Num(b)) => Ok(f64::from_bits(*b)),
73        Some(CellValue::Bool(b)) => Ok(if *b { 1.0 } else { 0.0 }),
74        Some(CellValue::Date(ms)) => Ok(*ms as f64),
75        _ => Err(err::VALUE),
76    }
77}
78
79/// Optional numeric argument `i`, or `default`.
80pub(crate) fn n_or(args: &[CellValue], i: usize, default: f64) -> Result<f64, u8> {
81    if args.len() > i {
82        n(args, i)
83    } else {
84        Ok(default)
85    }
86}
87
88// Used by the date and financial families as they land.
89#[allow(dead_code)]
90/// Milliseconds per day.
91pub(crate) const DAY_MS: i64 = 86_400_000;
92// Used by the date and financial families as they land.
93#[allow(dead_code)]
94/// 1899-12-30T00:00Z in epoch milliseconds: spreadsheet serial date 0, so
95/// serial 1 is 1899-12-31 and serial 60 the (fictitious) 1900-02-29 is
96/// skipped as in LibreOffice (serial 61 = 1900-03-01 in both systems).
97pub(crate) const SERIAL_EPOCH_MS: i64 = -2_209_161_600_000;
98
99// Used by the date and financial families as they land.
100#[allow(dead_code)]
101/// A date argument as epoch milliseconds: a date as itself, a number as a
102/// spreadsheet serial date (fractional days are the time of day).
103pub(crate) fn d(args: &[CellValue], i: usize) -> Result<i64, u8> {
104    match args.get(i) {
105        Some(CellValue::Date(ms)) => Ok(*ms),
106        Some(CellValue::Num(b)) => {
107            let serial = f64::from_bits(*b);
108            let ms = serial * DAY_MS as f64;
109            if !ms.is_finite() || ms.abs() > 1e16 {
110                return Err(err::NUM);
111            }
112            Ok(SERIAL_EPOCH_MS + ms.round() as i64)
113        }
114        _ => Err(err::VALUE),
115    }
116}
117
118// Used by the date and financial families as they land.
119#[allow(dead_code)]
120/// Epoch milliseconds as a spreadsheet serial date.
121pub(crate) fn serial(ms: i64) -> f64 {
122    (ms - SERIAL_EPOCH_MS) as f64 / DAY_MS as f64
123}
124
125pub(crate) fn t(args: &[CellValue], i: usize) -> Result<String, u8> {
126    match args.get(i) {
127        Some(v) => text_of(v),
128        None => Err(err::VALUE),
129    }
130}
131
132pub(crate) fn b(args: &[CellValue], i: usize) -> Result<bool, u8> {
133    match args.get(i) {
134        Some(CellValue::Bool(b)) => Ok(*b),
135        Some(CellValue::Num(x)) => Ok(f64::from_bits(*x) != 0.0),
136        _ => Err(err::VALUE),
137    }
138}
139
140/// A value as text, the way `&` and `CONCAT` join it.
141pub(crate) fn text_of(v: &CellValue) -> Result<String, u8> {
142    Ok(match v {
143        CellValue::Text(s) => s.clone(),
144        CellValue::Num(bits) => crate::display_number(f64::from_bits(*bits)),
145        CellValue::Bool(true) => "TRUE".into(),
146        CellValue::Bool(false) => "FALSE".into(),
147        CellValue::Date(_) => v.to_string(),
148        CellValue::Err(c) => return Err(*c),
149    })
150}
151
152pub(crate) fn ok_num(x: f64) -> R {
153    Ok(num(x))
154}
155
156pub(crate) fn ok_text(s: String) -> R {
157    Ok(CellValue::Text(s))
158}
159
160pub(crate) fn domain(ok: bool) -> Result<(), u8> {
161    if ok {
162        Ok(())
163    } else {
164        Err(err::NUM)
165    }
166}
167
168/// A non-negative integer argument (truncated), or `#NUM!`.
169pub(crate) fn count(x: f64) -> Result<u64, u8> {
170    domain((0.0..1e15).contains(&x))?;
171    Ok(x.trunc() as u64)
172}
173
174pub(crate) fn nums(args: &[CellValue]) -> Result<Vec<f64>, u8> {
175    (0..args.len()).map(|i| n(args, i)).collect()
176}
177
178pub(crate) fn bools(args: &[CellValue]) -> Result<Vec<bool>, u8> {
179    (0..args.len()).map(|i| b(args, i)).collect()
180}
181
182/// The id ranges each family module owns (the registry's id plan).
183fn eval_ok(id: u32, a: &[CellValue]) -> R {
184    match id {
185        56..=79 | 330..=399 | 400..=499 => date::eval(id, a),
186        160..=199 | 500..=699 => statistical::eval(id, a),
187        700..=899 => financial::eval(id, a),
188        1300..=1449 => engineering::eval(id, a),
189        1450..=1549 => math_extra::eval(id, a),
190        1550..=1749 => dist_extra::eval(id, a),
191        1750..=1849 => options::eval(id, a),
192        _ => eval_core(id, a),
193    }
194}
195
196fn eval_core(id: u32, a: &[CellValue]) -> R {
197    use std::f64::consts::PI;
198    Ok(match id {
199        // --- aggregation twins ---
200        20 => num(nums(a)?.into_iter().fold(f64::INFINITY, f64::min)),
201        21 => num(nums(a)?.into_iter().fold(f64::NEG_INFINITY, f64::max)),
202        22 => num(nums(a)?.iter().sum()),
203        23 => {
204            let v = nums(a)?;
205            num(v.iter().sum::<f64>() / v.len() as f64)
206        }
207        24 => num(nums(a)?.iter().product()),
208        25 => num(nums(a)?.iter().map(|x| x * x).sum()),
209        26 => CellValue::Bool(bools(a)?.iter().all(|x| *x)),
210        27 => CellValue::Bool(bools(a)?.iter().any(|x| *x)),
211        28 => CellValue::Bool(bools(a)?.iter().filter(|x| **x).count() % 2 == 1),
212        // --- logical ---
213        38 => {
214            let (x, lo, hi) = (n(a, 0)?, n(a, 1)?, n(a, 2)?);
215            let inclusive = if a.len() > 3 { b(a, 3)? } else { true };
216            CellValue::Bool(if inclusive {
217                lo <= x && x <= hi
218            } else {
219                lo < x && x < hi
220            })
221        }
222        39 => CellValue::Bool(true),
223        40 => CellValue::Bool(false),
224        // --- information / errors ---
225        50 => CellValue::Bool(matches!(a[0], CellValue::Err(_))),
226        51 => CellValue::Bool(matches!(a[0], CellValue::Err(c) if c != err::NA)),
227        52 => CellValue::Bool(matches!(a[0], CellValue::Err(err::NA))),
228        53 => return Err(err::NA),
229        54 => {
230            let code = t(a, 0)?;
231            let kind = improv_core_model::ValueErrorKind::from_code(&code).ok_or(err::VALUE)?;
232            return Err(kind.error_type().unwrap_or(err::VALUE));
233        }
234        55 => match a[0] {
235            CellValue::Err(c) if c > 0 => CellValue::num(f64::from(c)),
236            _ => return Err(err::NA),
237        },
238        // --- math ---
239        10 => num(n(a, 0)?.abs()),
240        15 => num(-n(a, 0)?),
241        100 => {
242            let x = n(a, 0)?;
243            num(if x > 0.0 {
244                1.0
245            } else if x < 0.0 {
246                -1.0
247            } else {
248                0.0
249            })
250        }
251        14 => {
252            let x = n(a, 0)?;
253            domain(x >= 0.0)?;
254            num(x.sqrt())
255        }
256        101 => {
257            let x = n(a, 0)?;
258            domain(x >= 0.0)?;
259            num((x * PI).sqrt())
260        }
261        102 => num(n(a, 0)?.exp()),
262        103 => num(n(a, 0)?.exp_m1()),
263        104 => {
264            let x = n(a, 0)?;
265            domain(x > 0.0)?;
266            num(x.ln())
267        }
268        105 => {
269            let x = n(a, 0)?;
270            domain(x > -1.0)?;
271            num(x.ln_1p())
272        }
273        106 => {
274            let (x, base) = (n(a, 0)?, n_or(a, 1, 10.0)?);
275            domain(x > 0.0 && base > 0.0)?;
276            if base == 1.0 {
277                return Err(err::DIV0);
278            }
279            num(if base == 10.0 {
280                x.log10()
281            } else {
282                x.ln() / base.ln()
283            })
284        }
285        107 => {
286            let x = n(a, 0)?;
287            domain(x > 0.0)?;
288            num(x.log10())
289        }
290        108 => {
291            let x = n(a, 0)?;
292            domain(x > 0.0)?;
293            num(x.log2())
294        }
295        109 => return power(n(a, 0)?, n(a, 1)?),
296        110 => {
297            let (x, d) = (n(a, 0)?, n(a, 1)?);
298            if d == 0.0 {
299                return Err(err::DIV0);
300            }
301            num(x - d * (x / d).floor())
302        }
303        111 => {
304            let (x, d) = (n(a, 0)?, n(a, 1)?);
305            if d == 0.0 {
306                return Err(err::DIV0);
307            }
308            num((x / d).trunc())
309        }
310        112 => CellValue::num(PI),
311        113 => num(n(a, 0)?.to_degrees()),
312        114 => num(n(a, 0)?.to_radians()),
313        115 => num(n(a, 0)?.sin()),
314        116 => num(n(a, 0)?.cos()),
315        117 => num(n(a, 0)?.tan()),
316        118 => {
317            let x = n(a, 0)?;
318            domain((-1.0..=1.0).contains(&x))?;
319            num(x.asin())
320        }
321        119 => {
322            let x = n(a, 0)?;
323            domain((-1.0..=1.0).contains(&x))?;
324            num(x.acos())
325        }
326        120 => num(n(a, 0)?.atan()),
327        121 => {
328            let (x, y) = (n(a, 0)?, n(a, 1)?);
329            if x == 0.0 && y == 0.0 {
330                return Err(err::DIV0);
331            }
332            num(y.atan2(x))
333        }
334        122 => num(n(a, 0)?.sinh()),
335        123 => num(n(a, 0)?.cosh()),
336        124 => num(n(a, 0)?.tanh()),
337        125 => num(n(a, 0)?.asinh()),
338        126 => {
339            let x = n(a, 0)?;
340            domain(x >= 1.0)?;
341            num(x.acosh())
342        }
343        127 => {
344            let x = n(a, 0)?;
345            domain(x > -1.0 && x < 1.0)?;
346            num(x.atanh())
347        }
348        128 => recip(trig_arg(n(a, 0)?)?.cos())?,
349        129 => recip(trig_arg(n(a, 0)?)?.sin())?,
350        130 => recip(trig_arg(n(a, 0)?)?.tan())?,
351        131 => recip(n(a, 0)?.cosh())?,
352        132 => recip(n(a, 0)?.sinh())?,
353        133 => recip(n(a, 0)?.tanh())?,
354        134 => num(PI / 2.0 - n(a, 0)?.atan()),
355        135 => {
356            let x = n(a, 0)?;
357            domain(x.abs() > 1.0)?;
358            num(0.5 * ((x + 1.0) / (x - 1.0)).ln())
359        }
360        136 => num(sin_pi(n(a, 0)?)),
361        137 => num(cos_pi(n(a, 0)?)),
362        138 => {
363            let x = n(a, 0)?;
364            let c = cos_pi(x);
365            domain(c != 0.0)?;
366            num(sin_pi(x) / c)
367        }
368        139 => {
369            let x = n(a, 0)?;
370            let s = sin_pi(x);
371            domain(s != 0.0)?;
372            num(cos_pi(x) / s)
373        }
374        140 => num(nums(a)?.into_iter().fold(0.0, f64::hypot)),
375        141 => num(fact(count(n(a, 0)?)?)),
376        142 => {
377            let x = n(a, 0)?;
378            domain(x >= 0.0)?;
379            let k = x.trunc() as i64;
380            num(product((2 - k % 2..=k).rev().step_by(2).map(|i| i as f64)))
381        }
382        143 => {
383            let (nn, k) = (count(n(a, 0)?)?, count(n(a, 1)?)?);
384            domain(k <= nn)?;
385            num(combin(nn, k))
386        }
387        144 => {
388            let (nn, k) = (count(n(a, 0)?)?, count(n(a, 1)?)?);
389            if nn == 0 && k == 0 {
390                CellValue::num(1.0)
391            } else {
392                domain(nn > 0)?;
393                num(combin(nn + k - 1, k))
394            }
395        }
396        145 => {
397            let (nn, k) = (count(n(a, 0)?)?, count(n(a, 1)?)?);
398            domain(k <= nn)?;
399            num(product(((nn - k + 1)..=nn).map(|i| i as f64)))
400        }
401        146 => {
402            let (nn, k) = (count(n(a, 0)?)?, count(n(a, 1)?)?);
403            num((nn as f64).powf(k as f64))
404        }
405        147 => {
406            let v = nums(a)?;
407            let mut g = 0u64;
408            for x in v {
409                g = gcd(g, count(x)?);
410            }
411            CellValue::num(g as f64)
412        }
413        148 => {
414            let v = nums(a)?;
415            let mut l = 1u64;
416            for x in v {
417                let k = count(x)?;
418                if k == 0 {
419                    return Ok(CellValue::num(0.0));
420                }
421                // Exact: #NUM! iff the true lcm reaches 2⁵³ (proven).
422                l = improv_verified::gcd::lcm_below(l, k, 1 << 53).ok_or(err::NUM)?;
423            }
424            CellValue::num(l as f64)
425        }
426        149 => {
427            let v = nums(a)?;
428            // Π C(k₁ + … + kⱼ, kⱼ).
429            let mut total = 0u64;
430            let mut r = 1f64;
431            for x in v {
432                let k = count(x)?;
433                total += k;
434                r *= combin(total, k);
435            }
436            num(r.round())
437        }
438        150 => {
439            let v = nums(a)?;
440            num(v[1..].iter().fold(v[0], |acc, x| acc - x))
441        }
442        151 => {
443            let v = nums(a)?;
444            if v.iter().any(|x| *x < 0.0) {
445                return Err(err::VALUE);
446            }
447            if v.contains(&0.0) {
448                return Ok(CellValue::num(0.0));
449            }
450            num(1.0 / v.iter().map(|x| 1.0 / x).sum::<f64>())
451        }
452        // --- rounding ---
453        11 => num(round_to(n(a, 0)?, n_or(a, 1, 0.0)?, Dir::HalfAway)),
454        200 => num(round_to(n(a, 0)?, n_or(a, 1, 0.0)?, Dir::Away)),
455        201 | 203 => num(round_to(n(a, 0)?, n_or(a, 1, 0.0)?, Dir::Toward)),
456        202 => {
457            let (x, d) = (n(a, 0)?, n(a, 1)?);
458            domain(d >= 1.0)?;
459            if x == 0.0 {
460                CellValue::num(0.0)
461            } else {
462                let digits = d.trunc() - 1.0 - x.abs().log10().floor();
463                num(round_to(x, digits, Dir::HalfAway))
464            }
465        }
466        204 => num(n(a, 0)?.floor()),
467        13 => num(n(a, 0)?.ceil()),
468        12 => {
469            let (x, sig) = (n(a, 0)?, n_or(a, 1, 1.0)?);
470            floor_xl(x, sig)?
471        }
472        205 => {
473            let (x, sig) = (n(a, 0)?, n_or(a, 1, 1.0)?);
474            ceiling_xl(x, sig)?
475        }
476        206 => {
477            let (x, sig, mode) = (n(a, 0)?, n_or(a, 1, 1.0)?, n_or(a, 2, 0.0)?);
478            let sig = sig.abs();
479            if sig == 0.0 {
480                return Ok(CellValue::num(0.0));
481            }
482            num(if x < 0.0 && mode != 0.0 {
483                sig15(clean(x / sig).ceil() * sig)
484            } else {
485                sig15(clean(x / sig).floor() * sig)
486            })
487        }
488        207 => {
489            let (x, sig, mode) = (n(a, 0)?, n_or(a, 1, 1.0)?, n_or(a, 2, 0.0)?);
490            let sig = sig.abs();
491            if sig == 0.0 {
492                return Ok(CellValue::num(0.0));
493            }
494            num(if x < 0.0 && mode != 0.0 {
495                sig15(clean(x / sig).floor() * sig)
496            } else {
497                sig15(clean(x / sig).ceil() * sig)
498            })
499        }
500        208 => {
501            let (x, sig) = (n(a, 0)?, n_or(a, 1, 1.0)?.abs());
502            if sig == 0.0 {
503                return Ok(CellValue::num(0.0));
504            }
505            num(sig15(clean(x / sig).floor() * sig))
506        }
507        209 | 210 => {
508            let (x, sig) = (n(a, 0)?, n_or(a, 1, 1.0)?.abs());
509            if sig == 0.0 {
510                return Ok(CellValue::num(0.0));
511            }
512            num(sig15(clean(x / sig).ceil() * sig))
513        }
514        211 => {
515            let (x, m) = (n(a, 0)?, n(a, 1)?);
516            if m == 0.0 {
517                return Ok(CellValue::num(0.0));
518            }
519            domain(x == 0.0 || (x > 0.0) == (m > 0.0))?;
520            num(sig15(round_to(x / m, 0.0, Dir::HalfAway) * m))
521        }
522        212 => {
523            let x = n(a, 0)?;
524            let r = (x.abs() / 2.0).ceil() * 2.0;
525            num(r.copysign(x))
526        }
527        213 => {
528            let x = n(a, 0)?;
529            let m = x.abs().ceil();
530            let r = if m % 2.0 == 1.0 { m } else { m + 1.0 };
531            num(if x < 0.0 { -r } else { r })
532        }
533        // --- text ---
534        300 => {
535            let c = n(a, 0)?.trunc();
536            if !(1.0..=255.0).contains(&c) {
537                return Err(err::VALUE);
538            }
539            // Codes 128–255 as Latin-1 (Windows-1252 differs only in 128–159).
540            ok_text(char::from_u32(c as u32).ok_or(err::VALUE)?.to_string())?
541        }
542        301 => {
543            let c = n(a, 0)?.trunc();
544            if !(1.0..=1_114_111.0).contains(&c) {
545                return Err(err::VALUE);
546            }
547            ok_text(char::from_u32(c as u32).ok_or(err::VALUE)?.to_string())?
548        }
549        302 | 303 => {
550            let s = t(a, 0)?;
551            let c = s.chars().next().ok_or(err::VALUE)?;
552            CellValue::num(f64::from(c as u32))
553        }
554        304 => ok_text(t(a, 0)?.chars().filter(|c| (*c as u32) >= 32).collect())?,
555        305 | 306 => {
556            let mut s = String::new();
557            for v in a {
558                s.push_str(&text_of(v)?);
559            }
560            ok_text(s)?
561        }
562        307 => CellValue::Bool(t(a, 0)? == t(a, 1)?),
563        308 | 309 => {
564            let (find, text) = (t(a, 0)?, t(a, 1)?);
565            let start = n_or(a, 2, 1.0)?.trunc();
566            let chars: Vec<char> = text.chars().collect();
567            if start < 1.0 || start > chars.len() as f64 + 1.0 {
568                return Err(err::VALUE);
569            }
570            let from = start as usize - 1;
571            let hit = if id == 308 {
572                find_chars(&chars, &find.chars().collect::<Vec<_>>(), from)
573            } else {
574                search_chars(&chars, &find, from)
575            };
576            CellValue::num(hit.ok_or(err::VALUE)? as f64 + 1.0)
577        }
578        310 | 311 => {
579            let s: Vec<char> = t(a, 0)?.chars().collect();
580            let k = n_or(a, 1, 1.0)?;
581            if k < 0.0 {
582                return Err(err::VALUE);
583            }
584            let k = (k.trunc() as usize).min(s.len());
585            let part = if id == 310 {
586                &s[..k]
587            } else {
588                &s[s.len() - k..]
589            };
590            ok_text(part.iter().collect())?
591        }
592        312 => {
593            let s: Vec<char> = t(a, 0)?.chars().collect();
594            let (start, k) = (n(a, 1)?.trunc(), n(a, 2)?.trunc());
595            if start < 1.0 || k < 0.0 {
596                return Err(err::VALUE);
597            }
598            let from = (start as usize - 1).min(s.len());
599            let to = from.saturating_add(k as usize).min(s.len());
600            ok_text(s[from..to].iter().collect())?
601        }
602        313 => CellValue::num(t(a, 0)?.chars().count() as f64),
603        314 => ok_text(t(a, 0)?.to_lowercase())?,
604        315 => ok_text(t(a, 0)?.to_uppercase())?,
605        316 => ok_text(proper(&t(a, 0)?))?,
606        317 => {
607            let s: Vec<char> = t(a, 0)?.chars().collect();
608            let (start, k, new) = (n(a, 1)?.trunc(), n(a, 2)?.trunc(), t(a, 3)?);
609            if start < 1.0 || k < 0.0 {
610                return Err(err::VALUE);
611            }
612            let from = (start as usize - 1).min(s.len());
613            let to = from.saturating_add(k as usize).min(s.len());
614            let mut out: String = s[..from].iter().collect();
615            out.push_str(&new);
616            out.extend(&s[to..]);
617            ok_text(out)?
618        }
619        318 => {
620            let (s, k) = (t(a, 0)?, n(a, 1)?);
621            if k < 0.0 || (s.chars().count() as f64) * k.trunc() > 32_767.0 {
622                return Err(err::VALUE);
623            }
624            ok_text(s.repeat(k.trunc() as usize))?
625        }
626        319 => {
627            let (s, old, new) = (t(a, 0)?, t(a, 1)?, t(a, 2)?);
628            if old.is_empty() {
629                return ok_text(s);
630            }
631            if a.len() > 3 {
632                let inst = n(a, 3)?.trunc();
633                if inst < 1.0 {
634                    return Err(err::VALUE);
635                }
636                match s.match_indices(&old).nth(inst as usize - 1) {
637                    Some((i, _)) => ok_text(format!("{}{new}{}", &s[..i], &s[i + old.len()..]))?,
638                    None => ok_text(s)?,
639                }
640            } else {
641                ok_text(s.replace(&old, &new))?
642            }
643        }
644        320 => ok_text(
645            t(a, 0)?
646                .split(' ')
647                .filter(|w| !w.is_empty())
648                .collect::<Vec<_>>()
649                .join(" "),
650        )?,
651        321 => {
652            let s = t(a, 0)?;
653            num(parse_value(&s).ok_or(err::VALUE)?)
654        }
655        322 | 323 => text_before_after(id == 322, a)?,
656        324 => {
657            let s: Vec<char> = t(a, 0)?.chars().collect();
658            let fold = |v: &[char]| v.iter().map(|c| fold_char(*c)).collect::<Vec<_>>();
659            let hay = fold(&s);
660            let open = fold(&t(a, 1)?.chars().collect::<Vec<_>>());
661            let close = fold(&t(a, 2)?.chars().collect::<Vec<_>>());
662            let i = find_chars(&hay, &open, 0).ok_or(err::NA)? + open.len();
663            let j = find_chars(&hay, &close, i).ok_or(err::NA)?;
664            ok_text(s[i..j].iter().collect())?
665        }
666        325 => {
667            // Numbers counts overlapping, case-insensitive matches.
668            let hay: Vec<char> = t(a, 0)?.chars().map(fold_char).collect();
669            let needle: Vec<char> = t(a, 1)?.chars().map(fold_char).collect();
670            if needle.is_empty() {
671                return Err(err::VALUE);
672            }
673            let hits = hay
674                .windows(needle.len())
675                .filter(|w| *w == needle.as_slice())
676                .count();
677            CellValue::num(hits as f64)
678        }
679        _ => return Err(err::NAME),
680    })
681}
682
683/// `d` (epoch milliseconds) plus `days` (fractional days allowed), or
684/// `#NUM!` when the result leaves the representable range.
685pub(crate) fn date_plus_days(d: i64, days: f64) -> CellValue {
686    let ms = days * 86_400_000.0;
687    if !ms.is_finite() || ms.abs() > 1e17 {
688        return e(err::NUM);
689    }
690    d.checked_add(ms.round() as i64)
691        .filter(|v| chrono::DateTime::<chrono::Utc>::from_timestamp_millis(*v).is_some())
692        .map_or(e(err::NUM), CellValue::Date)
693}
694
695/// `x ^ y` with the spreadsheet edge cases: `0^0` is 1, `0^-1` is `#DIV/0!`,
696/// a negative base with a fractional exponent is `#NUM!`.
697pub(crate) fn power(x: f64, y: f64) -> R {
698    if x == 0.0 && y < 0.0 {
699        return Err(err::DIV0);
700    }
701    if x < 0.0 && y.fract() != 0.0 {
702        return Err(err::NUM);
703    }
704    ok_num(x.powf(y))
705}
706
707/// Excel's SEC/CSC/COT argument limit: |x| < 2²⁷.
708fn trig_arg(x: f64) -> Result<f64, u8> {
709    domain(x.abs() < 134_217_728.0)?;
710    Ok(x)
711}
712
713/// One character, case-folded (lowercased; a multi-char mapping keeps the
714/// first, which leaves character positions intact).
715pub(crate) fn fold_char(c: char) -> char {
716    c.to_lowercase().next().unwrap_or(c)
717}
718
719fn recip(d: f64) -> R {
720    if d == 0.0 {
721        return Err(err::DIV0);
722    }
723    ok_num(1.0 / d)
724}
725
726/// sin(πx), exactly 0 at integers.
727fn sin_pi(x: f64) -> f64 {
728    let r = x.rem_euclid(2.0);
729    if r.fract() == 0.0 {
730        return 0.0;
731    }
732    if r == 0.5 {
733        return 1.0;
734    }
735    if r == 1.5 {
736        return -1.0;
737    }
738    (std::f64::consts::PI * r).sin()
739}
740
741/// cos(πx), exactly 0 at half-integers.
742fn cos_pi(x: f64) -> f64 {
743    let r = x.rem_euclid(2.0);
744    if r == 0.5 || r == 1.5 {
745        return 0.0;
746    }
747    if r == 0.0 {
748        return 1.0;
749    }
750    if r == 1.0 {
751        return -1.0;
752    }
753    (std::f64::consts::PI * r).cos()
754}
755
756fn fact(k: u64) -> f64 {
757    product((1..=k).map(|i| i as f64))
758}
759
760/// The product of factors ≥ 1, stopping once it overflows (so a count near
761/// `count`'s 1e15 limit costs a few hundred steps, not 1e15).
762fn product(factors: impl Iterator<Item = f64>) -> f64 {
763    let mut p = 1f64;
764    for x in factors {
765        p *= x;
766        if p.is_infinite() {
767            break;
768        }
769    }
770    p
771}
772
773fn combin(n: u64, k: u64) -> f64 {
774    let k = k.min(n - k);
775    let mut r = 1f64;
776    // Every factor is ≥ 1 (i < k ≤ n/2), so once r overflows it stays so.
777    for i in 0..k {
778        r = r * (n - i) as f64 / (i + 1) as f64;
779        if r.is_infinite() {
780            break;
781        }
782    }
783    r.round()
784}
785
786/// Euclid's gcd, proven in [`improv_verified::gcd`].
787use improv_verified::gcd::gcd;
788
789#[derive(Clone, Copy)]
790enum Dir {
791    HalfAway,
792    Away,
793    Toward,
794}
795
796/// Round `x` to `digits` decimal places (negative digits round left of the
797/// point). Decimal-correct for the halfway cases people type
798/// (`ROUND(2.675, 2)` is 2.68, as in Excel): the scaled value is first
799/// rounded to 15 significant digits, the precision an `f64` holds in decimal.
800fn round_to(x: f64, digits: f64, dir: Dir) -> f64 {
801    let d = digits.trunc().clamp(-308.0, 308.0) as i32;
802    let scale = 10f64.powi(d.abs());
803    let y = if d >= 0 { x * scale } else { x / scale };
804    let y = if y.abs() < 1e15 {
805        format!("{y:.14e}").parse::<f64>().unwrap_or(y)
806    } else {
807        y
808    };
809    let r = match dir {
810        Dir::HalfAway => y.round(),
811        Dir::Away => y.abs().ceil().copysign(y),
812        Dir::Toward => y.trunc(),
813    };
814    sig15(if d >= 0 { r / scale } else { r * scale })
815}
816
817/// Excel `FLOOR(x, sig)`: down to a multiple of sig (`FLOOR(-2.5, 2)` is −4,
818/// `FLOOR(-2.5, -2)` is −2); a positive x with a negative sig is `#NUM!`.
819fn floor_xl(x: f64, sig: f64) -> R {
820    if sig == 0.0 {
821        return if x == 0.0 {
822            Ok(CellValue::num(0.0))
823        } else {
824            Err(err::DIV0)
825        };
826    }
827    if x > 0.0 && sig < 0.0 {
828        return Err(err::NUM);
829    }
830    ok_num(sig15(clean(x / sig).floor() * sig))
831}
832
833/// Excel `CEILING(x, sig)`.
834fn ceiling_xl(x: f64, sig: f64) -> R {
835    if sig == 0.0 {
836        return Ok(CellValue::num(0.0));
837    }
838    if x > 0.0 && sig < 0.0 {
839        return Err(err::NUM);
840    }
841    ok_num(sig15(clean(x / sig).ceil() * sig))
842}
843
844/// `x` rounded to 15 significant digits: a multiple of a decimal step
845/// (`3 × 0.1`) shows as the decimal people expect (0.3, not
846/// 0.30000000000000004), as the spreadsheets do.
847fn sig15(x: f64) -> f64 {
848    if x == 0.0 || !x.is_finite() {
849        return x;
850    }
851    format!("{x:.14e}").parse().unwrap_or(x)
852}
853
854/// A quotient to 15 significant digits (LibreOffice's `approxFloor` and
855/// `approxCeil` round it so before taking the floor or ceiling), so
856/// `FLOOR(0.3, 0.1)` is 0.3, not 0.2, but a quotient 1e-12 below an integer
857/// is not snapped up past x, and a tiny one is not snapped to 0.
858fn clean(q: f64) -> f64 {
859    sig15(q)
860}
861
862fn proper(s: &str) -> String {
863    let mut out = String::with_capacity(s.len());
864    let mut prev_letter = false;
865    for c in s.chars() {
866        if c.is_alphabetic() {
867            if prev_letter {
868                out.extend(c.to_lowercase());
869            } else {
870                out.extend(c.to_uppercase());
871            }
872            prev_letter = true;
873        } else {
874            out.push(c);
875            prev_letter = false;
876        }
877    }
878    out
879}
880
881pub(crate) fn find_chars(hay: &[char], needle: &[char], from: usize) -> Option<usize> {
882    if needle.is_empty() {
883        return Some(from);
884    }
885    (from..=hay.len().checked_sub(needle.len())?).find(|&i| hay[i..i + needle.len()] == *needle)
886}
887
888/// `SEARCH`: case-insensitive, `?` any one character, `*` any run, `~`
889/// escapes the next character.
890pub(crate) fn search_chars(hay: &[char], pattern: &str, from: usize) -> Option<usize> {
891    let lower: Vec<char> = hay.iter().flat_map(|c| c.to_lowercase()).collect();
892    if lower.len() != hay.len() {
893        // A case mapping that changes length: fall back to exact positions.
894        return find_chars(hay, &pattern.chars().collect::<Vec<_>>(), from);
895    }
896    let pat: Vec<Pat> = {
897        let mut out = Vec::new();
898        let mut it = pattern.chars();
899        while let Some(c) = it.next() {
900            out.push(match c {
901                '~' => match it.next() {
902                    Some(n) => Pat::Lit(n.to_lowercase().next().unwrap_or(n)),
903                    None => Pat::Lit('~'),
904                },
905                '?' => Pat::One,
906                '*' => Pat::Many,
907                c => Pat::Lit(c.to_lowercase().next().unwrap_or(c)),
908            });
909        }
910        out
911    };
912    (from..=lower.len()).find(|&i| matches_at(&lower[i..], &pat))
913}
914
915#[derive(Clone, Copy)]
916enum Pat {
917    Lit(char),
918    One,
919    Many,
920}
921
922/// Whether `pat` matches a prefix of `s`.
923fn matches_at(s: &[char], pat: &[Pat]) -> bool {
924    // Iterative glob with backtracking to the last `*`.
925    let (mut si, mut pi) = (0, 0);
926    let mut star: Option<(usize, usize)> = None;
927    loop {
928        if pi == pat.len() {
929            return true;
930        }
931        match pat[pi] {
932            Pat::Many => {
933                star = Some((pi, si));
934                pi += 1;
935                continue;
936            }
937            Pat::One if si < s.len() => {
938                si += 1;
939                pi += 1;
940                continue;
941            }
942            Pat::Lit(c) if si < s.len() && s[si] == c => {
943                si += 1;
944                pi += 1;
945                continue;
946            }
947            _ => {}
948        }
949        match star {
950            Some((sp, ss)) if ss < s.len() => {
951                star = Some((sp, ss + 1));
952                pi = sp + 1;
953                si = ss + 1;
954            }
955            _ => return false,
956        }
957    }
958}
959
960/// `VALUE`: a decimal number, optionally with a leading sign, thousands
961/// commas, a trailing `%`, or surrounding spaces.
962fn parse_value(s: &str) -> Option<f64> {
963    let s = s.trim();
964    let (s, pct) = match s.strip_suffix('%') {
965        Some(rest) => (rest.trim_end(), true),
966        None => (s, false),
967    };
968    let cleaned: String = s.chars().filter(|c| *c != ',').collect();
969    if cleaned.is_empty()
970        || !cleaned
971            .chars()
972            .all(|c| c.is_ascii_digit() || matches!(c, '.' | '-' | '+' | 'e' | 'E'))
973    {
974        return None;
975    }
976    let v: f64 = cleaned.parse().ok()?;
977    Some(if pct { v / 100.0 } else { v })
978}
979
980/// `TEXTBEFORE` / `TEXTAFTER(text, delim, [instance=1], [match_mode=0],
981/// [match_end=0], [if_not_found])`. A negative instance counts from the end;
982/// match_mode 1 is case-insensitive; match_end 1 treats the end of the text
983/// as a delimiter.
984fn text_before_after(before: bool, a: &[CellValue]) -> R {
985    let (s, delim) = (t(a, 0)?, t(a, 1)?);
986    let inst = n_or(a, 2, 1.0)?.trunc() as i64;
987    let insensitive = n_or(a, 3, 0.0)? != 0.0;
988    let match_end = n_or(a, 4, 0.0)? != 0.0;
989    let chars: Vec<char> = s.chars().collect();
990    if inst == 0 || inst.unsigned_abs() as usize > chars.len().max(1) {
991        return Err(err::VALUE);
992    }
993    let fold = |c: char| {
994        if insensitive {
995            c.to_lowercase().next().unwrap_or(c)
996        } else {
997            c
998        }
999    };
1000    let hay: Vec<char> = chars.iter().map(|c| fold(*c)).collect();
1001    let needle: Vec<char> = delim.chars().map(fold).collect();
1002    // Every (start, end) of a delimiter occurrence, in character offsets.
1003    let mut hits: Vec<(usize, usize)> = Vec::new();
1004    if needle.is_empty() {
1005        hits.push((0, 0));
1006        hits.push((chars.len(), chars.len()));
1007    } else {
1008        let mut i = 0;
1009        while let Some(p) = find_chars(&hay, &needle, i) {
1010            hits.push((p, p + needle.len()));
1011            i = p + needle.len();
1012        }
1013    }
1014    if match_end {
1015        if inst > 0 {
1016            hits.push((chars.len(), chars.len()));
1017        } else {
1018            hits.insert(0, (0, 0));
1019        }
1020    }
1021    let pick = if inst > 0 {
1022        hits.get(inst as usize - 1)
1023    } else {
1024        hits.len()
1025            .checked_sub(inst.unsigned_abs() as usize)
1026            .and_then(|i| hits.get(i))
1027    };
1028    let Some(&(start, end)) = pick else {
1029        return match a.get(5) {
1030            Some(v) => Ok(v.clone()),
1031            None => Err(err::NA),
1032        };
1033    };
1034    let part: String = if before {
1035        chars[..start].iter().collect()
1036    } else {
1037        chars[end..].iter().collect()
1038    };
1039    ok_text(part)
1040}
1041
1042#[cfg(test)]
1043#[cfg_attr(coverage_nightly, coverage(off))]
1044mod tests {
1045    use super::*;
1046    use improv_core_model::functions::{all, Kind};
1047
1048    /// Every evaluable registry entry has an implementation (unknown ids fall
1049    /// through to `#NAME?`).
1050    #[test]
1051    fn every_scalar_function_is_implemented() {
1052        // The dimensional ones compile to their own plan nodes.
1053        for d in all().filter(|d| {
1054            d.kind == Kind::Scalar
1055                && d.category != improv_core_model::functions::Category::Dimensional
1056        }) {
1057            let args: Vec<CellValue> = (0..d.required.max(1).min(d.params.len().max(1)))
1058                .take(d.required)
1059                .map(|i| match d.param(i) {
1060                    improv_core_model::functions::Arg::Text => CellValue::Text("ab".into()),
1061                    improv_core_model::functions::Arg::Boolean => CellValue::Bool(true),
1062                    _ => CellValue::num(1.0),
1063                })
1064                .collect();
1065            let v = eval(d.id, &args);
1066            assert_ne!(
1067                v,
1068                CellValue::Err(err::NAME),
1069                "{} ({:?}) has no implementation",
1070                d.name,
1071                d.id
1072            );
1073        }
1074    }
1075
1076    fn num_of(f: u32, args: &[f64]) -> CellValue {
1077        let a: Vec<CellValue> = args.iter().map(|x| CellValue::num(*x)).collect();
1078        eval(FuncId(f), &a)
1079    }
1080
1081    #[test]
1082    fn errors_propagate_and_are_inspected() {
1083        let div0 = CellValue::Err(err::DIV0);
1084        assert_eq!(eval(FuncId(10), std::slice::from_ref(&div0)), div0);
1085        assert_eq!(
1086            eval(FuncId(50), std::slice::from_ref(&div0)),
1087            CellValue::Bool(true)
1088        );
1089        assert_eq!(
1090            eval(FuncId(51), &[CellValue::Err(err::NA)]),
1091            CellValue::Bool(false)
1092        );
1093        assert_eq!(
1094            eval(FuncId(52), &[CellValue::Err(err::NA)]),
1095            CellValue::Bool(true)
1096        );
1097        assert_eq!(
1098            eval(FuncId(55), std::slice::from_ref(&div0)),
1099            CellValue::num(2.0)
1100        );
1101        assert_eq!(
1102            eval(FuncId(55), &[CellValue::num(1.0)]),
1103            CellValue::Err(err::NA)
1104        );
1105        assert_eq!(eval(FuncId(53), &[]), CellValue::Err(err::NA));
1106        assert_eq!(
1107            eval(FuncId(54), &[CellValue::Text("#num!".into())]),
1108            CellValue::Err(err::NUM)
1109        );
1110        assert_eq!(num_of(14, &[-1.0]), CellValue::Err(err::NUM));
1111        assert_eq!(num_of(102, &[1000.0]), CellValue::Err(err::NUM), "overflow");
1112        assert_eq!(eval(FuncId(999), &[]), CellValue::Err(err::NAME));
1113    }
1114
1115    #[test]
1116    fn rounding_is_decimal_correct() {
1117        assert_eq!(num_of(11, &[2.5]), CellValue::num(3.0));
1118        assert_eq!(num_of(11, &[-2.5]), CellValue::num(-3.0));
1119        assert_eq!(num_of(11, &[2.675, 2.0]), CellValue::num(2.68));
1120        assert_eq!(num_of(11, &[1234.5, -2.0]), CellValue::num(1200.0));
1121        assert_eq!(num_of(200, &[-3.125, 1.0]), CellValue::num(-3.2));
1122        assert_eq!(num_of(201, &[3.999, 2.0]), CellValue::num(3.99));
1123        assert_eq!(num_of(12, &[0.3, 0.1]), CellValue::num(0.3));
1124        assert_eq!(num_of(12, &[-2.5, 2.0]), CellValue::num(-4.0));
1125        assert_eq!(num_of(12, &[2.5, -2.0]), CellValue::Err(err::NUM));
1126        assert_eq!(num_of(205, &[2.5, 1.0]), CellValue::num(3.0));
1127        assert_eq!(num_of(205, &[-2.5, -2.0]), CellValue::num(-4.0));
1128        assert_eq!(num_of(212, &[-1.0]), CellValue::num(-2.0));
1129        assert_eq!(num_of(213, &[2.0]), CellValue::num(3.0));
1130        assert_eq!(num_of(213, &[0.0]), CellValue::num(1.0));
1131        assert_eq!(num_of(211, &[10.0, 3.0]), CellValue::num(9.0));
1132        assert_eq!(num_of(202, &[123.456, 2.0]), CellValue::num(120.0));
1133    }
1134
1135    /// The edge cases of each family: one line per branch the goldens do not
1136    /// already reach (Excel's documented results).
1137    #[test]
1138    fn edge_cases() {
1139        let n = CellValue::num;
1140        let s = |x: &str| CellValue::Text(x.into());
1141        let b = CellValue::Bool;
1142        let e = CellValue::Err;
1143        let call = |f: u32, a: &[CellValue]| eval(FuncId(f), a);
1144        // Booleans and dates read as numbers; errors propagate first-come.
1145        assert_eq!(call(10, &[b(true)]), n(1.0));
1146        assert_eq!(call(10, &[CellValue::Date(5)]), n(5.0));
1147        assert_eq!(call(22, &[n(1.0), e(err::NA), e(err::DIV0)]), e(err::NA));
1148        assert_eq!(call(10, &[s("x")]), e(err::VALUE));
1149        // BETWEEN inclusive and strict; ISERR on a non-error.
1150        assert_eq!(call(38, &[n(1.0), n(1.0), n(2.0)]), b(true));
1151        assert_eq!(call(38, &[n(1.0), n(1.0), n(2.0), b(false)]), b(false));
1152        assert_eq!(call(38, &[n(1.5), n(1.0), n(2.0), b(false)]), b(true));
1153        assert_eq!(call(51, &[n(1.0)]), b(false));
1154        assert_eq!(call(55, &[e(0)]), e(err::NA));
1155        assert_eq!(call(54, &[s("#bogus")]), e(err::VALUE));
1156        // Logs, MOD/QUOTIENT, ATAN2 and the poles.
1157        assert_eq!(call(106, &[n(8.0), n(1.0)]), e(err::DIV0));
1158        assert_eq!(call(106, &[n(8.0), n(2.0)]), n(3.0));
1159        assert_eq!(call(110, &[n(5.0), n(0.0)]), e(err::DIV0));
1160        assert_eq!(call(110, &[n(-5.0), n(3.0)]), n(1.0));
1161        assert_eq!(call(111, &[n(5.0), n(0.0)]), e(err::DIV0));
1162        assert_eq!(call(121, &[n(0.0), n(0.0)]), e(err::DIV0));
1163        assert_eq!(call(130, &[n(0.0)]), e(err::DIV0));
1164        assert_eq!(call(128, &[n(2e8)]), e(err::NUM));
1165        assert_eq!(call(138, &[n(0.5)]), e(err::NUM));
1166        assert_eq!(call(139, &[n(0.0)]), e(err::NUM));
1167        assert_eq!(call(136, &[n(0.5)]), n(1.0));
1168        assert_eq!(call(136, &[n(1.5)]), n(-1.0));
1169        assert_eq!(call(136, &[n(2.0)]), n(0.0));
1170        assert_eq!(call(137, &[n(1.0)]), n(-1.0));
1171        assert_eq!(call(137, &[n(2.0)]), n(1.0));
1172        assert_eq!(call(137, &[n(1.5)]), n(0.0));
1173        // Combinatorics.
1174        assert_eq!(call(142, &[n(1.0)]), n(1.0));
1175        assert_eq!(call(142, &[n(-1.0)]), e(err::NUM));
1176        assert_eq!(call(143, &[n(2.0), n(3.0)]), e(err::NUM));
1177        assert_eq!(call(144, &[n(0.0), n(0.0)]), n(1.0));
1178        assert_eq!(call(144, &[n(0.0), n(2.0)]), e(err::NUM));
1179        assert_eq!(call(145, &[n(2.0), n(3.0)]), e(err::NUM));
1180        assert_eq!(call(148, &[n(4.0), n(0.0)]), n(0.0));
1181        assert_eq!(call(148, &[n(4.0), n(6.0)]), n(12.0));
1182        assert_eq!(call(141, &[n(-1.0)]), e(err::NUM));
1183        assert_eq!(call(151, &[n(2.0), n(0.0)]), n(0.0));
1184        assert_eq!(call(151, &[n(-2.0)]), e(err::VALUE));
1185        // Rounding: zero, zero significance, modes, MROUND signs.
1186        assert_eq!(call(202, &[n(0.0), n(3.0)]), n(0.0));
1187        assert_eq!(call(202, &[n(5.0), n(0.0)]), e(err::NUM));
1188        assert_eq!(call(12, &[n(0.0), n(0.0)]), n(0.0));
1189        assert_eq!(call(12, &[n(3.0), n(0.0)]), e(err::DIV0));
1190        assert_eq!(call(205, &[n(3.0), n(0.0)]), n(0.0));
1191        assert_eq!(call(205, &[n(3.0), n(-1.0)]), e(err::NUM));
1192        assert_eq!(call(206, &[n(-5.5), n(2.0), n(1.0)]), n(-4.0));
1193        assert_eq!(call(206, &[n(-5.5), n(2.0)]), n(-6.0));
1194        assert_eq!(call(206, &[n(5.0), n(0.0)]), n(0.0));
1195        assert_eq!(call(207, &[n(-5.5), n(2.0), n(1.0)]), n(-6.0));
1196        assert_eq!(call(207, &[n(5.0), n(0.0)]), n(0.0));
1197        assert_eq!(call(208, &[n(5.0), n(0.0)]), n(0.0));
1198        assert_eq!(call(209, &[n(5.0), n(0.0)]), n(0.0));
1199        assert_eq!(call(211, &[n(5.0), n(0.0)]), n(0.0));
1200        assert_eq!(call(211, &[n(5.0), n(-2.0)]), e(err::NUM));
1201        assert_eq!(call(211, &[n(-5.0), n(-2.0)]), n(-6.0));
1202        assert_eq!(call(213, &[n(-2.0)]), n(-3.0));
1203        assert_eq!(call(213, &[n(3.0)]), n(3.0));
1204        // Text out of range.
1205        assert_eq!(call(300, &[n(0.0)]), e(err::VALUE));
1206        assert_eq!(call(300, &[n(65.0)]), s("A"));
1207        assert_eq!(call(301, &[n(0.0)]), e(err::VALUE));
1208        assert_eq!(
1209            call(301, &[n(55_296.0)]),
1210            e(err::VALUE),
1211            "a surrogate is no char"
1212        );
1213        assert_eq!(call(302, &[s("")]), e(err::VALUE));
1214        assert_eq!(call(308, &[s("a"), s("abc"), n(0.0)]), e(err::VALUE));
1215        assert_eq!(call(308, &[s("a"), s("abc"), n(5.0)]), e(err::VALUE));
1216        assert_eq!(call(308, &[s(""), s("abc"), n(2.0)]), n(2.0));
1217        assert_eq!(call(308, &[s("abcd"), s("abc")]), e(err::VALUE));
1218        assert_eq!(call(310, &[s("abc"), n(-1.0)]), e(err::VALUE));
1219        assert_eq!(call(311, &[s("abc"), n(9.0)]), s("abc"));
1220        assert_eq!(call(312, &[s("abc"), n(0.0), n(1.0)]), e(err::VALUE));
1221        assert_eq!(call(312, &[s("abc"), n(9.0), n(1.0)]), s(""));
1222        assert_eq!(
1223            call(317, &[s("abc"), n(0.0), n(1.0), s("x")]),
1224            e(err::VALUE)
1225        );
1226        assert_eq!(call(317, &[s("abc"), n(2.0), n(1.0), s("XY")]), s("aXYc"));
1227        assert_eq!(call(318, &[s("ab"), n(-1.0)]), e(err::VALUE));
1228        assert_eq!(call(318, &[s("ab"), n(20_000.0)]), e(err::VALUE));
1229        assert_eq!(call(319, &[s("abc"), s(""), s("x")]), s("abc"));
1230        assert_eq!(
1231            call(319, &[s("abc"), s("b"), s("x"), n(0.0)]),
1232            e(err::VALUE)
1233        );
1234        assert_eq!(call(319, &[s("abc"), s("b"), s("x"), n(5.0)]), s("abc"));
1235        assert_eq!(call(321, &[s("")]), e(err::VALUE));
1236        assert_eq!(call(321, &[s("1,234")]), n(1234.0));
1237        assert_eq!(call(324, &[s("abc"), s("["), s("]")]), e(err::NA));
1238        assert_eq!(call(325, &[s("abc"), s("")]), e(err::VALUE));
1239        // SEARCH: a case mapping that changes length; `~` escapes; `*`.
1240        assert_eq!(call(309, &[s("x"), s("İx")]), n(2.0));
1241        assert_eq!(call(309, &[s("~*"), s("a*b")]), n(2.0));
1242        assert_eq!(call(309, &[s("~"), s("a~b")]), n(2.0));
1243        assert_eq!(call(309, &[s("a*c"), s("xabbc")]), n(2.0));
1244        assert_eq!(call(309, &[s("a*z"), s("abc")]), e(err::VALUE));
1245        // TEXTBEFORE/AFTER: bad instance, empty delimiter, case, match_end,
1246        // a fallback value.
1247        assert_eq!(call(322, &[s("a-b"), s("-"), n(0.0)]), e(err::VALUE));
1248        assert_eq!(call(322, &[s("a-b"), s(""), n(1.0)]), s(""));
1249        assert_eq!(call(323, &[s("aXb"), s("x"), n(1.0), n(1.0)]), s("b"));
1250        assert_eq!(
1251            call(323, &[s("a-b"), s("-"), n(2.0), n(0.0), n(1.0)]),
1252            s("")
1253        );
1254        assert_eq!(
1255            call(322, &[s("a-b"), s("-"), n(-2.0), n(0.0), n(1.0)]),
1256            s("")
1257        );
1258        assert_eq!(
1259            call(322, &[s("ab"), s("-"), n(1.0), n(0.0), n(0.0), s("none")]),
1260            s("none")
1261        );
1262        assert_eq!(call(322, &[s("a-b-c"), s("-"), n(-5.0)]), e(err::NA));
1263        assert_eq!(call(322, &[s("a-b"), s("-"), n(-9.0)]), e(err::VALUE));
1264        // POWER edge cases.
1265        assert_eq!(power(0.0, -1.0), Err(err::DIV0));
1266        assert_eq!(power(-8.0, 1.0 / 3.0), Err(err::NUM));
1267        assert_eq!(power(-2.0, 3.0), Ok(n(-8.0)));
1268    }
1269
1270    /// Serial dates: 1 is 1899-12-31, 61 is 1900-03-01 (the fictitious
1271    /// 1900-02-29 is skipped, as in LibreOffice), and a date adds days.
1272    #[test]
1273    fn serial_dates_and_date_arithmetic() {
1274        let ms = |y, m, dd| {
1275            chrono::NaiveDate::from_ymd_opt(y, m, dd)
1276                .unwrap()
1277                .and_hms_opt(0, 0, 0)
1278                .unwrap()
1279                .and_utc()
1280                .timestamp_millis()
1281        };
1282        assert_eq!(d(&[CellValue::num(1.0)], 0), Ok(ms(1899, 12, 31)));
1283        assert_eq!(d(&[CellValue::num(61.0)], 0), Ok(ms(1900, 3, 1)));
1284        assert_eq!(d(&[CellValue::num(45658.0)], 0), Ok(ms(2025, 1, 1)));
1285        assert_eq!(
1286            d(&[CellValue::num(0.5)], 0),
1287            Ok(ms(1899, 12, 30) + DAY_MS / 2)
1288        );
1289        assert_eq!(d(&[CellValue::Date(7)], 0), Ok(7));
1290        assert_eq!(d(&[CellValue::Text("x".into())], 0), Err(err::VALUE));
1291        assert_eq!(d(&[CellValue::num(1e300)], 0), Err(err::NUM));
1292        assert_eq!(serial(ms(2025, 1, 1)), 45658.0);
1293        assert_eq!(
1294            date_plus_days(ms(2025, 1, 31), 1.0),
1295            CellValue::Date(ms(2025, 2, 1))
1296        );
1297        assert_eq!(date_plus_days(0, 1e30), CellValue::Err(err::NUM));
1298        assert_eq!(date_plus_days(i64::MAX - 1, 1.0), CellValue::Err(err::NUM));
1299    }
1300
1301    /// Branches the goldens leave to one binary, checked against
1302    /// LibreOffice 25.2.
1303    #[test]
1304    fn scalar_edges_against_libreoffice() {
1305        let (x, s) = (CellValue::num, |v: &str| CellValue::Text(v.into()));
1306        let e = CellValue::Err;
1307        let ev = |id: u32, a: &[CellValue]| eval(FuncId(id), a);
1308        // BETWEEN false (inclusive) on either side, ISERR of #N/A and of
1309        // another error, SIGN of a negative.
1310        assert_eq!(ev(38, &[x(5.0), x(1.0), x(3.0)]), CellValue::Bool(false));
1311        assert_eq!(ev(38, &[x(0.0), x(1.0), x(3.0)]), CellValue::Bool(false));
1312        assert_eq!(ev(51, &[e(err::NA)]), CellValue::Bool(false));
1313        assert_eq!(ev(51, &[e(err::DIV0)]), CellValue::Bool(true));
1314        assert_eq!(ev(100, &[x(-2.0)]), x(-1.0));
1315        assert_eq!(ev(100, &[x(0.0)]), x(0.0));
1316        // ATAN2(0, y), ATANH(-1), FACTDOUBLE(5), MROUND(0, 5).
1317        assert_eq!(ev(121, &[x(0.0), x(1.0)]), x(std::f64::consts::FRAC_PI_2));
1318        assert_eq!(ev(127, &[x(-1.0)]), e(err::NUM));
1319        assert_eq!(ev(142, &[x(5.0)]), x(15.0));
1320        assert_eq!(ev(211, &[x(0.0), x(5.0)]), x(0.0));
1321        // CEILING.MATH of a negative: toward 0, or away with a mode
1322        // (LibreOffice -2 and -3).
1323        assert_eq!(ev(207, &[x(-2.5), x(1.0), x(0.0)]), x(-2.0));
1324        assert_eq!(ev(207, &[x(-2.5), x(1.0), x(1.0)]), x(-3.0));
1325        // MID and REPLACE with a negative length (LibreOffice Err:502).
1326        assert_eq!(ev(312, &[s("abc"), x(1.0), x(-1.0)]), e(err::VALUE));
1327        assert_eq!(ev(317, &[s("abc"), x(1.0), x(-1.0), s("x")]), e(err::VALUE));
1328        // ROUND of a value already past 15 digits, of 0 and of infinity.
1329        assert_eq!(round_to(1e20, 2.0, Dir::HalfAway), 1e20);
1330        assert_eq!(round_to(0.0, 2.0, Dir::HalfAway), 0.0);
1331        assert_eq!(sig15(f64::INFINITY), f64::INFINITY);
1332        // SEARCH: a ? past the end of the text matches nothing.
1333        assert_eq!(ev(309, &[s("a?"), s("a")]), e(err::VALUE));
1334        // A serial or day count that overflows milliseconds.
1335        assert_eq!(d(&[x(f64::NAN)], 0), Err(err::NUM));
1336        assert_eq!(date_plus_days(0, f64::NAN), e(err::NUM));
1337    }
1338
1339    #[test]
1340    fn text_positions_are_characters() {
1341        let s = |x: &str| CellValue::Text(x.into());
1342        assert_eq!(eval(FuncId(313), &[s("héllo")]), CellValue::num(5.0));
1343        assert_eq!(
1344            eval(
1345                FuncId(312),
1346                &[s("héllo"), CellValue::num(2.0), CellValue::num(2.0)]
1347            ),
1348            s("él")
1349        );
1350        assert_eq!(
1351            eval(FuncId(309), &[s("A*"), s("xxaBc"),]),
1352            CellValue::num(3.0)
1353        );
1354        assert_eq!(
1355            eval(FuncId(309), &[s("b?d"), s("abcd")]),
1356            CellValue::num(2.0)
1357        );
1358        assert_eq!(
1359            eval(FuncId(308), &[s("B"), s("abc")]),
1360            CellValue::Err(err::VALUE)
1361        );
1362        assert_eq!(
1363            eval(FuncId(322), &[s("a-b-c"), s("-"), CellValue::num(-1.0)]),
1364            s("a-b")
1365        );
1366        assert_eq!(eval(FuncId(323), &[s("a-b-c"), s("-")]), s("b-c"));
1367        assert_eq!(
1368            eval(FuncId(323), &[s("abc"), s("-")]),
1369            CellValue::Err(err::NA)
1370        );
1371        assert_eq!(
1372            eval(
1373                FuncId(319),
1374                &[s("aaa"), s("a"), s("b"), CellValue::num(2.0)]
1375            ),
1376            s("aba")
1377        );
1378        assert_eq!(eval(FuncId(320), &[s("  a   b ")]), s("a b"));
1379        assert_eq!(eval(FuncId(316), &[s("hello wORLD")]), s("Hello World"));
1380        assert_eq!(eval(FuncId(321), &[s(" 12.5% ")]), CellValue::num(0.125));
1381        assert_eq!(
1382            eval(
1383                FuncId(305),
1384                &[s("a"), CellValue::num(1.5), CellValue::Bool(true)]
1385            ),
1386            s("a1.5TRUE")
1387        );
1388    }
1389
1390    /// FACT, FACTDOUBLE, COMBIN, COMBINA, PERMUT and MULTINOMIAL of a count
1391    /// near the 1e15 limit overflow to `#NUM!` in a few hundred steps; they
1392    /// used to multiply up to 1e15 factors (hours).
1393    #[test]
1394    fn huge_counts_overflow_promptly() {
1395        let n = CellValue::num;
1396        let num_err = CellValue::Err(err::NUM);
1397        let t0 = std::time::Instant::now();
1398        assert_eq!(eval(FuncId(141), &[n(1e14)]), num_err, "FACT");
1399        assert_eq!(eval(FuncId(142), &[n(1.02e14)]), num_err, "FACTDOUBLE");
1400        assert_eq!(eval(FuncId(142), &[n(1e300)]), num_err, "FACTDOUBLE");
1401        assert_eq!(eval(FuncId(143), &[n(1e14), n(1e13)]), num_err, "COMBIN");
1402        assert_eq!(eval(FuncId(144), &[n(1e13), n(1e13)]), num_err, "COMBINA");
1403        assert_eq!(eval(FuncId(145), &[n(1e14), n(1e13)]), num_err, "PERMUT");
1404        assert_eq!(
1405            eval(FuncId(149), &[n(1.2e13), n(52.0), n(642.0)]),
1406            num_err,
1407            "MULTINOMIAL"
1408        );
1409        assert!(t0.elapsed() < std::time::Duration::from_secs(10));
1410        // The small values are unchanged.
1411        assert_eq!(eval(FuncId(141), &[n(5.0)]), n(120.0));
1412        assert_eq!(eval(FuncId(142), &[n(7.0)]), n(105.0));
1413        assert_eq!(eval(FuncId(142), &[n(8.0)]), n(384.0));
1414        assert_eq!(eval(FuncId(142), &[n(0.0)]), n(1.0));
1415        assert_eq!(eval(FuncId(143), &[n(10.0), n(3.0)]), n(120.0));
1416        assert_eq!(eval(FuncId(143), &[n(1e14), n(1.0)]), n(1e14));
1417        assert_eq!(eval(FuncId(145), &[n(10.0), n(3.0)]), n(720.0));
1418        assert_eq!(eval(FuncId(149), &[n(2.0), n(3.0), n(4.0)]), n(1260.0));
1419        assert_eq!(eval(FuncId(149), &[n(0.0), n(0.0)]), n(1.0));
1420    }
1421
1422    /// The rounding functions snap their quotient to 15 digits, not within
1423    /// 1e-12 of an integer: `CEILING(1.17e-10, 117)` is 117 and
1424    /// `FLOOR(-1e-13, 1)` is -1 (they were 0), `FLOOR(9618236.874990381,
1425    /// 0.625)` is 9618236.25 (it was 9618236.875, above x), as in
1426    /// LibreOffice and Gnumeric.
1427    #[test]
1428    fn floor_and_ceiling_snap_to_15_digits() {
1429        let x = 9_618_236.874_990_381;
1430        assert_eq!(num_of(12, &[x, 0.625]), CellValue::num(9_618_236.25));
1431        assert_eq!(num_of(205, &[x, 0.625]), CellValue::num(9_618_236.875));
1432        assert_eq!(num_of(208, &[x, 0.625]), CellValue::num(9_618_236.25));
1433        assert_eq!(
1434            num_of(205, &[1.168_724_262_385_476_7e-10, 117.0]),
1435            CellValue::num(117.0)
1436        );
1437        assert_eq!(num_of(205, &[1e-13, 1.0]), CellValue::num(1.0));
1438        assert_eq!(num_of(12, &[1e-13, 1.0]), CellValue::num(0.0));
1439        assert_eq!(num_of(12, &[-1e-13, 1.0]), CellValue::num(-1.0));
1440        assert_eq!(num_of(207, &[1e-13, 1.0]), CellValue::num(1.0));
1441        assert_eq!(num_of(207, &[-1e-13, 1.0, 1.0]), CellValue::num(-1.0));
1442        assert_eq!(num_of(206, &[1e-13, 1.0]), CellValue::num(0.0));
1443        assert_eq!(num_of(205, &[0.0, 1.0]), CellValue::num(0.0));
1444        // The snap near an integer stays.
1445        assert_eq!(num_of(12, &[0.3, 0.1]), CellValue::num(0.3));
1446        assert_eq!(num_of(205, &[0.3, 0.1]), CellValue::num(0.3));
1447    }
1448}