///|
pub fn run_json(request : String) -> String {
  try {
    guard request.length() <= 3000000 else {
      raise Limit("request exceeds 3000000 UTF-16 units")
    }
    let o = obj(@json.parse(request))
    let ballots = arr(required(o, "ballots")).map(v => {
      let b = obj(v)
      {
        weight: num(required(b, "weight")),
        ranking: arr(required(b, "ranking")).map(group => arr(group).map(txt)),
      }
    })
    let e = election(arr(required(o, "candidates")).map(txt), ballots)
    let algorithm = txt(o.get("method").unwrap_or(Json::string("audit")))
    let result = match algorithm {
      "schulze" => @json.to_json(e.schulze())
      "ranked_pairs" => @json.to_json(e.ranked_pairs())
      "irv" =>
        @json.to_json(
          e.irv(arr(o.get("tie_order").unwrap_or(Json::array([]))).map(txt)),
        )
      "irv_possible" =>
        @json.to_json(
          e.irv_possibilities(num(o.get("max_states").unwrap_or(jn(65535)))),
        )
      "audit" =>
        jo([
          ("schulze", @json.to_json(e.schulze())),
          ("ranked_pairs", @json.to_json(e.ranked_pairs())),
        ])
      _ => raise Invalid("unknown method")
    }
    jo([
      ("ok", Json::boolean(true)),
      ("candidates", @json.to_json(e.candidates())),
      ("total_weight", jn(e.total_weight())),
      ("pairwise", @json.to_json(e.pairwise())),
      ("condorcet", @json.to_json(e.condorcet())),
      ("result", result),
    ]).stringify()
  } catch {
    e => {
      let (code, message) = match e {
        TallyError::Invalid(s) => ("invalid", s)
        TallyError::Limit(s) => ("limit", s)
        _ => ("json", "invalid JSON request")
      }
      jo([
        ("ok", Json::boolean(false)),
        ("code", Json::string(code)),
        ("error", Json::string(message)),
      ]).stringify()
    }
  }
}