///|
fn number(s : String) -> Int32 {
  let mut n = 0
  for c in s.iter() {
    n = n * 10 + c.to_int() - 48
  }
  n
}

///|
fn main {
  let args = @env.args()
  let n = number(args[args.length() - 2])
  let fan = number(args[args.length() - 1])
  let store = @store.MemoryStore::new()
  let tuple = fn(o, u) {
    @fga.Tuple::{
      object: o,
      relation: if o.has_prefix("group:") {
        "member"
      } else {
        "viewer"
      },
      user: u,
      condition: None,
    }
  }
  // 20 levels, exact tuple count; deterministic LCG chooses unrelated users.
  for i in 0..<20 {
    let _ = store.write(
      tuple(
        "group:" + i.to_string(),
        if i == 0 {
          "user:alice"
        } else {
          "group:" + (i - 1).to_string() + "#member"
        },
      ),
    )
  }
  let mut seed = 20261101U
  for i in 20.. abort("model")
  }
  let engine = @engine.Engine::new(model, store as &@fga.TupleReader)
  for i in 0..<33 {
    let clock = @bench.monotonic_clock_start()
    let ok = engine.check("document:0", "viewer", "user:alice") catch {
      _ => abort("check")
    }
    let us = @bench.monotonic_clock_end(clock)
    if !ok {
      abort("check result")
    }
    if i >= 3 {
      println("check," + us.to_string())
    }
  }
  for i in 0..<33 {
    let clock = @bench.monotonic_clock_start()
    let objects = engine.list_objects(
      "document",
      "viewer",
      "user:alice",
      Map([]),
      [],
    ) catch {
      _ => abort("list")
    }
    let us = @bench.monotonic_clock_end(clock)
    if objects.length() != fan {
      abort("list result")
    }
    if i >= 3 {
      println("list," + us.to_string())
    }
  }
}