///|
fn unit_at(s : String, i : Int) -> String {
  s[i:i + 1].to_owned()
}

///|
fn mrz_value(c : UInt16) -> Int {
  if c == '<' {
    return 0
  }
  if c >= '0' && c <= '9' {
    let digits = "0123456789"
    let mut k = 0
    while k < 10 {
      if digits[k] == c {
        return k
      }
      k += 1
    }
  }
  if c >= 'A' && c <= 'Z' {
    let letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    let mut k = 0
    while k < 26 {
      if letters[k] == c {
        return 10 + k
      }
      k += 1
    }
  }
  -1
}

///|
fn is_mrz_char(c : UInt16) -> Bool {
  mrz_value(c) >= 0
}

///|
fn weight_at(i : Int) -> Int {
  let r = i % 3
  if r == 0 {
    7
  } else if r == 1 {
    3
  } else {
    1
  }
}

///|
fn digit_string(n : Int) -> String {
  "0123456789"[n:n + 1].to_owned()
}

///|
/// ICAO 9303 7-3-1 check digit for a field that already uses MRZ characters.
pub fn check_digit(field : String) -> Int? {
  let mut sum = 0
  let mut i = 0
  while i < field.length() {
    let v = mrz_value(field[i])
    if v < 0 {
      return None
    }
    sum = sum + v * weight_at(i)
    i += 1
  }
  Some(sum % 10)
}

///|
pub fn check_digit_char(field : String) -> String? {
  match check_digit(field) {
    None => None
    Some(n) => Some(digit_string(n))
  }
}

///|
fn field_matches_check(field : String, actual : String) -> Bool {
  match check_digit_char(field) {
    None => false
    Some(expected) => expected == actual
  }
}