///|
/// Whitespace accepted around a number by Python's `int()`: `str.isspace()`
/// characters except U+001C..U+001F, which the numeric parser rejects.
fn is_py_int_space(c : Char) -> Bool {
  match c.to_int() {
    0x09..=0x0d
    | 0x20
    | 0x85
    | 0xa0
    | 0x1680
    | 0x2000..=0x200a
    | 0x2028
    | 0x2029
    | 0x202f
    | 0x205f
    | 0x3000 => true
    _ => false
  }
}

///|
/// Code points of the digit zero of every Unicode decimal-digit run
/// (category Nd, Unicode 15.1 as in CPython 3.13); each run is `zero..=zero+9`.
let decimal_zeros : ReadOnlyArray[Int] = [
  0x30, 0x660, 0x6f0, 0x7c0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66, 0xce6,
  0xd66, 0xde6, 0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946, 0x19d0,
  0x1a80, 0x1a90, 0x1b50, 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900, 0xa9d0,
  0xa9f0, 0xaa50, 0xabf0, 0xff10, 0x104a0, 0x10d30, 0x11066, 0x110f0, 0x11136, 0x111d0,
  0x112f0, 0x11450, 0x114d0, 0x11650, 0x116c0, 0x11730, 0x118e0, 0x11950, 0x11c50,
  0x11d50, 0x11da0, 0x11f50, 0x16a60, 0x16ac0, 0x16b50, 0x1d7ce, 0x1d7d8, 0x1d7e2,
  0x1d7ec, 0x1d7f6, 0x1e140, 0x1e2f0, 0x1e4f0, 0x1e950, 0x1fbf0,
]

///|
/// The value of a Unicode decimal digit (Python's `unicodedata.decimal`).
fn py_decimal_value(c : Char) -> Int? {
  let code = c.to_int()
  for zero in decimal_zeros {
    if code >= zero && code <= zero + 9 {
      return Some(code - zero)
    }
  }
  None
}

///|
/// Python's `int(s)` (base 10): surrounding numeric whitespace, an optional
/// sign, Unicode decimal digits, and single underscores between digits.
/// Values beyond `Int` saturate (they can never be valid indices anyway).
fn py_int(s : String) -> Int? {
  let chars = s.iter().to_array()
  let mut start = 0
  let mut end = chars.length()
  while start < end && is_py_int_space(chars[start]) {
    start += 1
  }
  while end > start && is_py_int_space(chars[end - 1]) {
    end -= 1
  }
  let mut negative = false
  if start < end && (chars[start] == '+' || chars[start] == '-') {
    negative = chars[start] == '-'
    start += 1
  }
  if start >= end {
    return None
  }
  let mut value = 0L
  let mut prev_digit = false
  for i in start.. 2147483647L {
        value = 2147483647L
      }
      prev_digit = true
    } else if c == '_' && prev_digit && i + 1 < end {
      prev_digit = false
    } else {
      return None
    }
  }
  if !prev_digit {
    return None
  }
  let v = value.to_int()
  Some(if negative { -v } else { v })
}