// The Luhn (mod-10) check-digit algorithm, used by payment cards, IMEI
// numbers and several national identifiers.

///|
/// Parse a string of ASCII digits into an array of ints.
/// Returns None if the string is empty or contains a non-digit.
fn digits_of(s : String) -> Array[Int]? {
  let out : Array[Int] = []
  for ch in s {
    if ch < '0' || ch > '9' {
      return None
    }
    out.push(ch.to_int() - '0'.to_int())
  }
  if out.length() == 0 {
    None
  } else {
    Some(out)
  }
}

///|
/// Sum of digits after Luhn weighting. When `double_first` is true the
/// rightmost digit is doubled — that is the layout used when the check digit
/// has not been appended yet.
fn luhn_sum(digits : Array[Int], double_first : Bool) -> Int {
  let mut sum = 0
  let mut double = double_first
  for i = digits.length() - 1; i >= 0; i = i - 1 {
    let mut d = digits[i]
    if double {
      d = d * 2
      if d > 9 {
        d = d - 9
      }
    }
    sum = sum + d
    double = !double
  }
  sum
}

///|
/// Validate a number that already carries its Luhn check digit.
pub fn luhn_valid(s : String) -> Bool {
  match digits_of(s) {
    None => false
    Some(digits) => luhn_sum(digits, false) % 10 == 0
  }
}

///|
/// Compute the Luhn check digit that should be appended to `payload`.
pub fn luhn_check_digit(payload : String) -> Int? {
  match digits_of(payload) {
    None => None
    Some(digits) => Some((10 - luhn_sum(digits, true) % 10) % 10)
  }
}