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)]
258mod tests {
259 use crate::cell::CellValue;
260 use crate::formula::eval::{evaluate, CellSnapshot};
261 use crate::formula::parser::parse_formula;
262
263 fn eval_with_data(formula: &str, data: &[(&str, u32, u32, CellValue)]) -> CellValue {
264 let mut snap = CellSnapshot::new("Sheet1".to_string());
265 for (sheet, col, row, val) in data {
266 snap.set_cell(sheet, *col, *row, val.clone());
267 }
268 let expr = parse_formula(formula).unwrap();
269 evaluate(&expr, &snap).unwrap()
270 }
271
272 fn eval(formula: &str) -> CellValue {
273 eval_with_data(formula, &[])
274 }
275
276 fn approx_eq(a: f64, b: f64) -> bool {
277 (a - b).abs() < 1e-9
278 }
279
280 #[test]
283 fn sumif_greater_than() {
284 let data = vec![
285 ("Sheet1", 1, 1, CellValue::Number(1.0)),
286 ("Sheet1", 1, 2, CellValue::Number(5.0)),
287 ("Sheet1", 1, 3, CellValue::Number(10.0)),
288 ];
289 let result = eval_with_data("SUMIF(A1:A3,\">3\")", &data);
290 assert_eq!(result, CellValue::Number(15.0));
291 }
292
293 #[test]
294 fn sumif_less_than_or_equal() {
295 let data = vec![
296 ("Sheet1", 1, 1, CellValue::Number(1.0)),
297 ("Sheet1", 1, 2, CellValue::Number(5.0)),
298 ("Sheet1", 1, 3, CellValue::Number(10.0)),
299 ];
300 let result = eval_with_data("SUMIF(A1:A3,\"<=5\")", &data);
301 assert_eq!(result, CellValue::Number(6.0));
302 }
303
304 #[test]
305 fn sumif_exact_text_match() {
306 let data = vec![
307 ("Sheet1", 1, 1, CellValue::String("Apple".to_string())),
308 ("Sheet1", 2, 1, CellValue::Number(10.0)),
309 ("Sheet1", 1, 2, CellValue::String("Banana".to_string())),
310 ("Sheet1", 2, 2, CellValue::Number(20.0)),
311 ("Sheet1", 1, 3, CellValue::String("Apple".to_string())),
312 ("Sheet1", 2, 3, CellValue::Number(30.0)),
313 ];
314 let result = eval_with_data("SUMIF(A1:A3,\"Apple\",B1:B3)", &data);
315 assert_eq!(result, CellValue::Number(40.0));
316 }
317
318 #[test]
319 fn sumif_not_equal() {
320 let data = vec![
321 ("Sheet1", 1, 1, CellValue::Number(0.0)),
322 ("Sheet1", 1, 2, CellValue::Number(5.0)),
323 ("Sheet1", 1, 3, CellValue::Number(0.0)),
324 ];
325 let result = eval_with_data("SUMIF(A1:A3,\"<>0\")", &data);
326 assert_eq!(result, CellValue::Number(5.0));
327 }
328
329 #[test]
330 fn sumif_no_sum_range() {
331 let data = vec![
332 ("Sheet1", 1, 1, CellValue::Number(2.0)),
333 ("Sheet1", 1, 2, CellValue::Number(4.0)),
334 ("Sheet1", 1, 3, CellValue::Number(6.0)),
335 ];
336 let result = eval_with_data("SUMIF(A1:A3,\">3\")", &data);
337 assert_eq!(result, CellValue::Number(10.0));
338 }
339
340 #[test]
343 fn sumifs_multi_criteria() {
344 let data = vec![
345 ("Sheet1", 1, 1, CellValue::String("A".to_string())),
346 ("Sheet1", 2, 1, CellValue::Number(1.0)),
347 ("Sheet1", 3, 1, CellValue::Number(10.0)),
348 ("Sheet1", 1, 2, CellValue::String("B".to_string())),
349 ("Sheet1", 2, 2, CellValue::Number(2.0)),
350 ("Sheet1", 3, 2, CellValue::Number(20.0)),
351 ("Sheet1", 1, 3, CellValue::String("A".to_string())),
352 ("Sheet1", 2, 3, CellValue::Number(3.0)),
353 ("Sheet1", 3, 3, CellValue::Number(30.0)),
354 ];
355 let result = eval_with_data("SUMIFS(C1:C3,A1:A3,\"A\",B1:B3,\">1\")", &data);
356 assert_eq!(result, CellValue::Number(30.0));
357 }
358
359 #[test]
362 fn roundup_positive() {
363 let result = eval("ROUNDUP(3.2,0)");
364 assert_eq!(result, CellValue::Number(4.0));
365 }
366
367 #[test]
368 fn roundup_negative() {
369 let result = eval("ROUNDUP(-3.2,0)");
370 assert_eq!(result, CellValue::Number(-4.0));
371 }
372
373 #[test]
374 fn roundup_with_digits() {
375 let result = eval("ROUNDUP(3.14159,2)");
376 assert_eq!(result, CellValue::Number(3.15));
377 }
378
379 #[test]
382 fn rounddown_positive() {
383 let result = eval("ROUNDDOWN(3.9,0)");
384 assert_eq!(result, CellValue::Number(3.0));
385 }
386
387 #[test]
388 fn rounddown_negative() {
389 let result = eval("ROUNDDOWN(-3.9,0)");
390 assert_eq!(result, CellValue::Number(-3.0));
391 }
392
393 #[test]
396 fn ceiling_basic() {
397 let result = eval("CEILING(2.5,1)");
398 assert_eq!(result, CellValue::Number(3.0));
399 }
400
401 #[test]
402 fn ceiling_significance() {
403 let result = eval("CEILING(4.42,0.05)");
404 if let CellValue::Number(n) = result {
405 assert!(approx_eq(n, 4.45));
406 } else {
407 panic!("expected number");
408 }
409 }
410
411 #[test]
414 fn floor_basic() {
415 let result = eval("FLOOR(2.5,1)");
416 assert_eq!(result, CellValue::Number(2.0));
417 }
418
419 #[test]
420 fn floor_zero_significance() {
421 let result = eval("FLOOR(2.5,0)");
422 assert_eq!(result, CellValue::Error("#DIV/0!".to_string()));
423 }
424
425 #[test]
428 fn sign_positive() {
429 assert_eq!(eval("SIGN(42)"), CellValue::Number(1.0));
430 }
431
432 #[test]
433 fn sign_negative() {
434 assert_eq!(eval("SIGN(-42)"), CellValue::Number(-1.0));
435 }
436
437 #[test]
438 fn sign_zero() {
439 assert_eq!(eval("SIGN(0)"), CellValue::Number(0.0));
440 }
441
442 #[test]
445 fn pi_value() {
446 if let CellValue::Number(n) = eval("PI()") {
447 assert!(approx_eq(n, std::f64::consts::PI));
448 } else {
449 panic!("expected number");
450 }
451 }
452
453 #[test]
456 fn log_base10_default() {
457 if let CellValue::Number(n) = eval("LOG(100)") {
458 assert!(approx_eq(n, 2.0));
459 } else {
460 panic!("expected number");
461 }
462 }
463
464 #[test]
465 fn log_base2() {
466 if let CellValue::Number(n) = eval("LOG(8,2)") {
467 assert!(approx_eq(n, 3.0));
468 } else {
469 panic!("expected number");
470 }
471 }
472
473 #[test]
474 fn log_negative_input() {
475 assert_eq!(eval("LOG(-1)"), CellValue::Error("#NUM!".to_string()));
476 }
477
478 #[test]
481 fn log10_basic() {
482 if let CellValue::Number(n) = eval("LOG10(1000)") {
483 assert!(approx_eq(n, 3.0));
484 } else {
485 panic!("expected number");
486 }
487 }
488
489 #[test]
492 fn ln_basic() {
493 if let CellValue::Number(n) = eval("LN(1)") {
494 assert!(approx_eq(n, 0.0));
495 } else {
496 panic!("expected number");
497 }
498 }
499
500 #[test]
503 fn exp_basic() {
504 if let CellValue::Number(n) = eval("EXP(0)") {
505 assert!(approx_eq(n, 1.0));
506 } else {
507 panic!("expected number");
508 }
509 }
510
511 #[test]
512 fn exp_one() {
513 if let CellValue::Number(n) = eval("EXP(1)") {
514 assert!(approx_eq(n, std::f64::consts::E));
515 } else {
516 panic!("expected number");
517 }
518 }
519
520 #[test]
523 fn product_basic() {
524 assert_eq!(eval("PRODUCT(2,3,4)"), CellValue::Number(24.0));
525 }
526
527 #[test]
530 fn quotient_basic() {
531 assert_eq!(eval("QUOTIENT(7,2)"), CellValue::Number(3.0));
532 }
533
534 #[test]
535 fn quotient_negative() {
536 assert_eq!(eval("QUOTIENT(-7,2)"), CellValue::Number(-3.0));
537 }
538
539 #[test]
540 fn quotient_div_zero() {
541 assert_eq!(
542 eval("QUOTIENT(7,0)"),
543 CellValue::Error("#DIV/0!".to_string())
544 );
545 }
546
547 #[test]
550 fn fact_basic() {
551 assert_eq!(eval("FACT(5)"), CellValue::Number(120.0));
552 }
553
554 #[test]
555 fn fact_zero() {
556 assert_eq!(eval("FACT(0)"), CellValue::Number(1.0));
557 }
558
559 #[test]
560 fn fact_negative() {
561 assert_eq!(eval("FACT(-1)"), CellValue::Error("#NUM!".to_string()));
562 }
563
564 #[test]
567 fn rand_returns_number() {
568 if let CellValue::Number(n) = eval("RAND()") {
569 assert!(n >= 0.0 && n < 1.0);
570 } else {
571 panic!("expected number");
572 }
573 }
574
575 #[test]
578 fn randbetween_returns_integer_in_range() {
579 if let CellValue::Number(n) = eval("RANDBETWEEN(1,10)") {
580 assert!(n >= 1.0 && n <= 10.0);
581 assert_eq!(n, n.floor());
582 } else {
583 panic!("expected number");
584 }
585 }
586
587 #[test]
588 fn randbetween_invalid_range() {
589 assert_eq!(
590 eval("RANDBETWEEN(10,1)"),
591 CellValue::Error("#NUM!".to_string())
592 );
593 }
594}