1use crate::cell::CellValue;
5use crate::error::Result;
6use crate::formula::ast::Expr;
7use crate::formula::eval::{coerce_to_number, coerce_to_string, Evaluator};
8use crate::formula::functions::{check_arg_count, collect_criteria_range_values, matches_criteria};
9
10pub fn fn_sumif(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
12 check_arg_count("SUMIF", args, 2, 3)?;
13 let range_vals = collect_criteria_range_values(&args[0], ctx)?;
14 let criteria_val = ctx.eval_expr(&args[1])?;
15 let criteria = coerce_to_string(&criteria_val);
16 let sum_vals = if args.len() == 3 {
17 collect_criteria_range_values(&args[2], ctx)?
18 } else {
19 range_vals.clone()
20 };
21 let mut total = 0.0;
22 for (i, rv) in range_vals.iter().enumerate() {
23 if matches_criteria(rv, &criteria) {
24 if let Some(sv) = sum_vals.get(i) {
25 if let Ok(n) = coerce_to_number(sv) {
26 total += n;
27 }
28 }
29 }
30 }
31 Ok(CellValue::Number(total))
32}
33
34pub fn fn_sumifs(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
36 check_arg_count("SUMIFS", args, 3, 255)?;
37 if !(args.len() - 1).is_multiple_of(2) {
38 return Ok(CellValue::Error("#VALUE!".to_string()));
39 }
40 let sum_vals = collect_criteria_range_values(&args[0], ctx)?;
41 let pair_count = (args.len() - 1) / 2;
42 let mut criteria_ranges: Vec<Vec<CellValue>> = Vec::with_capacity(pair_count);
43 let mut criteria_strings: Vec<String> = Vec::with_capacity(pair_count);
44 for i in 0..pair_count {
45 let range_vals = collect_criteria_range_values(&args[1 + i * 2], ctx)?;
46 let crit_val = ctx.eval_expr(&args[2 + i * 2])?;
47 criteria_ranges.push(range_vals);
48 criteria_strings.push(coerce_to_string(&crit_val));
49 }
50 let mut total = 0.0;
51 for (idx, sv) in sum_vals.iter().enumerate() {
52 let all_match =
53 criteria_ranges
54 .iter()
55 .zip(criteria_strings.iter())
56 .all(|(range_vals, crit)| {
57 range_vals
58 .get(idx)
59 .is_some_and(|rv| matches_criteria(rv, crit))
60 });
61 if all_match {
62 if let Ok(n) = coerce_to_number(sv) {
63 total += n;
64 }
65 }
66 }
67 Ok(CellValue::Number(total))
68}
69
70pub fn fn_roundup(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
72 check_arg_count("ROUNDUP", args, 2, 2)?;
73 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
74 let digits = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
75 let factor = 10f64.powi(digits);
76 let result = if n >= 0.0 {
77 (n * factor).ceil() / factor
78 } else {
79 (n * factor).floor() / factor
80 };
81 Ok(CellValue::Number(result))
82}
83
84pub fn fn_rounddown(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
86 check_arg_count("ROUNDDOWN", args, 2, 2)?;
87 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
88 let digits = coerce_to_number(&ctx.eval_expr(&args[1])?)? as i32;
89 let factor = 10f64.powi(digits);
90 let result = (n * factor).trunc() / factor;
91 Ok(CellValue::Number(result))
92}
93
94pub fn fn_ceiling(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
96 check_arg_count("CEILING", args, 2, 2)?;
97 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
98 let sig = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
99 if sig == 0.0 {
100 return Ok(CellValue::Number(0.0));
101 }
102 if n > 0.0 && sig < 0.0 {
103 return Ok(CellValue::Error("#NUM!".to_string()));
104 }
105 let result = (n / sig).ceil() * sig;
106 Ok(CellValue::Number(result))
107}
108
109pub fn fn_floor(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
111 check_arg_count("FLOOR", args, 2, 2)?;
112 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
113 let sig = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
114 if sig == 0.0 {
115 return Ok(CellValue::Error("#DIV/0!".to_string()));
116 }
117 if n > 0.0 && sig < 0.0 {
118 return Ok(CellValue::Error("#NUM!".to_string()));
119 }
120 let result = (n / sig).floor() * sig;
121 Ok(CellValue::Number(result))
122}
123
124pub fn fn_sign(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
126 check_arg_count("SIGN", args, 1, 1)?;
127 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
128 let result = if n > 0.0 {
129 1.0
130 } else if n < 0.0 {
131 -1.0
132 } else {
133 0.0
134 };
135 Ok(CellValue::Number(result))
136}
137
138pub fn fn_rand(args: &[Expr], _ctx: &mut Evaluator) -> Result<CellValue> {
140 check_arg_count("RAND", args, 0, 0)?;
141 let t = std::time::SystemTime::now()
144 .duration_since(std::time::UNIX_EPOCH)
145 .unwrap_or_default()
146 .subsec_nanos();
147 let r = (t as f64 % 1_000_000.0) / 1_000_000.0;
148 Ok(CellValue::Number(r))
149}
150
151pub fn fn_randbetween(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
153 check_arg_count("RANDBETWEEN", args, 2, 2)?;
154 let bottom = coerce_to_number(&ctx.eval_expr(&args[0])?)?.ceil() as i64;
155 let top = coerce_to_number(&ctx.eval_expr(&args[1])?)?.floor() as i64;
156 if bottom > top {
157 return Ok(CellValue::Error("#NUM!".to_string()));
158 }
159 let t = std::time::SystemTime::now()
160 .duration_since(std::time::UNIX_EPOCH)
161 .unwrap_or_default()
162 .subsec_nanos() as i64;
163 let range = top - bottom + 1;
164 let result = bottom + (t.abs() % range);
165 Ok(CellValue::Number(result as f64))
166}
167
168pub fn fn_pi(args: &[Expr], _ctx: &mut Evaluator) -> Result<CellValue> {
170 check_arg_count("PI", args, 0, 0)?;
171 Ok(CellValue::Number(std::f64::consts::PI))
172}
173
174pub fn fn_log(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
176 check_arg_count("LOG", args, 1, 2)?;
177 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
178 if n <= 0.0 {
179 return Ok(CellValue::Error("#NUM!".to_string()));
180 }
181 let base = if args.len() > 1 {
182 coerce_to_number(&ctx.eval_expr(&args[1])?)?
183 } else {
184 10.0
185 };
186 if base <= 0.0 || base == 1.0 {
187 return Ok(CellValue::Error("#NUM!".to_string()));
188 }
189 Ok(CellValue::Number(n.log(base)))
190}
191
192pub fn fn_log10(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
194 check_arg_count("LOG10", args, 1, 1)?;
195 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
196 if n <= 0.0 {
197 return Ok(CellValue::Error("#NUM!".to_string()));
198 }
199 Ok(CellValue::Number(n.log10()))
200}
201
202pub fn fn_ln(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
204 check_arg_count("LN", args, 1, 1)?;
205 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
206 if n <= 0.0 {
207 return Ok(CellValue::Error("#NUM!".to_string()));
208 }
209 Ok(CellValue::Number(n.ln()))
210}
211
212pub fn fn_exp(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
214 check_arg_count("EXP", args, 1, 1)?;
215 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
216 Ok(CellValue::Number(n.exp()))
217}
218
219pub fn fn_product(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
221 check_arg_count("PRODUCT", args, 1, 255)?;
222 let nums = ctx.collect_numbers(args)?;
223 if nums.is_empty() {
224 return Ok(CellValue::Number(0.0));
225 }
226 let result: f64 = nums.iter().product();
227 Ok(CellValue::Number(result))
228}
229
230pub fn fn_quotient(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
232 check_arg_count("QUOTIENT", args, 2, 2)?;
233 let num = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
234 let den = coerce_to_number(&ctx.eval_expr(&args[1])?)?;
235 if den == 0.0 {
236 return Ok(CellValue::Error("#DIV/0!".to_string()));
237 }
238 let result = (num / den).trunc();
239 Ok(CellValue::Number(result))
240}
241
242pub fn fn_fact(args: &[Expr], ctx: &mut Evaluator) -> Result<CellValue> {
244 check_arg_count("FACT", args, 1, 1)?;
245 let n = coerce_to_number(&ctx.eval_expr(&args[0])?)?;
246 if n < 0.0 {
247 return Ok(CellValue::Error("#NUM!".to_string()));
248 }
249 let n_int = n.floor() as u64;
250 let mut result: f64 = 1.0;
251 for i in 2..=n_int {
252 result *= i as f64;
253 }
254 Ok(CellValue::Number(result))
255}
256
257#[cfg(test)]
258#[allow(clippy::manual_range_contains)]
259mod tests {
260 use crate::cell::CellValue;
261 use crate::formula::eval::{evaluate, CellSnapshot};
262 use crate::formula::parser::parse_formula;
263
264 fn eval_with_data(formula: &str, data: &[(&str, u32, u32, CellValue)]) -> CellValue {
265 let mut snap = CellSnapshot::new("Sheet1".to_string());
266 for (sheet, col, row, val) in data {
267 snap.set_cell(sheet, *col, *row, val.clone());
268 }
269 let expr = parse_formula(formula).unwrap();
270 evaluate(&expr, &snap).unwrap()
271 }
272
273 fn eval(formula: &str) -> CellValue {
274 eval_with_data(formula, &[])
275 }
276
277 fn approx_eq(a: f64, b: f64) -> bool {
278 (a - b).abs() < 1e-9
279 }
280
281 #[test]
284 fn sumif_greater_than() {
285 let data = vec![
286 ("Sheet1", 1, 1, CellValue::Number(1.0)),
287 ("Sheet1", 1, 2, CellValue::Number(5.0)),
288 ("Sheet1", 1, 3, CellValue::Number(10.0)),
289 ];
290 let result = eval_with_data("SUMIF(A1:A3,\">3\")", &data);
291 assert_eq!(result, CellValue::Number(15.0));
292 }
293
294 #[test]
295 fn sumif_less_than_or_equal() {
296 let data = vec![
297 ("Sheet1", 1, 1, CellValue::Number(1.0)),
298 ("Sheet1", 1, 2, CellValue::Number(5.0)),
299 ("Sheet1", 1, 3, CellValue::Number(10.0)),
300 ];
301 let result = eval_with_data("SUMIF(A1:A3,\"<=5\")", &data);
302 assert_eq!(result, CellValue::Number(6.0));
303 }
304
305 #[test]
306 fn sumif_exact_text_match() {
307 let data = vec![
308 ("Sheet1", 1, 1, CellValue::String("Apple".to_string())),
309 ("Sheet1", 2, 1, CellValue::Number(10.0)),
310 ("Sheet1", 1, 2, CellValue::String("Banana".to_string())),
311 ("Sheet1", 2, 2, CellValue::Number(20.0)),
312 ("Sheet1", 1, 3, CellValue::String("Apple".to_string())),
313 ("Sheet1", 2, 3, CellValue::Number(30.0)),
314 ];
315 let result = eval_with_data("SUMIF(A1:A3,\"Apple\",B1:B3)", &data);
316 assert_eq!(result, CellValue::Number(40.0));
317 }
318
319 #[test]
320 fn sumif_not_equal() {
321 let data = vec![
322 ("Sheet1", 1, 1, CellValue::Number(0.0)),
323 ("Sheet1", 1, 2, CellValue::Number(5.0)),
324 ("Sheet1", 1, 3, CellValue::Number(0.0)),
325 ];
326 let result = eval_with_data("SUMIF(A1:A3,\"<>0\")", &data);
327 assert_eq!(result, CellValue::Number(5.0));
328 }
329
330 #[test]
331 fn sumif_no_sum_range() {
332 let data = vec![
333 ("Sheet1", 1, 1, CellValue::Number(2.0)),
334 ("Sheet1", 1, 2, CellValue::Number(4.0)),
335 ("Sheet1", 1, 3, CellValue::Number(6.0)),
336 ];
337 let result = eval_with_data("SUMIF(A1:A3,\">3\")", &data);
338 assert_eq!(result, CellValue::Number(10.0));
339 }
340
341 #[test]
344 fn sumifs_multi_criteria() {
345 let data = vec![
346 ("Sheet1", 1, 1, CellValue::String("A".to_string())),
347 ("Sheet1", 2, 1, CellValue::Number(1.0)),
348 ("Sheet1", 3, 1, CellValue::Number(10.0)),
349 ("Sheet1", 1, 2, CellValue::String("B".to_string())),
350 ("Sheet1", 2, 2, CellValue::Number(2.0)),
351 ("Sheet1", 3, 2, CellValue::Number(20.0)),
352 ("Sheet1", 1, 3, CellValue::String("A".to_string())),
353 ("Sheet1", 2, 3, CellValue::Number(3.0)),
354 ("Sheet1", 3, 3, CellValue::Number(30.0)),
355 ];
356 let result = eval_with_data("SUMIFS(C1:C3,A1:A3,\"A\",B1:B3,\">1\")", &data);
357 assert_eq!(result, CellValue::Number(30.0));
358 }
359
360 #[test]
363 fn roundup_positive() {
364 let result = eval("ROUNDUP(3.2,0)");
365 assert_eq!(result, CellValue::Number(4.0));
366 }
367
368 #[test]
369 fn roundup_negative() {
370 let result = eval("ROUNDUP(-3.2,0)");
371 assert_eq!(result, CellValue::Number(-4.0));
372 }
373
374 #[test]
375 fn roundup_with_digits() {
376 let result = eval("ROUNDUP(3.14159,2)");
377 assert_eq!(result, CellValue::Number(3.15));
378 }
379
380 #[test]
383 fn rounddown_positive() {
384 let result = eval("ROUNDDOWN(3.9,0)");
385 assert_eq!(result, CellValue::Number(3.0));
386 }
387
388 #[test]
389 fn rounddown_negative() {
390 let result = eval("ROUNDDOWN(-3.9,0)");
391 assert_eq!(result, CellValue::Number(-3.0));
392 }
393
394 #[test]
397 fn ceiling_basic() {
398 let result = eval("CEILING(2.5,1)");
399 assert_eq!(result, CellValue::Number(3.0));
400 }
401
402 #[test]
403 fn ceiling_significance() {
404 let result = eval("CEILING(4.42,0.05)");
405 if let CellValue::Number(n) = result {
406 assert!(approx_eq(n, 4.45));
407 } else {
408 panic!("expected number");
409 }
410 }
411
412 #[test]
415 fn floor_basic() {
416 let result = eval("FLOOR(2.5,1)");
417 assert_eq!(result, CellValue::Number(2.0));
418 }
419
420 #[test]
421 fn floor_zero_significance() {
422 let result = eval("FLOOR(2.5,0)");
423 assert_eq!(result, CellValue::Error("#DIV/0!".to_string()));
424 }
425
426 #[test]
429 fn sign_positive() {
430 assert_eq!(eval("SIGN(42)"), CellValue::Number(1.0));
431 }
432
433 #[test]
434 fn sign_negative() {
435 assert_eq!(eval("SIGN(-42)"), CellValue::Number(-1.0));
436 }
437
438 #[test]
439 fn sign_zero() {
440 assert_eq!(eval("SIGN(0)"), CellValue::Number(0.0));
441 }
442
443 #[test]
446 fn pi_value() {
447 if let CellValue::Number(n) = eval("PI()") {
448 assert!(approx_eq(n, std::f64::consts::PI));
449 } else {
450 panic!("expected number");
451 }
452 }
453
454 #[test]
457 fn log_base10_default() {
458 if let CellValue::Number(n) = eval("LOG(100)") {
459 assert!(approx_eq(n, 2.0));
460 } else {
461 panic!("expected number");
462 }
463 }
464
465 #[test]
466 fn log_base2() {
467 if let CellValue::Number(n) = eval("LOG(8,2)") {
468 assert!(approx_eq(n, 3.0));
469 } else {
470 panic!("expected number");
471 }
472 }
473
474 #[test]
475 fn log_negative_input() {
476 assert_eq!(eval("LOG(-1)"), CellValue::Error("#NUM!".to_string()));
477 }
478
479 #[test]
482 fn log10_basic() {
483 if let CellValue::Number(n) = eval("LOG10(1000)") {
484 assert!(approx_eq(n, 3.0));
485 } else {
486 panic!("expected number");
487 }
488 }
489
490 #[test]
493 fn ln_basic() {
494 if let CellValue::Number(n) = eval("LN(1)") {
495 assert!(approx_eq(n, 0.0));
496 } else {
497 panic!("expected number");
498 }
499 }
500
501 #[test]
504 fn exp_basic() {
505 if let CellValue::Number(n) = eval("EXP(0)") {
506 assert!(approx_eq(n, 1.0));
507 } else {
508 panic!("expected number");
509 }
510 }
511
512 #[test]
513 fn exp_one() {
514 if let CellValue::Number(n) = eval("EXP(1)") {
515 assert!(approx_eq(n, std::f64::consts::E));
516 } else {
517 panic!("expected number");
518 }
519 }
520
521 #[test]
524 fn product_basic() {
525 assert_eq!(eval("PRODUCT(2,3,4)"), CellValue::Number(24.0));
526 }
527
528 #[test]
531 fn quotient_basic() {
532 assert_eq!(eval("QUOTIENT(7,2)"), CellValue::Number(3.0));
533 }
534
535 #[test]
536 fn quotient_negative() {
537 assert_eq!(eval("QUOTIENT(-7,2)"), CellValue::Number(-3.0));
538 }
539
540 #[test]
541 fn quotient_div_zero() {
542 assert_eq!(
543 eval("QUOTIENT(7,0)"),
544 CellValue::Error("#DIV/0!".to_string())
545 );
546 }
547
548 #[test]
551 fn fact_basic() {
552 assert_eq!(eval("FACT(5)"), CellValue::Number(120.0));
553 }
554
555 #[test]
556 fn fact_zero() {
557 assert_eq!(eval("FACT(0)"), CellValue::Number(1.0));
558 }
559
560 #[test]
561 fn fact_negative() {
562 assert_eq!(eval("FACT(-1)"), CellValue::Error("#NUM!".to_string()));
563 }
564
565 #[test]
568 fn rand_returns_number() {
569 if let CellValue::Number(n) = eval("RAND()") {
570 assert!(n >= 0.0 && n < 1.0);
571 } else {
572 panic!("expected number");
573 }
574 }
575
576 #[test]
579 fn randbetween_returns_integer_in_range() {
580 if let CellValue::Number(n) = eval("RANDBETWEEN(1,10)") {
581 assert!(n >= 1.0 && n <= 10.0);
582 assert_eq!(n, n.floor());
583 } else {
584 panic!("expected number");
585 }
586 }
587
588 #[test]
589 fn randbetween_invalid_range() {
590 assert_eq!(
591 eval("RANDBETWEEN(10,1)"),
592 CellValue::Error("#NUM!".to_string())
593 );
594 }
595}