///|
priv struct R {
  n : @bigint.BigInt
  d : @bigint.BigInt
} derive(Eq)

///|
fn ratio(n : @bigint.BigInt, d : @bigint.BigInt) -> R {
  let (n, d) = if d < 0N { (-n, -d) } else { (n, d) }
  let mut a = if n < 0N { -n } else { n }
  let mut b = d
  while b != 0N {
    let remainder = a % b
    a = b
    b = remainder
  }
  { n: n / a, d: d / a, }
}

///|
fn r(value : Int32) -> R {
  { n: @bigint.BigInt::from_int(value), d: 1N, }
}

///|
impl Add for R with fn add(a, b) {
  ratio(a.n * b.d + b.n * a.d, a.d * b.d)
}

///|
impl Sub for R with fn sub(a, b) {
  ratio(a.n * b.d - b.n * a.d, a.d * b.d)
}

///|
impl Mul for R with fn mul(a, b) {
  ratio(a.n * b.n, a.d * b.d)
}

///|
impl Div for R with fn div(a, b) {
  ratio(a.n * b.d, a.d * b.n)
}

///|
impl Neg for R with fn neg(a) {
  { n: -a.n, d: a.d, }
}

///|
impl Compare for R with fn compare(a, b) {
  (a.n * b.d).compare(b.n * a.d)
}

///|
fn exact(value : R) -> Exact {
  { numerator: value.n.to_string(), denominator: value.d.to_string(), }
}

///|
fn integer_text(value : String, signed~ : Bool) -> Bool {
  let bytes = value.to_array()
  if bytes.length() == 0 || bytes.length() > 700 {
    return false
  }
  let start = if signed && bytes[0] == '-' { 1 } else { 0 }
  if start == bytes.length() {
    return false
  }
  for i = start; i < bytes.length(); i = i + 1 {
    if bytes[i] < '0' || bytes[i] > '9' {
      return false
    }
  }
  true
}

///|
fn parse_exact(value : Exact) -> Result[R, String] {
  if !integer_text(value.numerator, signed=true) ||
    !integer_text(value.denominator, signed=false) {
    return Err(
      "exact values require integer numerator and positive denominator",
    )
  }
  let d = @bigint.BigInt::from_string(value.denominator)
  if d == 0N {
    return Err("exact denominator cannot be zero")
  }
  Ok(ratio(@bigint.BigInt::from_string(value.numerator), d))
}