///|
/// Evaluate Combinations
fn eval_combinations(input : Json) -> Iter[Json] raise InterpreterError {
  match input {
    Array(arr) =>
      match collect_arrays(arr) {
        None => Iter::empty()
        Some(arrays) =>
          match arrays {
            [] => Iter::empty()
            _ => cartesian_product(arrays).iter().map(Json::array)
          }
      }
    _ => raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
}

///|
/// Evaluate Transpose
fn eval_transpose(input : Json) -> Iter[Json] raise InterpreterError {
  match input {
    Array(arr) => {
      let max_len = max_row_len(arr)
      let rows = build_columns(arr, max_len)
      Iter::singleton(Json::array(rows.map(Json::array)))
    }
    _ => raise TypeMismatch("array", @ast_internal.json_type_name(input))
  }
}

///|
fn collect_arrays(items : ArrayView[Json]) -> Array[Array[Json]]? {
  let arrays : Array[Array[Json]] = []
  for item in items {
    match item {
      Array(inner) => arrays.push(inner)
      _ => return None
    }
  }
  Some(arrays)
}

///|
fn cartesian_product(arrays : ArrayView[Array[Json]]) -> Array[Array[Json]] {
  let mut result : Array[Array[Json]] = [[]]
  for arr in arrays {
    let next : Array[Array[Json]] = []
    for prefix in result {
      for item in arr {
        let combined = prefix.copy()
        combined.push(item)
        next.push(combined)
      }
    }
    result = next
  }
  result
}

///|
fn max_row_len(rows : ArrayView[Json]) -> Int {
  rows.fold(init=0, fn(max_len, row) {
    match row {
      Array(inner) => @cmp.maximum(max_len, inner.length())
      _ => max_len
    }
  })
}

///|
fn build_columns(rows : ArrayView[Json], max_len : Int) -> Array[Array[Json]] {
  let columns : Array[Array[Json]] = []
  for col in 0.. if col < inner.length() { column.push(inner[col]) }
        _ => ()
      }
    }
    columns.push(column)
  }
  columns
}