///|
fn range_item_count(config : RomanRangeConfig) -> Result[Int, RomanRangeError] {
  if config.max_items < 1 {
    return Err(InvalidRangeItemLimit(config.max_items))
  }
  if config.step == 0 {
    return Err(ZeroRangeStep)
  }
  if (config.start < config.end && config.step < 0) ||
    (config.start > config.end && config.step > 0) {
    return Err(RangeDirectionMismatch(config.start, config.end, config.step))
  }
  let distance = if config.end >= config.start {
    config.end - config.start
  } else {
    config.start - config.end
  }
  let step_size = if config.step < 0 { -config.step } else { config.step }
  let count = distance / step_size + 1
  if count > config.max_items {
    Err(RangeItemLimitExceeded(count, config.max_items))
  } else {
    Ok(count)
  }
}

///|
/// Format an inclusive sequence after validating direction and output size.
pub fn format_roman_range(
  config : RomanRangeConfig,
) -> Result[Array[RomanRangeEntry], RomanRangeError] {
  let count = match range_item_count(config) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let entries : Array[RomanRangeEntry] = []
  let mut value = config.start
  for index = 0; index < count; index = index + 1 {
    let text = match format_with_config(value, config.format_config) {
      Ok(formatted) => formatted
      Err(error) => return Err(RangeValueFormatFailed(value, error))
    }
    entries.push({ value, text })
    value = value + config.step
  }
  Ok(entries)
}