1pub mod xirr;
2
3use crate::{
4 evaluate::{
5 evaluate_expr_with_context,
6 evaluate_str,
7 ensure_non_range,
8 value::Value,
9 },
10 reference::Reference,
11 cell::Cell,
12 errors::Error,
13 parser::ast::{Expr, Error as ExcelError},
14 workbook::Book,
15};
16use excel_emulator_macro::function;
17use chrono::{Months, naive::NaiveDate, Datelike};
18
19pub fn get_function_value(name: &str, args: Vec<Value>) -> Result<Value, Error> {
20 match name {
21 "SUM" => Ok(Box::new(Sum::from(args)).evaluate()),
22 "SUMIF" => Ok(Box::new(Sumifs::from(args)).evaluate()),
23 "AVERAGE" => Ok(Box::new(Average::from(args)).evaluate()),
24 "AVERAGEIF" => Ok(Box::new(Averageif::from(args)).evaluate()),
25 "COUNT" => Ok(Box::new(Count::from(args)).evaluate()),
26 "EXPONENT" => Ok(Box::new(Exponent::from(args)).evaluate()),
27 "CONCAT" => Ok(Box::new(Concat::from(args)).evaluate()),
28 "AND" => Ok(Box::new(Andfunc::from(args)).evaluate()),
29 "OR" => Ok(Box::new(Orfunc::from(args)).evaluate()),
30 "MAX" => Ok(Box::new(Max::from(args)).evaluate()),
31 "MIN" => Ok(Box::new(Min::from(args)).evaluate()),
32 "MATCH" => Ok(Box::new(Matchfn::from(args)).evaluate()),
33 "DATE" => Ok(Box::new(Date::from(args)).evaluate()),
34 "FLOOR" => Ok(Box::new(Floor::from(args)).evaluate()),
35 "IFERROR" => {
36 let a = args.get(0).unwrap().clone();
37 let b = args.get(1).unwrap().clone();
38 Ok(Box::new(Iferror { a, b }).evaluate())
39 },
40 "EOMONTH" => Ok(Box::new(Eomonth::from(args)).evaluate()),
41 "SUMIFS" => Ok(Box::new(Sumifs::from(args)).evaluate()),
42 "AVERAGEIFS" => Ok(Box::new(Averageifs::from(args)).evaluate()),
43 "XIRR" => Ok(Box::new(Xirrfunc::from(args)).evaluate()),
44 "IF" => Ok(Box::new(Iffunc::from(args)).evaluate()),
45 "XNPV" => Ok(Box::new(Xnpv::from(args)).evaluate()),
46 "YEARFRAC" => Ok(Box::new(Yearfrac::from(args)).evaluate()),
47 "DATEDIF" => Ok(Box::new(Datedif::from(args)).evaluate()),
48 "PMT" => Ok(Box::new(Pmt::from(args)).evaluate()),
49 "COUNTA" => Ok(Box::new(Counta::from(args)).evaluate()),
50 "ROUNDDOWN" => Ok(Box::new(Rounddown::from(args)).evaluate()),
51 _ => Err(Error::FunctionNotSupport(name.to_string()))
52 }
53}
54
55pub trait Function {
56 fn evaluate(self) -> Value;
57}
58
59pub fn offset_reference(r: &mut Reference, rows: i32, cols: i32, height: Option<i32>, width: Option<i32>) -> Reference {
60 if r.row() as i32 + rows < 0 || r.column() as i32 + cols < 0 {
61 panic!("Invalid offset");
62 } else {
63 r.offset((rows, cols));
64 }
65 let mut end_cell : Option<Cell> = None;
66 if height.is_some() || width.is_some() {
67 let h_u : i32 = height.unwrap_or(0);
68 let w_u : i32 = width.unwrap_or(0);
69 if h_u.abs() > 1 || w_u.abs() > 1 {
70 let h_offset = match h_u.is_positive() {
71 true => h_u - 1,
72 false => h_u + 1
73 };
74 let w_offset = match w_u.is_positive() {
75 true => w_u - 1,
76 false => w_u + 1
77 };
78 end_cell = Some(
79 Cell::from((
80 (r.row() as i32 + h_offset) as usize,
81 (r.column() as i32 + w_offset) as usize
82 ))
83 );
84 }
85 }
86 r.end_cell = end_cell;
87 if let Some(end_cell) = r.end_cell {
88 if end_cell < r.start_cell {
89 return Reference::from((end_cell, Some(r.start_cell)));
90 }
91 }
92 *r
93}
94
95#[function]
96fn exponent(a: Value, b: Value) -> Value {
97 Value::from(a.as_num().powf(b.as_num()))
98}
99
100#[function]
101fn sum(args: Vec<Value>) -> Value {
102 args.into_iter().fold(Value::from(0.0), |mut s, v| {
103 if let Value::Array(arr) = v {
104 for x in arr {
105 if x.is_num() {
106 s += x
107 }
108 }
109 } else if let Value::Array2(arr2) = v {
110 for x in arr2 {
111 if x.is_num() {
112 s += x
113 }
114 }
115 } else {
116 s += Value::from(v.as_num())
117 }
118 s
119 })
120}
121
122#[function]
123fn average(args: Vec<Value>) -> Value {
124 let mut count = 0.0;
125 let mut sum_values: Vec<Value> = vec![];
126 for arg in args.into_iter() {
127 if let Value::Array(arr) = arg {
128 for x in arr {
129 if x.is_num() {
130 sum_values.push(x);
131 count += 1.0;
132 }
133 }
134 } else {
135 sum_values.push(Value::from(arg.as_num()));
136 count += 1.0;
137 }
138 }
139 let average = sum_values.into_iter().fold(0.0, |mut s, v| {
140 s += v.as_num();
141 s
142 }) / count;
143 Value::from(average)
144}
145
146#[function]
147fn count(args: Vec<Value>) -> Value {
148 let mut count = 0.0;
149 for arg in args.iter() {
150 if let Value::Array(arr) = arg {
151 for x in arr.iter() {
152 if x.is_num() {
153 count += 1.0;
154 }
155 }
156 } else {
157 count += 1.0;
158 }
159 }
160 Value::from(count)
161}
162
163#[function]
164fn concat(a: Value, b: Value) -> Value {
165 Value::from(format!("{}{}", a.as_text(), b.as_text()))
166}
167
168#[function]
169fn andfunc(a: Value, b: Value) -> Value {
170 Value::from(a.as_bool() && b.as_bool())
171}
172
173#[function]
174fn orfunc(a: Value, b: Value) -> Value {
175 Value::from(a.as_bool() || b.as_bool())
176}
177
178#[function]
179fn max(args: Vec<Value>) -> Value {
180 let mut output = args[0].clone();
181 for v in args.into_iter() {
182 if let Value::Array(arr) = v {
183 for x in arr {
184 if x.is_num() {
185 output = output.max(x);
186 }
187 }
188 } else if let Value::Array2(arr2) = v {
189 for x in arr2 {
190 if x.is_num() {
191 output = output.max(x);
192 }
193 }
194 } else {
195 output = output.max(v);
196 }
197 }
198 output
199}
200
201#[function]
202fn min(args: Vec<Value>) -> Value {
203 let mut output = args[0].clone();
204 for v in args.into_iter() {
205 if let Value::Array(arr) = v {
206 for x in arr {
207 if x.is_num() {
208 output = output.min(x);
209 }
210 }
211 } else if let Value::Array2(arr2) = v {
212 for x in arr2 {
213 if x.is_num() {
214 output = output.min(x);
215 }
216 }
217 } else {
218 output = output.min(v);
219 }
220 }
221 output
222}
223
224#[function]
225fn matchfn(lookup_value: Value, lookup_array: Value, match_type: Value) -> Value {
226 let lookup_value = lookup_value.ensure_single();
227 let mut lookup_array_mut = lookup_array.as_array();
228 if match_type.as_num() == -1.0 {
229 lookup_array_mut.sort_by(|a, b| b.cmp(a)); match lookup_array.as_array().into_iter().enumerate().filter(|(_,v)| v >= &lookup_value).last() {
233 Some(v) => { Value::from(v.0 + 1) },
234 _ => Value::Error(ExcelError::NA)
235 }
236 } else if match_type.as_num() == 0.0 {
237 match lookup_array_mut.into_iter().position(|v| v == lookup_value) {
238 Some(v) => { Value::from(v + 1) },
239 _ => Value::Error(ExcelError::NA)
240 }
241 } else {
242 lookup_array_mut.sort(); match lookup_array_mut.into_iter().enumerate().filter(|(_, v)| v <= &lookup_value).last() {
246 Some(v) => { Value::from(v.0 + 1) },
247 _ => Value::Error(ExcelError::NA)
248 }
249 }
250}
251
252#[function]
253fn date(year: Value, month: Value, day: Value) -> Value {
254 Value::from(NaiveDate::from_ymd(year.as_num() as i32, month.as_num() as u32, day.as_num() as u32))
255}
256
257
258#[function]
259fn floor(x: Value, _significance: Value) -> Value {
261 Value::from(math::round::floor(x.as_num(), 0))
262}
263
264pub fn index(args: Vec<Expr>, book: &Book, debug: bool) -> Result<Value, Error> {
271 let mut arg_values = args.into_iter();
272 let array: Value = evaluate_expr_with_context(arg_values.next().unwrap(), book, debug)?; let row_num: Value = evaluate_expr_with_context(arg_values.next().unwrap(), book, debug)?;
274 let col_num_option = arg_values.next();
275 let col_num = match col_num_option {
276 Some(expr) => evaluate_expr_with_context(expr, book, debug)?,
277 None => Value::from(1.0)
278 };
279 if array.is_err() {
281 return Ok(array);
282 } else if row_num.is_err() {
283 return Ok(row_num);
284 } else if col_num.is_err() {
285 return Ok(col_num);
286 }
287 let row_idx = row_num.as_num() as usize - 1;
288 let col_idx = col_num.as_num() as usize - 1;
289 if let Value::Range { sheet, reference, value } = array {
290 let reference = Reference::from(reference);
291 let (start_row, start_col, _, _) = reference.get_dimensions();
292
293 if row_num.as_num() == 0.0 {
296 let new_col = start_col + col_idx;
297 return Ok(Value::Range { sheet: sheet.clone(), reference: Reference::from((0, new_col)), value: None });
298 }
299
300 if col_num.as_num() == 0.0 {
303 let new_row = start_row + row_idx;
304 return Ok(Value::Range { sheet: sheet.clone(), reference: Reference::from((new_row, 0)), value: None });
305 }
306
307 let new_row = start_row + row_idx;
308 let new_col = start_col + col_idx;
309 let new_value: Value = value.unwrap().as_array2()[[row_idx, col_idx]].clone();
310 return Ok(Value::Range { sheet: sheet.clone(), reference: Reference::from((new_row, new_col)), value: Some(Box::new(new_value)) });
311 } else {
312 panic!("First argument must be a range.");
313 }
314}
315
316pub fn offset(args: Vec<Expr>, book: &Book, debug: bool) -> Result<Value, Error> {
317 let array = evaluate_expr_with_context(args.get(0).unwrap().clone(), book, debug)?;
318 if let Value::Range { sheet, reference, value: _ } = array {
319 let rows = ensure_non_range(evaluate_expr_with_context(args.get(1).unwrap().clone(), book, debug)?);
320 let cols = ensure_non_range(evaluate_expr_with_context(args.get(2).unwrap().clone(), book, debug)?);
321 let height = args.get(3);
322 let height_opt: Option<i32> = height.map(|h| {
323 ensure_non_range(evaluate_expr_with_context(h.clone(), book, debug).unwrap()).as_num() as i32
324 });
325 let width = args.get(4);
326 let width_opt: Option<i32> = width.map(|w| {
327 ensure_non_range(evaluate_expr_with_context(w.clone(), book, debug).unwrap()).as_num() as i32
328 });
329 let new_reference = offset_reference(&mut reference.clone(), rows.as_num() as i32, cols.as_num() as i32, height_opt, width_opt);
330 let new_expr = Expr::Reference { sheet: sheet.clone(), reference: new_reference.to_string() };
331 if book.is_calculated(new_expr.clone()) {
332 let reference_value = match evaluate_expr_with_context(new_expr.clone(), book, debug) {
333 Ok(value) => Some(Box::new(ensure_non_range(value))),
334 _ => panic!("New expression could not be evaluated: {}", new_expr.clone())
335 };
336 Ok(Value::Range { sheet: sheet.clone(), reference: new_reference, value: reference_value})
337 } else {
338 Err(Error::Volatile(Box::new(new_expr)))
339 }
340 } else {
341 panic!("First expression must be a Reference.")
342 }
343}
344
345struct Iferror {
346 a: Value,
347 b: Value,
348}
349
350impl Function for Iferror {
351 fn evaluate(self) -> Value {
352 if self.a.is_err() {
353 self.b
354 } else {
355 self.a
356 }
357 }
358}
359
360#[function]
361fn eomonth(start_date: Value, months: Value) -> Value {
362 let start_date: NaiveDate = start_date.as_date();
363 let bom = NaiveDate::from_ymd(start_date.year(), start_date.month(), 1);
364 let eom: NaiveDate;
365 if months.as_num() > 0.0 {
366 eom = bom.checked_add_months(Months::new((months.as_num()+1.0) as u32)).unwrap();
367 } else if months.as_num() < 0.0 {
368 eom = bom.checked_sub_months(Months::new((months.as_num()*-1.0-1.0) as u32)).unwrap();
369 } else {
370 eom = bom.checked_add_months(Months::new(1)).unwrap();
371 }
372 Value::from(eom.pred())
373}
374
375#[function]
376fn sumifs(sum_range: Value, args: Vec<Value>) -> Value {
377 let mut keep_index: Vec<usize> = vec![];
378 for (idx, i) in (0..args.len()).step_by(2).enumerate() {
379 let cell_range: Vec<Value> = args.get(i).unwrap().as_array();
380 let criteria: Value = args.get(i+1).unwrap().ensure_single();
381 let criteria_text = criteria.as_text();
382 for (y, cell) in cell_range.iter().enumerate() {
383 let eval: bool = parse_criteria(criteria_text.as_str(), cell);
384 if idx == 0 {
385 if eval {
386 keep_index.push(y);
387 }
388 } else {
389 if ! eval && keep_index.contains(&y) {
390 keep_index.retain(|x| x != &y);
391 }
392 }
393 }
394 }
395 Value::from(sum_range.as_array()
396 .into_iter()
397 .enumerate()
398 .filter_map(|(i, v)| match keep_index.contains(&i) {
399 true => Some(v.as_num()),
400 false => None
401 })
402 .collect::<Vec<f64>>()
403 .iter()
404 .sum::<f64>())
405}
406
407#[function]
408fn sumif(range: Value, criteria: Value, sum_range: Option<Value>) -> Value {
409 let mut keep_index: Vec<usize> = vec![];
410 let range: Vec<Value> = range.as_array();
411 let criteria = criteria.ensure_single();
412 let criteria_text = format!("{}", criteria);
413 for (i, cell) in range.iter().enumerate() {
414 let eval = parse_criteria(criteria_text.as_str(), cell);
415 if eval && !keep_index.contains(&i) {
416 keep_index.push(i);
417 }
418 }
419 let sum_range = match sum_range {
420 Some(val) => val.as_array(),
421 None => range
422 };
423 Value::from(sum_range
424 .into_iter()
425 .enumerate()
426 .filter_map(|(i, v)| match keep_index.contains(&i) {
427 true => Some(v.as_num()),
428 false => None
429 })
430 .collect::<Vec<f64>>()
431 .iter()
432 .sum::<f64>())
433}
434
435fn parse_criteria(c: &str, cell: &Value) -> bool {
436 let cell = cell.ensure_single().as_text();
437 let op: &str = if c.split("<>").count() > 1 {
438 "<>"
439 } else if c.split("<=").count() > 1 {
440 "<="
441 } else if c.split("<").count() > 1 {
442 "<"
443 } else if c.split(">=").count() > 1 {
444 ">="
445 } else if c.split(">").count() > 1 {
446 ">"
447 } else if c.split("=").count() > 1 {
448 "="
449 } else {
450 ""
451 };
452 if ! op.is_empty() {
453 evaluate_str(format!("\"{}\"{}\"{}\"", c.split(op).collect::<Vec<&str>>()[1], op, cell).as_str()).unwrap().as_bool()
454 } else {
455 evaluate_str(format!("\"{}\"=\"{}\"", c, cell).as_str()).unwrap().as_bool()
456 }
457}
458
459#[function]
460fn averageif(range: Value, criteria: Value, average_range: Option<Value>) -> Value {
461 let mut keep_index: Vec<usize> = vec![];
462 let range: Vec<Value> = range.as_array();
463 let criteria = criteria.ensure_single();
464 let criteria_text = criteria.as_text();
465 for (i, cell) in range.iter().enumerate() {
466 let eval = parse_criteria(criteria_text.as_str(), cell);
467 if eval && !keep_index.contains(&i) {
468 keep_index.push(i);
469 }
470 }
471 let average_range = match average_range {
472 Some(val) => val.as_array(),
473 None => range
474 };
475 let average_range_filter = average_range
476 .into_iter()
477 .enumerate()
478 .filter_map(|(i, v)| match keep_index.contains(&i) {
479 true => Some(v.as_num()),
480 false => None
481 }).collect::<Vec<f64>>();
482 Value::from(average_range_filter
483 .iter()
484 .sum::<f64>()/average_range_filter.len() as f64)
485}
486
487
488
489#[function]
490fn averageifs(average_range: Value, args: Vec<Value>) -> Value {
491 let mut keep_index: Vec<usize> = vec![];
492 for i in (0..args.len()).step_by(2) {
493 let cell_range: Vec<Value> = args.get(i).unwrap().as_array();
494 let criteria: Value = args.get(i+1).unwrap().ensure_single();
495 let criteria_text = criteria.as_text();
496 for (i, cell) in cell_range.iter().enumerate() {
497 let eval = parse_criteria(criteria_text.as_str(), cell);
498 if eval && !keep_index.contains(&i) {
499 keep_index.push(i);
500 }
501 }
502 }
503 let average_range_filter = average_range.as_array()
504 .into_iter()
505 .enumerate()
506 .filter_map(|(i, v)| match keep_index.contains(&i) {
507 true => Some(v.as_num()),
508 false => None
509 }).collect::<Vec<f64>>();
510 Value::from(average_range_filter
511 .iter()
512 .sum::<f64>()/average_range_filter.len() as f64)
513}
514
515
516
517#[function]
518fn xirrfunc(values: Value, dates: Value) -> Value {
519 let payments: Vec<xirr::Payment> = values
520 .as_array()
521 .iter()
522 .zip(
523 dates
524 .as_array()
525 .iter()
526 ).map(|(v, d)| xirr::Payment { amount: v.as_num(), date: d.as_date() })
527 .collect();
528 match xirr::compute(&payments) {
529 Ok(v) => Value::from(v),
530 _ => Value::Error(ExcelError::Num),
531 }
532}
533
534#[function]
535fn iffunc(condition: Value, a: Value, b: Value) -> Value {
536 if condition.as_bool() {
537 a
538 } else {
539 b
540 }
541}
542
543#[function]
544fn xnpv(rate: Value, values: Value, dates: Value) -> Value {
545 let rate: f64 = rate.as_num();
546 let dates: Vec<NaiveDate> = dates.as_array().iter().map(|x| x.as_date()).collect();
547 let start_date = *dates.get(0).unwrap();
548 Value::from(
549 values.as_array().iter().map(|x| x.as_num())
550 .into_iter()
551 .zip(
552 dates
553 .into_iter()
554 ).fold(0.0, |s, (value, date)| {
555 let days = NaiveDate::signed_duration_since(date, start_date).num_days() as f64;
556 s + (value / ((1.0+rate).powf(days / 365.0)))
557 })
558 )
559}
560
561#[function]
562fn yearfrac(start_date: Value, end_date: Value) -> Value {
564 let start_date: NaiveDate = start_date.as_date();
565 let end_date: NaiveDate = end_date.as_date();
566 Value::from(
567 (
568 ((end_date.year() as i32 - start_date.year() as i32) * 360) +
569 (end_date.ordinal() as i32 - start_date.ordinal() as i32)
570 ) as f64 / 360.0
571 )
572}
573
574#[function]
575fn datedif(start_date: Value, end_date: Value, unit: Value) -> Value {
576 let start_date: NaiveDate = start_date.as_date();
577 let end_date: NaiveDate = end_date.as_date();
578 match unit.as_text().as_str() {
579 "Y" | "y" => Value::from(end_date.year() - start_date.year()),
580 "M" | "m" => Value::from((end_date.year() as i32 - start_date.year() as i32)*12 + (end_date.month() as i32 - start_date.month() as i32)),
581 "D" | "d" => Value::from(NaiveDate::signed_duration_since(end_date, start_date).num_days() as f64),
582 "MD" | "md" => Value::from(end_date.day() as i32 - start_date.day() as i32),
583 "YM" | "ym" => Value::from(end_date.month() as i32 - start_date.month() as i32),
584 "YD" | "yd" => Value::from(end_date.ordinal() as i32 - start_date.ordinal() as i32),
585 _ => panic!("Not a valid unit.")
586 }
587}
588
589#[function]
590fn pmt(rate: Value, nper: Value, pv: Value, fv: Option<Value>, f_type: Option<Value>) -> Value {
591 let rate = rate.as_num();
592 let nper = nper.as_num();
593 let pv = pv.as_num();
594 let fv = fv.unwrap_or_else(|| Value::from(0.0)).as_num();
595 let f_type = f_type.unwrap_or_else(|| Value::from(0.0)).as_num();
596 let value = rate*(fv*-1.0+pv*(1.0+rate).powf(nper))/((1.0+rate*f_type)*(1.0-(1.0+rate).powf(nper)));
597 if value == f64::INFINITY || value == f64::NEG_INFINITY {
598 Value::Error(ExcelError::Num)
599 } else {
600 Value::from(value)
601 }
602}
603
604#[function]
605fn counta(args: Vec<Value>) -> Value {
606 Value::from(
607 args.into_iter().fold(0, |s, v| {
608 match v {
609 Value::Array(arr) => {
610 s + arr.into_iter().fold(0, |s, v| match v {
611 Value::Empty => s,
612 _ => s + 1
613 })
614 },
615 Value::Array2(arr2) => {
616 s + arr2.into_raw_vec().into_iter().fold(0, |s, v| match v {
617 Value::Empty => s,
618 _ => s + 1
619 })
620 },
621 _ => s + 1
622 }
623 })
624 )
625}
626
627#[function]
628fn rounddown(x: Value, num_digits: Value) -> Value {
629 let x: f64 = x.as_num();
630 let num_digits: f64 = num_digits.as_num();
631 if num_digits > 0.0 {
632 Value::from(((x * 10.0_f64.powf(num_digits)) as i64) as f64 / 10.0_f64.powf(num_digits))
633 } else if num_digits < 0.0 {
634 Value::from(((x / 10.0_f64.powf(-num_digits)) as i64) as f64 * 10.0_f64.powf(-num_digits))
635 } else {
636 Value::from((x as i64) as f64)
637 }
638}
639
640#[cfg(test)]
641mod tests {
642 use crate::{
643 evaluate::{
644 value:: Value,
645 evaluate_str
646 },
647 workbook::Book,
648 errors::Error,
649 };
650 use chrono::naive::NaiveDate;
651
652 #[test]
653 fn test_rounddown() -> Result<(), Error> {
654 assert_eq!(evaluate_str("ROUNDDOWN(3.2, 0)")?, Value::from(3.0));
655 assert_eq!(evaluate_str("ROUNDDOWN(76.9, 0)")?, Value::from(76.0));
656 assert_eq!(evaluate_str("ROUNDDOWN(3.14159, 3)")?, Value::from(3.141));
657 assert_eq!(evaluate_str("ROUNDDOWN(-3.14159, 1)")?, Value::from(-3.1));
658 assert_eq!(evaluate_str("ROUNDDOWN(31415.92654, -2)")?, Value::from(31400));
659 Ok(())
660 }
661
662 #[test]
663 fn test_counta() -> Result<(), Error> {
664 assert_eq!(evaluate_str("COUNTA(1,2,3,4,5)")?, Value::from(5.0));
665 assert_eq!(evaluate_str("COUNTA({1,2,3,4,5})")?, Value::from(5.0));
666 assert_eq!(evaluate_str("COUNTA({1,2,3,4,5},6,\"7\")")?, Value::from(7.0));
667 Ok(())
668 }
669
670 #[test]
671 fn test_pmt() -> Result<(), Error> {
672 assert!((-1037.03 - evaluate_str("PMT(0.08/12, 10, 10000)")?.as_num()).abs() < 0.01);
673 assert!((-1030.16 - evaluate_str("PMT(0.08/12, 10, 10000, 0, 1)")?.as_num()).abs() < 0.01);
674 Ok(())
675 }
676
677 #[test]
678 fn test_datedif() -> Result<(), Error> {
679 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"Y\")")?, Value::from(16.0));
680 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"M\")")?, Value::from(193.0));
681 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"D\")")?, Value::from(5873.0));
682 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"YM\")")?, Value::from(1.0));
683 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"MD\")")?, Value::from(0.0));
684 assert_eq!(evaluate_str("DATEDIF(DATE(2004, 2, 10), DATE(2020, 3, 10), \"YD\")")?, Value::from(29.0));
685 Ok(())
686 }
687
688 #[test]
689 fn test_sum() -> Result<(), Error> {
690 assert_eq!(evaluate_str("SUM(1,2,3,4,5)")?, Value::from(15.0));
691 assert_eq!(evaluate_str("SUM({1,2;3,4})")?, Value::from(10.0));
692 assert_eq!(evaluate_str("SUM({1,2,3,4,5},6,\"7\")")?, Value::from(28.0));
693 assert_eq!(evaluate_str("SUM({1,\"2\",TRUE,4})")?, Value::from(5.0));
694 Ok(())
695 }
696
697 #[test]
698 fn test_average() -> Result<(), Error> {
699 assert_eq!(evaluate_str("AVERAGE(1,2,3,4,5)")?, Value::from(3.0));
700 assert_eq!(evaluate_str("AVERAGE({1,2;3,4})")?, Value::from(2.5));
701 assert_eq!(evaluate_str("AVERAGE({1,2,3,4,5},6,\"7\")")?, Value::from(4.0));
702 assert_eq!(evaluate_str("AVERAGE({1,\"2\",TRUE,4})")?, Value::from(2.5));
703 Ok(())
704 }
705
706 #[test]
707 fn test_count() -> Result<(), Error> {
708 assert_eq!(evaluate_str("COUNT(1,2,3,4,5)")?, Value::from(5.0));
709 assert_eq!(evaluate_str("COUNT({1,2,3,4,5})")?, Value::from(5.0));
710 assert_eq!(evaluate_str("COUNT({1,2,3,4,5},6,\"7\")")?, Value::from(7.0));
711 Ok(())
712 }
713
714 #[test]
715 fn test_concat() -> Result<(), Error> {
716 assert_eq!(evaluate_str("CONCAT(\"test\", \"func\")")?, Value::from("testfunc".to_string()));
717 Ok(())
718 }
719
720 #[test]
721 fn test_and() -> Result<(), Error> {
722 assert_eq!(evaluate_str("AND(TRUE, TRUE)")?, Value::from(true));
723 Ok(())
724 }
725
726 #[test]
727 fn test_or() -> Result<(), Error> {
728 assert_eq!(evaluate_str("OR(TRUE, FALSE)")?, Value::from(true));
729 Ok(())
730 }
731
732 #[test]
733 fn test_max_min() -> Result<(), Error> {
734 assert_eq!(evaluate_str("MAX(1, 5, 10)")?, Value::from(10.0));
735 assert_eq!(evaluate_str("MIN(1, 5, 10)")?, Value::from(1.0));
736 Ok(())
737 }
738
739 #[test]
740 fn test_match() -> Result<(), Error> {
741 assert_eq!(evaluate_str("MATCH(3, {1,2,3,4,5}, 0)")?, Value::from(3.0));
742 Ok(())
743 }
744
745 #[test]
746 fn test_index() -> Result<(), Error> {
747 let mut book = Book::from("assets/functions.xlsx");
748 book.load(false).unwrap();
749 book.calculate(false, false)?;
750 assert_eq!(book.resolve_str_ref("Sheet1!H3")?[[0,0]].as_num(), 11.0);
751 Ok(())
752 }
753
754 #[test]
755 fn test_date() -> Result<(), Error> {
756 assert_eq!(evaluate_str("DATE(2022, 1, 1)")?, Value::from(NaiveDate::from_ymd(2022, 1, 1)));
757 Ok(())
758 }
759
760 #[test]
761 fn test_floor() -> Result<(), Error> {
762 assert_eq!(evaluate_str("FLOOR(3.7, 1)")?, Value::from(3.0));
763 Ok(())
767 }
768
769 #[test]
770 fn test_iferror() -> Result<(), Error> {
771 assert_eq!(evaluate_str("IFERROR(#VALUE!, 1)")?, Value::from(1.0));
772 Ok(())
773 }
774
775 #[test]
776 fn test_eomonth() -> Result<(), Error> {
777 assert_eq!(evaluate_str("EOMONTH(DATE(2004, 2, 29), 12)")?, Value::from(NaiveDate::from_ymd(2005, 2, 28)));
778 assert_eq!(evaluate_str("EOMONTH(DATE(2004, 2, 28), 12)")?, Value::from(NaiveDate::from_ymd(2005, 2, 28)));
779 assert_eq!(evaluate_str("EOMONTH(DATE(2004, 1, 15), -23)")?, Value::from(NaiveDate::from_ymd(2002, 2, 28)));
780 assert_eq!(evaluate_str("EOMONTH(DATE(2004, 1, 15), 0)")?, Value::from(NaiveDate::from_ymd(2004, 1, 31)));
781 Ok(())
782 }
783
784 #[test]
785 fn test_sumifs() -> Result<(), Error> {
786 let mut book = Book::from("assets/functions.xlsx");
787 book.load(false).unwrap();
788 book.calculate(false, false)?;
789 assert_eq!(book.resolve_str_ref("Sheet1!H5")?[[0,0]].as_num(), 2.0);
790 Ok(())
791 }
792
793 #[test]
794 fn test_averageifs() -> Result<(), Error> {
795 let mut book = Book::from("assets/functions.xlsx");
796 book.load(false).unwrap();
797 book.calculate(false, false)?;
798 assert_eq!(book.resolve_str_ref("Sheet1!H8")?[[0,0]].as_num(), 2.0);
799 Ok(())
800 }
801
802 #[test]
803 fn test_xirr() -> Result<(), Error> {
804 let mut book = Book::from("assets/functions.xlsx");
805 book.load(false).unwrap();
806 book.calculate(false, false)?;
807 assert!((0.3340 - book.resolve_str_ref("Sheet1!H4")?[[0,0]].as_num()).abs() < 0.01);
808 Ok(())
809 }
810
811 #[test]
812 fn test_offset() -> Result<(), Error> {
813 let mut book = Book::from("assets/functions.xlsx");
814 book.load(false).unwrap();
815 book.calculate(false, false)?;
816 assert_eq!(book.resolve_str_ref("Sheet1!H6")?[[0,0]].as_num(), 10.0);
817 Ok(())
818 }
819
820 #[test]
821 fn test_if() -> Result<(), Error> {
822 assert_eq!(evaluate_str("IF(TRUE, 1, 2)")?, Value::from(1.0));
823 assert_eq!(evaluate_str("IF(FALSE, 1, 2)")?, Value::from(2.0));
824 Ok(())
825 }
826
827 #[test]
828 fn test_xnpv() -> Result<(), Error> {
829 let mut book = Book::from("assets/functions.xlsx");
830 book.load(false).unwrap();
831 book.calculate(false, false)?;
832 assert!((7.657 - book.resolve_str_ref("Sheet1!H7")?[[0,0]].as_num()).abs() < 0.01);
833 Ok(())
834 }
835
836 #[test]
837 fn test_yearfrac() -> Result<(), Error> {
838 assert!((0.58055 - evaluate_str("YEARFRAC(DATE(2012, 1, 1), DATE(2012, 7, 30))")?.as_num() < 0.01));
839 Ok(())
840 }
841}