///|
pub struct CodeRange {
  start : String
  end : String
  start_rank : Int
  end_rank : Int
} derive(Debug, Eq, ToJson)

///|
pub struct RangePartition {
  inside : Array[String]
  outside : Array[String]
  invalid : Array[String]
} derive(Debug, Eq, ToJson)

///|
pub fn CodeRange::new(
  start : String,
  end : String,
) -> CodeRange raise IcdParseError {
  let first = parse_icd10(start)
  let last = parse_icd10(end)
  if first.numeric > last.numeric {
    raise IcdParseError::InvalidCode(start + ".." + end)
  }
  {
    start: first.canonical(),
    end: last.canonical(),
    start_rank: first.numeric,
    end_rank: last.numeric,
  }
}

///|
pub fn CodeRange::contains(self : CodeRange, code : String) -> Bool {
  try parse_icd10(code) catch {
    _ => false
  } noraise {
    parsed =>
      parsed.numeric >= self.start_rank && parsed.numeric <= self.end_rank
  }
}

///|
pub fn CodeRange::overlaps(self : CodeRange, other : CodeRange) -> Bool {
  self.start_rank <= other.end_rank && other.start_rank <= self.end_rank
}

///|
pub fn CodeRange::partition(
  self : CodeRange,
  codes : Array[String],
) -> RangePartition {
  let inside = []
  let outside = []
  let invalid = []
  for raw in codes {
    try parse_icd10(raw) catch {
      _ => invalid.push(raw)
    } noraise {
      code =>
        if self.contains(raw) {
          inside.push(code.canonical())
        } else {
          outside.push(code.canonical())
        }
    }
  }
  { inside, outside, invalid }
}