type ('a, 'b) t = ('a, 'b list ref) Hashtbl.t

let create = Hashtbl.create
let clear = Hashtbl.clear

let add tbl key data =
  let r =
    try Hashtbl.find tbl key
    with Not_found ->
      let r = ref [] in
      Hashtbl.add tbl key r;
      r in
  r := data :: !r

let copy tbl =
  let tbl2 = Hashtbl.copy tbl in
  Hashtbl.iter (fun key r -> Hashtbl.replace tbl2 key (ref !r)) tbl;
  tbl2

let find tbl key =
  List.hd !(Hashtbl.find tbl key)

let find_all tbl key =
  !(Hashtbl.find tbl key)

let mem = Hashtbl.mem

let remove tbl key =
  try
    let r = Hashtbl.find tbl key in
    match !r with
        [data] -> Hashtbl.remove tbl key
      | hd :: tl -> r := tl
      | [] -> invalid_arg "remove"
  with Not_found -> ()

let remove_all = Hashtbl.remove

let replace tbl key data =
  try
    let r = Hashtbl.find tbl key in
    r := data :: (List.tl !r)
  with 
      Not_found -> Hashtbl.add tbl key (ref [data])

let replace_all tbl key l =
  try
    let r = Hashtbl.find tbl key in
    r := l
  with 
      Not_found -> Hashtbl.add tbl key (ref l)

let iter f tbl =
  Hashtbl.iter (fun key r -> f key (List.hd !r)) tbl

let iter_all f tbl =
  Hashtbl.iter (fun key r -> f key !r) tbl

let fold f tbl init =
  Hashtbl.fold (fun key r accu -> f key (List.hd !r) accu) tbl init

let fold_all f tbl init =
  Hashtbl.fold 
    (fun key r accu -> f key !r accu)
    tbl init

let list_keys tbl =
  fold (fun key _ accu -> key :: accu) tbl []

let list_values tbl =
  fold (fun _ data accu -> data :: accu) tbl []

let list_all_values tbl =
  fold_all (fun _ l accu -> l :: accu) tbl []

let list tbl =
  fold (fun key data accu -> (key, data) :: accu) tbl []

let list_all tbl =
  fold_all (fun key l accu -> (key, l) :: accu) tbl []

let of_list n l =
  let tbl = create n in
  List.iter (fun (key, data) -> add tbl key data) l;
  tbl

let of_keys n l =
  let tbl = create n in
  List.iter (fun key -> replace tbl key ()) l;
  tbl