///|
/// AWK input coercion accepts a numeric prefix; overflow remains a string attribute.
fn numeric_prefix(text : String) -> (Double, Int, Bool) {
  let cs = text.to_array()
  let mut start = 0
  let mut sign = 1.0
  if cs.get(0) == Some('-') || cs.get(0) == Some('+') {
    if cs[0] == '-' {
      sign = -1.0
    }
    start = 1
  }
  let tail = String::from_array(cs[start:]).to_lower()
  for word in ["infinity", "inf", "nan"] {
    if tail.has_prefix(word) {
      return (
        if word == "nan" {
          0.0 / 0.0
        } else {
          sign * (1.0 / 0.0)
        },
        start + word.length(),
        false,
      )
    }
  }
  if cs.get(start) == Some('0') &&
    (cs.get(start + 1) == Some('x') || cs.get(start + 1) == Some('X')) {
    let mut i = start + 2
    let mut point = false
    let mut digits = 0
    let mut fraction = 0
    let mut kept = 0
    let mut omitted = 0
    let mut sticky = false
    let mut head = @bigint.BigInt::from_int(0)
    while i < cs.length() {
      let c = cs[i]
      if c == '.' && !point {
        point = true
        i += 1
        continue
      }
      if !c.is_ascii_hexdigit() {
        break
      }
      let digit = if c <= '9' {
        c.to_int() - 48
      } else {
        c.to_ascii_lowercase().to_int() - 87
      }
      digits += 1
      if point {
        fraction += 1
      }
      if kept < 16 {
        if kept > 0 || digit > 0 {
          head = (head << 4) + @bigint.BigInt::from_int(digit)
          kept += 1
        }
      } else {
        omitted += 1
        if digit > 0 {
          sticky = true
        }
      }
      i += 1
    }
    if digits > 0 {
      let mut exponent = 0
      if cs.get(i) == Some('p') || cs.get(i) == Some('P') {
        let saved = i
        i += 1
        let mut direction = 1
        if cs.get(i) == Some('-') || cs.get(i) == Some('+') {
          if cs[i] == '-' {
            direction = -1
          }
          i += 1
        }
        let begin = i
        while i < cs.length() && digit(cs[i]) {
          exponent = (exponent * 10 + cs[i].to_int() - 48).min(10000000)
          i += 1
        }
        if i == begin {
          i = saved
        } else {
          exponent *= direction
        }
      }
      if head.is_zero() {
        return (sign * 0.0, i, false)
      }
      let power = exponent - 4 * fraction + 4 * omitted
      let bits = head.bit_length()
      let top = power + bits - 1
      if top > 1023 {
        return (sign * (1.0 / 0.0), i, true)
      }
      if top < -1075 {
        return (sign * 0.0, i, false)
      }
      let drop = (bits - 53).max(-1074 - power).max(0)
      let mut rounded = head >> drop
      if drop > 0 {
        let rest = head - (rounded << drop)
        let half = @bigint.BigInt::from_int(1) << (drop - 1)
        if rest > half ||
          (
            rest == half &&
            (
              sticky ||
              (rounded % @bigint.BigInt::from_int(2)).compare_int(1) == 0
            )
          ) {
          rounded += @bigint.BigInt::from_int(1)
        }
      }
      let mantissa = @strconv.parse_double(rounded.to_string()) catch {
        _ => 0.0
      }
      let n = sign * @math.scalbn(mantissa, power + drop)
      return (n, i, n.is_inf())
    }
  }
  let end = number_end(cs, 0, true)
  if end == 0 {
    return (0.0, 0, false)
  }
  let n = @strconv.parse_double(String::from_array(cs[:end])) catch {
    _ => sign * (1.0 / 0.0)
  }
  (n, end, n.is_inf())
}