///|
fn eval_stdev(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => stdev_values(false, call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_stdeva(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => stdev_values(true, call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_stdevp(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] =>
      match variance_values(call.values, false, false) {
        Number(variance) => {
          let result = @math.pow(variance, 0.5)
          Number(round_significant_digits(result, 15))
        }
        value => value
      }
    [] => Error(formula_error_value)
  }
}

///|
fn eval_stdevpa(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] =>
      match variance_values(call.values, false, true) {
        Number(variance) => {
          let result = @math.pow(variance, 0.5)
          Number(round_significant_digits(result, 15))
        }
        value => value
      }
    [] => Error(formula_error_value)
  }
}

///|
fn eval_var(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => variance_values(call.values, true, false)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_vara(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => variance_values(call.values, true, true)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_varp(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => variance_values(call.values, false, false)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_varpa(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => variance_values(call.values, false, true)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_avedev(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => avedev_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_devsq(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => devsq_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_geomean(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => geomean_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_harmean(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => harmean_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_kurt(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => kurt_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_skew(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => skew_values("SKEW", call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_skew_p(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => skew_values("SKEW.P", call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_standardize(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _, _] => standardize_values(call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_large(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => kth_values("LARGE", v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_small(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => kth_values("SMALL", v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_mode(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => mode_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_mode_sngl(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => mode_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_mode_mult(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, ..] => mode_mult_values(call.values)
    [] => Error(formula_error_value)
  }
}

///|
fn eval_percentile(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(k) =>
          if k < 0.0 || k > 1.0 {
            Error(formula_error_na)
          } else {
            percentile_values(v0, k, false, false)
          }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_percentile_inc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(k) =>
          if k < 0.0 || k > 1.0 {
            Error(formula_error_na)
          } else {
            percentile_values(v0, k, false, false)
          }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_percentile_exc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(k) =>
          if k <= 0.0 || k >= 1.0 {
            Error(formula_error_num)
          } else {
            percentile_values(v0, k, true, true)
          }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_percentrank(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] | [v0, v1, _] =>
      match value_as_number(v1) {
        Ok(x) => {
          let significance = if call.values is [_, _, v2] {
            match value_as_number(v2) {
              Ok(num) => num
              Err(err) => return err
            }
          } else {
            3.0
          }
          percentrank_values("PERCENTRANK", v0, x, significance)
        }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_percentrank_inc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] | [v0, v1, _] =>
      match value_as_number(v1) {
        Ok(x) => {
          let significance = if call.values is [_, _, v2] {
            match value_as_number(v2) {
              Ok(num) => num
              Err(err) => return err
            }
          } else {
            3.0
          }
          percentrank_values("PERCENTRANK.INC", v0, x, significance)
        }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_percentrank_exc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] | [v0, v1, _] =>
      match value_as_number(v1) {
        Ok(x) => {
          let significance = if call.values is [_, _, v2] {
            match value_as_number(v2) {
              Ok(num) => num
              Err(err) => return err
            }
          } else {
            3.0
          }
          percentrank_values("PERCENTRANK.EXC", v0, x, significance)
        }
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_quartile(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(quart) => quartile_values("QUARTILE", v0, quart, false)
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_quartile_inc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(quart) => quartile_values("QUARTILE.INC", v0, quart, false)
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_quartile_exc(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match value_as_number(v1) {
        Ok(quart) => quartile_values("QUARTILE.EXC", v0, quart, true)
        Err(err) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_rank(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] | [v0, v1, _] => {
      let order = if call.values is [_, _, v2] {
        match value_as_number(v2) {
          Ok(num) => Some(num)
          Err(err) => return err
        }
      } else {
        None
      }
      rank_values(v0, v1, order)
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_correl(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => correl_values(v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_covar(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => covar_values("COVAR", v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_covariance_p(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => covar_values("COVARIANCE.P", v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_covariance_s(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] => covar_values("COVARIANCE.S", v0, v1)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_pearson(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _] => pearson_product_values("PEARSON", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_rsq(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _] => pearson_product_values("RSQ", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_slope(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _] => pearson_product_values("SLOPE", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_intercept(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _] => pearson_product_values("INTERCEPT", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_forecast(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _, _] => pearson_product_values("FORECAST", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_forecast_linear(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _, _] => pearson_product_values("FORECAST.LINEAR", call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_growth(call : EvalCall) -> FormulaValue raise XlsxError {
  trend_growth_values(
    call.workbook,
    call.sheet_name,
    "GROWTH",
    call.args,
    call.values,
    call.ctx,
  )
}

///|
fn eval_trend(call : EvalCall) -> FormulaValue raise XlsxError {
  trend_growth_values(
    call.workbook,
    call.sheet_name,
    "TREND",
    call.args,
    call.values,
    call.ctx,
  )
}

///|
fn eval_fisher(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0] => fisher_value(v0)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_fisherinv(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0] => fisherinv_value(v0)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_steyx(call : EvalCall) -> FormulaValue {
  match call.values {
    [_, _] => steyx_values(call.values)
    _ => Error(formula_error_value)
  }
}

///|
fn eval_countblank(call : EvalCall) -> FormulaValue raise XlsxError {
  match call.args {
    [a0] => {
      let range_values = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a0,
        call.ctx,
      )
      countblank_values(range_values)
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_countif(call : EvalCall) -> FormulaValue raise XlsxError {
  match call.args {
    [a0, _] => {
      let range_values = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a0,
        call.ctx,
      )
      countif_values(range_values, call.values[1])
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_countifs(call : EvalCall) -> FormulaValue raise XlsxError {
  if call.args.length() >= 2 {
    if call.args.length() % 2 != 0 {
      Error(formula_error_na)
    } else {
      let ranges : Array[RangeValues] = []
      let criterias : Array[FormulaCriteria] = []
      for i in 0..<(call.args.length() / 2) {
        let range_values = eval_range_expr(
          call.workbook,
          call.sheet_name,
          call.args[i * 2],
          call.ctx,
        )
        ranges.push(range_values)
        criterias.push(parse_formula_criteria(call.values[i * 2 + 1]))
      }
      let matches = ifs_match(ranges, criterias)
      Number(Double::from_int(matches.length()))
    }
  } else {
    Error(formula_error_value)
  }
}

///|
fn eval_daverage(call : EvalCall) -> FormulaValue raise XlsxError {
  match call.args {
    [a0, _, a2] => {
      let database = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a0,
        call.ctx,
      )
      let criteria = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a2,
        call.ctx,
      )
      database_values(call.name, database, call.values[1], criteria)
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_dcount(call : EvalCall) -> FormulaValue raise XlsxError {
  if call.args.length() >= 2 && call.args.length() <= 3 {
    let database = eval_range_expr(
      call.workbook,
      call.sheet_name,
      call.args[0],
      call.ctx,
    )
    let field = if call.args.length() == 3 {
      match call.args[1] {
        FuncCall(name, _) if name == "" => Empty
        _ => call.values[1]
      }
    } else {
      Empty
    }
    let criteria_idx = if call.args.length() == 3 { 2 } else { 1 }
    let criteria = eval_range_expr(
      call.workbook,
      call.sheet_name,
      call.args[criteria_idx],
      call.ctx,
    )
    dcount_values(call.name, database, field, criteria)
  } else {
    Error(formula_error_value)
  }
}

///|
fn eval_dget(call : EvalCall) -> FormulaValue raise XlsxError {
  match call.args {
    [a0, _, a2] => {
      let database = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a0,
        call.ctx,
      )
      let criteria = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a2,
        call.ctx,
      )
      dget_values(database, call.values[1], criteria)
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_averageif(call : EvalCall) -> FormulaValue raise XlsxError {
  match call.args {
    [a0, _] | [a0, _, _] => {
      let range_values = eval_range_expr(
        call.workbook,
        call.sheet_name,
        a0,
        call.ctx,
      )
      let average_range = if call.args is [_, _, a2] {
        Some(eval_range_expr(call.workbook, call.sheet_name, a2, call.ctx))
      } else {
        None
      }
      averageif_values(range_values, call.values[1], average_range)
    }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_averageifs(call : EvalCall) -> FormulaValue raise XlsxError {
  if call.args.length() >= 3 {
    if call.args.length() % 2 != 1 {
      Error(formula_error_na)
    } else {
      let average_range = eval_range_expr(
        call.workbook,
        call.sheet_name,
        call.args[0],
        call.ctx,
      )
      let ranges : Array[RangeValues] = []
      let criterias : Array[FormulaCriteria] = []
      for i in 0..<((call.args.length() - 1) / 2) {
        let offset = 1 + i * 2
        let range_values = eval_range_expr(
          call.workbook,
          call.sheet_name,
          call.args[offset],
          call.ctx,
        )
        ranges.push(range_values)
        criterias.push(parse_formula_criteria(call.values[offset + 1]))
      }
      let matches = ifs_match(ranges, criterias)
      let mut sum = 0.0
      let mut count = 0
      for cell in matches {
        match average_range.get(cell.row, cell.col) {
          Some(value) =>
            match value_as_number_opt(value) {
              Some(num) => {
                sum = sum + num
                count = count + 1
              }
              None => ()
            }
          None => return Error(formula_error_value)
        }
      }
      if count == 0 {
        Error(formula_error_div)
      } else {
        Number(sum / Double::from_int(count))
      }
    }
  } else {
    Error(formula_error_value)
  }
}

///|
fn eval_maxifs(call : EvalCall) -> FormulaValue raise XlsxError {
  if call.args.length() >= 3 {
    if call.args.length() % 2 != 1 {
      Error(formula_error_na)
    } else {
      let max_range = eval_range_expr(
        call.workbook,
        call.sheet_name,
        call.args[0],
        call.ctx,
      )
      let ranges : Array[RangeValues] = []
      let criterias : Array[FormulaCriteria] = []
      for i in 0..<((call.args.length() - 1) / 2) {
        let offset = 1 + i * 2
        let range_values = eval_range_expr(
          call.workbook,
          call.sheet_name,
          call.args[offset],
          call.ctx,
        )
        ranges.push(range_values)
        criterias.push(parse_formula_criteria(call.values[offset + 1]))
      }
      let matches = ifs_match(ranges, criterias)
      maxifs_values(max_range, matches)
    }
  } else {
    Error(formula_error_value)
  }
}

///|
fn eval_minifs(call : EvalCall) -> FormulaValue raise XlsxError {
  if call.args.length() >= 3 {
    if call.args.length() % 2 != 1 {
      Error(formula_error_na)
    } else {
      let min_range = eval_range_expr(
        call.workbook,
        call.sheet_name,
        call.args[0],
        call.ctx,
      )
      let ranges : Array[RangeValues] = []
      let criterias : Array[FormulaCriteria] = []
      for i in 0..<((call.args.length() - 1) / 2) {
        let offset = 1 + i * 2
        let range_values = eval_range_expr(
          call.workbook,
          call.sheet_name,
          call.args[offset],
          call.ctx,
        )
        ranges.push(range_values)
        criterias.push(parse_formula_criteria(call.values[offset + 1]))
      }
      let matches = ifs_match(ranges, criterias)
      minifs_values(min_range, matches)
    }
  } else {
    Error(formula_error_value)
  }
}

///|
fn eval_permut(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match (value_as_number(v0), value_as_number(v1)) {
        (Ok(num), Ok(chosen)) =>
          if num < chosen {
            Error(formula_error_na)
          } else {
            let result = factorial_double(num) / factorial_double(num - chosen)
            Number(round_half_away_from_zero(result))
          }
        (Err(err), _) => err
        (_, Err(err)) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_permutationa(call : EvalCall) -> FormulaValue {
  match call.values {
    [v0, v1] =>
      match (value_as_number(v0), value_as_number(v1)) {
        (Ok(num), Ok(chosen)) => {
          let number = Double::floor(num)
          let number_chosen = Double::floor(chosen)
          if number < 0.0 || number_chosen < 0.0 {
            Error(formula_error_na)
          } else {
            Number(@math.pow(number, number_chosen))
          }
        }
        (Err(err), _) => err
        (_, Err(err)) => err
      }
    _ => Error(formula_error_value)
  }
}

///|
fn eval_weibulldotdist(call : EvalCall) -> FormulaValue {
  weibull_value(call.values)
}

///|
fn eval_confidencedotnorm(call : EvalCall) -> FormulaValue {
  confidence_values(call.values)
}

///|
fn eval_confidencedott(call : EvalCall) -> FormulaValue {
  confidence_t_values(call.values)
}

///|
fn eval_normdotsdotdist(call : EvalCall) -> FormulaValue {
  norm_s_dist_values(call.values)
}

///|
fn eval_normdotsdotinv(call : EvalCall) -> FormulaValue {
  norm_s_inv_values(call.values)
}

///|
fn eval_lognormdotdist(call : EvalCall) -> FormulaValue {
  lognorm_dist_values(call.values)
}

///|
fn eval_lognormdotinv(call : EvalCall) -> FormulaValue {
  loginv_values(call.values)
}

///|
fn eval_gammadotdist(call : EvalCall) -> FormulaValue {
  gamma_dist_values(call.values)
}

///|
fn eval_gammalndotprecise(call : EvalCall) -> FormulaValue {
  gammaln_precise_values(call.values)
}

///|
fn eval_expondotdist(call : EvalCall) -> FormulaValue {
  expon_dist_values(call.values)
}

///|
fn eval_poissondotdist(call : EvalCall) -> FormulaValue {
  poisson_values(call.values)
}

///|
fn eval_binomdotdist(call : EvalCall) -> FormulaValue {
  binomdist_values(call.values)
}

///|
fn eval_binomdotdistdotrange(call : EvalCall) -> FormulaValue {
  binom_dist_range_values(call.values)
}

///|
fn eval_hypgeomdotdist(call : EvalCall) -> FormulaValue {
  hypgeom_dist_values(call.values)
}

///|
fn eval_negbinomdotdist(call : EvalCall) -> FormulaValue {
  negbinom_dist_values(call.values)
}

///|
fn eval_chisqdotdist(call : EvalCall) -> FormulaValue {
  chisq_dist_values(call.values)
}

///|
fn eval_chisqdotdistdotrt(call : EvalCall) -> FormulaValue {
  chidist_values(call.values)
}

///|
fn eval_chisqdotinvdotrt(call : EvalCall) -> FormulaValue {
  chiinv_values(call.values)
}

///|
fn eval_chisqdottest(call : EvalCall) -> FormulaValue raise XlsxError {
  chitest_values(
    call.workbook,
    call.sheet_name,
    call.args,
    call.values,
    call.ctx,
  )
}

///|
fn eval_fdottest(call : EvalCall) -> FormulaValue raise XlsxError {
  ftest_values(call.workbook, call.sheet_name, call.args, call.values, call.ctx)
}