open CCRandom

let uniformity_test ?(size_hint = 10) k rng st =
  let histogram = Hashtbl.create size_hint in
  let add x =
    let n = try Hashtbl.find histogram x with Not_found -> 0 in
    Hashtbl.replace histogram x (n + 1)
  in
  let () =
    for _i = 0 to k - 1 do
      add (rng st)
    done
  in
  let cardinal = float_of_int (Hashtbl.length histogram) in
  let kf = float_of_int k in
  (* average number of points assuming an uniform distribution *)
  let average = kf /. cardinal in
  (* The number of points is a sum of random variables with binomial distribution *)
  let p = 1. /. cardinal in
  (* The variance of a binomial distribution with average p is *)
  let variance = p *. (1. -. p) in
  (* Central limit theorem: a confidence interval of 4σ provides a false positive rate
     of 0.00634% *)
  let confidence = 4. in
  let std = confidence *. sqrt (kf *. variance) in
  let predicate _key n acc =
    let ( < ) (a : float) b = Stdlib.( < ) a b in
    acc && abs_float (average -. float_of_int n) < std
  in
  Hashtbl.fold predicate histogram true

let () =
  let st = Random.State.make_self_init () in
  let ok = run ~st (uniformity_test 500_000 (split_list 10 ~len:3)) in
  if not ok then failwith "uniformity check failed"
