(* closures *)
let unique =
  let r = ref 0 in fun s -> incr r; s ^ "__" ^ string_of_int !r


(** Hashtables - Mutable data with destructive updates *)

let hmap = Hashtbl.create 17

let l = Hashtbl.length hmap
let vy = try Hashtbl.find hmap "y" with Not_found -> -1

let () = Hashtbl.add hmap "x" 42
let () = Hashtbl.add hmap "y" 13

let vy = try Hashtbl.find hmap "y" with Not_found -> -1
let l = Hashtbl.length hmap

let () = Hashtbl.add hmap "x" 15
let l = Hashtbl.length hmap
let vx = Hashtbl.find hmap "x"

let () = Hashtbl.remove hmap "x"
let l = Hashtbl.length hmap
let vy = try Hashtbl.find hmap "x" with Not_found -> -1

let () = Hashtbl.replace hmap "x" 94
let l = Hashtbl.length hmap
let vx = Hashtbl.find hmap "x"

(* Using Hasthble as a set *)
let hset = Hashtbl.create 17
let () = Hashtbl.add hset "x" ()

(** Map.Make - Immutable, Persistent *)

module Env = Map.Make(String)
type env = int Env.t

let (mp : env) = Env.empty
let mp1 = Env.add "x" 42 mp
let mp2 = Env.add "y" 13 mp1
let l = Env.cardinal mp
let l1 = Env.cardinal mp1
let l2 = Env.cardinal mp2
let mp2 = Env.add "x" 94 mp2
let mp2 = Env.add "x" 24 mp2
let l2 = Env.cardinal mp2
let vx = Env.find "x" mp2

(** Exercise 1 *)
(* structural vs physical equality *)
let a = ref [4]
let b = ref [4]
let str_eq = a = b
let phys_eq = a == b


(** Exercise 3 *)
let rec my_list_iter (f : 'a -> unit) (l : 'a list) : unit = match l with
  | [] -> ()
  | hd :: tl -> f hd; my_list_iter f tl


(** Exercise 9 *)

let rec my_list_map (f : 'a -> 'b) (l : 'a list) : 'b list =
  match l with
  | [] ->  []
  | x :: q -> f x :: my_list_map f q

let rec my_list_map2 (f : 'a -> 'b -> 'c) (l1 : 'a list) (l2 : 'b list) : 'c list =
  match (l1, l2) with
  | [], [] ->  []
  | a1 :: tl1, b1 :: tl2 -> f a1 b1 :: my_list_map2 f tl1 tl2
  | _, _ -> failwith "invalid arguments my_list_map2"

(** Exercise 11 *)
let rec my_fold_left (f : 'b -> 'a -> 'b) (accu : 'b) (l : 'a list) : 'b =
  match l with
    [] -> accu
  | a::l -> my_fold_left f (f accu a) l

let fact = my_fold_left (fun acc x -> x * acc) 1 [1;2;3;4]

let fact24 =
  let f_aux acc x = x * acc in
  my_fold_left f_aux 1 [1;2;3;4]

(*  my_fold_left f_aux 1 [1;2;3;4] ->
    my_fold_left f_aux (f_aux 1 1) [2;3;4] = my_fold_left f_aux 1 [2;3;4] ->
    my_fold_left f_aux (f_aux 1 2) [3;4]   = my_fold_left f_aux 2 [3;4] ->
    my_fold_left f_aux (f_aux 2 3) [4]     = my_fold_left f_aux 6 [4] ->
    my_fold_left f_aux (f_aux 6 4) []      = my_fold_left f_aux 24 [] ->
    24
 *)