(* Time-stamp: <modified the 14/06/2019 (at 13:26) by Erwan Jahier> *)

(* This is algo 3.1 in the book *)

open Algo

let vars = ["c",It]
let k=3

let (init_vars: neighbor list -> local_env) =
  fun _nl ->
    set empty_env "c" (I (Random.int k))

 
let verbose = true

let (neigbhors_values : neighbor list -> Algo.value list) =
  fun nl ->
    List.map (fun n -> get n.lenv "c") nl 

let (clash : Algo.value -> neighbor list -> bool) = fun v nl -> 
  let vnl = neigbhors_values nl in
  let inl = List.map (fun n -> n.pid()) nl in
  let res = List.mem v vnl in
  if verbose then (
    Printf.printf "%s %s in [%s] (%s)\n" (value_to_string v)
      (if res then "" else "not")
      (String.concat "," (List.map value_to_string vnl))
      (String.concat "," (inl));
    flush stdout
  );
  res

let (free : neighbor list -> Algo.value list) = fun nl ->
  let clash_list = List.sort_uniq compare (neigbhors_values nl) in
  let rec aux free clash i =
    if i > k then free else
      (match clash with
       | x::tail ->
         if x = I i then aux free tail (i+1) else aux ((I i)::free) clash (i+1)
       | [] -> aux ((I i)::free) clash (i+1)
      )
  in
  let res = aux [] clash_list 0 in
  List.rev res

let (enable_f:neighbor list -> local_env -> action list) =
  fun nl e ->
    if (clash (get e "c") nl) then ["conflict"] else []

let (step_f : neighbor list -> local_env -> action -> local_env) =
  fun nl e a ->
    let f = free nl in
    if f = [] then e else
      match a with
      | _ -> set e "c" (List.hd f) 

let () =
  let algo_id = "p" in
  Algo.reg_actions   algo_id ["conflict"];
  Algo.reg_vars      algo_id vars;
  Algo.reg_init_vars algo_id init_vars; 
  Algo.reg_enable    algo_id enable_f;
  Algo.reg_step      algo_id step_f;
  ()