///|
priv struct Rng {
  mut state : Int
}

///|
fn rng(seed : Int) -> Rng {
  { state: seed }
}

///|
fn Rng::next(self : Rng) -> Int {
  let n = (self.state * 1103515245 + 12345) & 2147483647
  self.state = if n == 0 { 1 } else { n }
  self.state
}

///|
fn Rng::range(self : Rng, hi : Int) -> Int {
  self.next() % hi
}

///|
let segments : Array[String] = [
  "a", "b", "c", "public", "private", "tmp", "x", "z",
]

///|
fn make_case(r : Rng, i : Int) -> String {
  let agent = if i % 3 == 0 { "*" } else { "bot\{i % 7}" }
  let first = segments[r.range(segments.length())]
  let second = segments[r.range(segments.length())]
  let kind = if r.range(2) == 0 { "Allow" } else { "Disallow" }
  "User-agent: \{agent}\n\{kind}: /\{first}/\{second}\nSitemap: https://example.test/s\{i}.xml\n"
}

///|
test "property parse serialize parse is deterministic for 600 cases" {
  let r = rng(9309)
  for i = 0; i < 600; i = i + 1 {
    let input = make_case(r, i)
    let a = unwrap_file(parse_robots(input))
    let b = unwrap_file(parse_robots(serialize_robots(a)))
    assert_str_eq(serialize_robots(a), serialize_robots(b))
  }
}

///|
test "property matcher result is deterministic for 600 cases" {
  let r = rng(20260813)
  for i = 0; i < 600; i = i + 1 {
    let file = unwrap_file(parse_robots(make_case(r, i)))
    let path = "/" +
      segments[r.range(segments.length())] +
      "/" +
      segments[r.range(segments.length())]
    let a = unwrap_decision(evaluate(file, "bot\{i % 7}", path))
    let b = unwrap_decision(evaluate(file, "bot\{i % 7}", path))
    assert_true(a.allowed() == b.allowed())
    assert_int_eq(a.matched_length(), b.matched_length())
  }
}