///|
/// Evaluate RangeFromTo
fn eval_range_from_to(
  from_expr : Expr,
  to_expr : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  let from_results = eval_with_env(from_expr, input, env).collect()
  let to_results = eval_with_env(to_expr, input, env).collect()
  match (from_results, to_results) {
    ([], _) | (_, []) => Iter::empty()
    ([Number(from_num, ..), ..], [Number(to_num, ..), ..]) => {
      let from_int = from_num.to_int()
      let to_int = to_num.to_int()
      if to_int <= from_int {
        [].iter()
      } else {
        let count = to_int - from_int
        Array::makei(count, i => Json::number((from_int + i).to_double())).iter()
      }
    }
    _ => raise TypeMismatch("numbers", "non-numbers")
  }
}

///|
/// Evaluate RangeWithStep
fn eval_range_with_step(
  from_expr : Expr,
  to_expr : Expr,
  step_expr : Expr,
  input : Json,
  env : Env,
) -> Iter[Json] raise InterpreterError {
  let from_results = eval_with_env(from_expr, input, env).collect()
  let to_results = eval_with_env(to_expr, input, env).collect()
  let step_results = eval_with_env(step_expr, input, env).collect()
  match (from_results, to_results, step_results) {
    ([], _, _) | (_, [], _) | (_, _, []) => Iter::empty()
    (
      [Number(from_num, ..), ..],
      [Number(to_num, ..), ..],
      [Number(step_num, ..), ..],
    ) => {
      let from_int = from_num.to_int()
      let to_int = to_num.to_int()
      let step_int = step_num.to_int()
      let count = range_step_count(from_int, to_int, step_int)
      if count <= 0 {
        [].iter()
      } else {
        Array::makei(count, i => {
          Json::number((from_int + i * step_int).to_double())
        }).iter()
      }
    }
    _ => raise TypeMismatch("numbers", "non-numbers")
  }
}

///|
fn range_step_count(from_int : Int, to_int : Int, step_int : Int) -> Int {
  if step_int == 0 {
    0
  } else if step_int > 0 {
    if from_int >= to_int {
      0
    } else {
      (to_int - from_int - 1) / step_int + 1
    }
  } else if from_int <= to_int {
    0
  } else {
    let step_abs = -step_int
    (from_int - to_int - 1) / step_abs + 1
  }
}