open Common

(*****************************************************************************)
(* coded for LFS *)

(* todo: could take an incr/decr func in param, to make it generic
 * opti: remember the min/max (optimisation to have intersect biggest x -> x)
 * opti: avoid all those rev, and avoid the intervise
 * (but yes the algo are then more complex :)
 * opti: balanced set intervalle
*)

(*****************************************************************************)
type seti = elt list (* last elements is in first pos, ordered reverse *)
  and elt = Exact of int | Interv of int * int

(* invariant= ordered list, no incoherent interv (one elem or zero elem),
 * merged (intervalle are separated) *)
let invariant xs =
  let rec aux min xs =
    xs +> List.fold_left (fun min e ->
      match e with
      | Exact i ->
          if i <= min then pr2 (sprintf "i = %d, min = %d" i min);
          (* todo: should be even stronger, shoud be i > min+1 *)
          assert (i > min);
          i
      | Interv (i,j) ->
          assert (i > min);
          assert (j > i);
          j
    ) min
  in
  ignore(aux min_int (List.rev xs));
  ()

let string_of_seti xs =
  "[" ^
    join "," (xs +> List.rev +> map (function
    | (Exact i) -> string_of_int i
    | (Interv (i,j)) -> Printf.sprintf "%d - %d" i j)) ^
    "]"

(*****************************************************************************)
let empty = []

let pack newi j = function
  | [] -> [Interv (newi,j)]
  | (Exact z)::xs ->
      (Interv (newi, j))::(if newi =|= z then xs else (Exact z)::xs)
  | (Interv (i', j'))::xs ->
      if newi =|= j'
      then (Interv (i', j))::xs  (* merge *)
      else (Interv (newi, j))::(Interv (i', j'))::xs


(* the only possible merges are when x = i-1, otherwise, the job is done before *)
let rec (add2: int -> seti -> seti) = fun x -> function
  | [] -> [Exact x]
  | (Exact i)::xs when x > i+1 -> (Exact x)::(Exact i)::xs
  | (Interv (i,j)::xs) when x > j+1 -> (Exact x)::(Interv (i,j))::xs
  | (Interv (i,j)::xs) when x =|= j+1 -> (Interv (i,x))::xs
  | (Exact i)::xs when x =|= i+1 -> (Interv (i,x))::xs

  | (Exact i)::xs when i =|= x   -> (Exact i)::xs
  | (Interv (i,j)::xs) when x <= j && x >= i -> (Interv (i,j))::xs
  | other ->
(*         let _ = log "Cache miss" in *)
      let _ = count2 () in
      (match other with
      |       (Exact i)::xs when x =|= i-1 -> pack x i xs
      |       (Exact i)::xs when x < i-1 -> (Exact i)::add x xs

      |       (Interv (i,j)::xs) when x =|= i-1 -> pack x j xs
      |       (Interv (i,j)::xs) when x < i-1 -> (Interv (i,j))::add x xs
      |       _ -> raise (Impossible 6)
      )
and add x y = let _ = count5 () in add2 x y


let rec tolist2 = function
  | [] -> []
  | (Exact i)::xs -> i::tolist2 xs
  | (Interv (i,j))::xs -> enum i j @ tolist2 xs
let rec tolist xs = List.rev (tolist2 xs)

let rec fromlist = function xs -> List.fold_left (fun a e -> add e a) empty xs

let intervise = function
  | Exact x -> Interv (x,x)
  | y -> y
let exactize = function
  | Interv (i,j) when i =|= j -> Exact i
  | y -> y
let exactize2 x y = if x =|= y then Exact x else Interv (x,y)


let rec (remove: int -> seti -> seti) = fun x xs ->
  match xs with
  | [] -> [] (*  pb, not in  *)
  | (Exact z)::zs ->
      (match x <=> z with
      | Equal -> zs
      | Sup -> xs  (*  pb, not in *)
      | Inf -> (Exact z)::remove x zs
      )
  | (Interv (i,j)::zs) ->
      if x > j then xs (*  pb not in *)
      else
        if x >= i && x <= j then
          (
            let _ = assert (j > i) in (* otherwise can lead to construct seti such as [7,6] when removing 6 from [6,6] *)
            match () with
            | _ when x =|= i -> [exactize2 (i+1) j]
            | _ when x =|= j -> [exactize2 i (j-1)]
            | _ -> [exactize2 (x+1) j; exactize2 i (x-1)]
          ) @ zs
      else (Interv (i,j))::remove x zs

(* let _ = Example (remove 635 [Interv (3, 635)] = [Interv (3, 634)]) *)
(* let _ = Example (remove 2 [Interv (6, 7); Interv(1,4)] = [Interv (6,7); Interv (3,4); Exact 1]) *)
(* let _ = Example (remove 6 [Interv (6, 7); Interv(1,4)] = [Exact 7; Interv (1,4)]) *)
(* let _ = Example (remove 1 [Interv (6, 7); Interv(1,2)] = [Interv (6,7); Exact 2]) *)
(* let _ = Example (remove 3 [Interv (1, 7)] = [Interv (4,7); Interv (1,2)]) *)
let _ = assert_equal (remove 3 [Interv (1, 7)])  [Interv (4,7); Interv (1,2)]
let _ = assert_equal (remove 4 [Interv (3, 4)])  [Exact (3);]
(* let _ = example (try (ignore(remove 6 [Interv (6, 6)] = []); false) with _ -> true)   *)


let rec mem e = function
  | [] -> false
  | (Exact x)::xs ->
      (match e <=> x with
      | Equal -> true
      | Sup -> false
      | Inf -> mem e xs
      )
  | (Interv (i,j)::xs) ->
      if e > j then false
      else
        if e >= i && e <= j then true
      else mem e xs

let iter f xs = xs +> List.iter
  (function
  | Exact i -> f i
  | Interv (i, j) -> for k = i to j do f k done
  )

let is_empty xs = xs =*= []
let choose = function
  | [] -> failwith "not supposed to be called with empty set"
  | (Exact i)::xs -> i
  | (Interv (i,j))::xs -> i

let elements xs = tolist xs
let rec cardinal = function
  | [] -> 0
  | (Exact _)::xs -> 1+cardinal xs
  | (Interv (i,j)::xs) -> (j-i) +1 + cardinal xs

(*****************************************************************************)
(*  TODO: could return corresponding osetb ? *)
let rec inter xs ys =
  let rec aux = fun xs ys ->
    match (xs, ys) with
    | (_, []) -> []
    | ([],_)  -> []
    | (x::xs, y::ys) ->
        (match (x, y) with
        | (Interv (i1, j1), Interv (i2, j2)) ->
            (match i1 <=> i2 with
            | Equal ->
                (match j1 <=> j2 with
                | Equal -> (Interv (i1,j1))::aux xs ys
                    (*  [  ] *)
                    (*  [  ] *)
                | Inf -> (Interv (i1, j1))::aux xs      ((Interv (j1+1, j2))::ys)
                    (*  [  ] [      TODO? could have [ so cant englobe right now, but would be better *)
                    (*  [      ] *)
                | Sup -> (Interv (i1, j2))::aux ((Interv (j2+1, j1))::xs) ys
                    (*  [    ] *)
                    (*  [ ] [       same *)
                )
            | Inf ->
                if j1 < i2 then aux xs (y::ys) (* need order ? *)
                  (*  [    ] *)
                  (*         [ ] *)
                else
                  (match j1 <=> j2 with
                  | Equal -> (Interv (i2, j1))::aux xs ys
                      (*  [    ] *)
                      (*     [ ] *)
                  | Inf ->   (Interv (i2, j1))::aux xs ((Interv (j1+1, j2))::ys)
                      (*  [    ] [    same *)
                      (*     [     ]   *)
                  | Sup ->   (Interv (i2, j2))::aux ((Interv (j2+1, j1))::xs) ys
                      (*  [       ] *)
                      (*     [ ] [  same *)
                  )
            | Sup -> aux (y::ys) (x::xs) (* can cos commutative *)
            )
        | _ -> raise (Impossible 7) (* intervise *)
        )
  in
  (* TODO avoid the rev rev, but aux good ? need order ?  *)
  List.rev_map exactize (aux (List.rev_map intervise xs) (List.rev_map intervise ys))

let union xs ys =
  let rec aux = fun xs ys ->
    match (xs, ys) with
    | (vs, []) -> vs
    | ([],vs)  -> vs
    | (x::xs, y::ys) ->
        (match (x, y) with
        | (Interv (i1, j1), Interv (i2, j2)) ->
            (match i1 <=> i2 with
            | Equal ->
                (match j1 <=> j2 with
                | Equal -> (Interv (i1,j1))::aux xs ys
                    (*  [  ] *)
                    (*  [  ] *)
                | Inf -> (Interv (i1, j1))::aux xs      ((Interv (j1+1, j2))::ys)
                    (*  [  ] [      TODO? could have [ so cant englobe right now, but would be better *)
                    (*  [      ] *)
                | Sup -> (Interv (i1, j2))::aux ((Interv (j2+1, j1))::xs) ys
                    (*  [    ] *)
                    (*  [ ] [       same *)
                )
            | Inf ->
                if j1 < i2 then Interv (i1, j1):: aux xs (y::ys)
                  (*  [    ] *)
                  (*         [ ] *)
                else
                  (match j1 <=> j2 with
                  | Equal -> (Interv (i1, j1))::aux xs ys
                      (*  [    ] *)
                      (*     [ ] *)
                  | Inf ->   (Interv (i1, j1))::aux xs ((Interv (j1+1, j2))::ys)
                      (*  [    ] [    same *)
                      (*     [     ]   *)
                  | Sup ->   (Interv (i1, j2))::aux ((Interv (j2+1, j1))::xs) ys
                      (*  [       ] *)
                      (*     [ ] [  same *)
                  )
            | Sup -> aux (y::ys) (x::xs) (* can cos commutative *)
            )
        | _ -> raise (Impossible 8) (* intervise *)
        )
  in
(*     union_set (tolist xs) (tolist ys) +> fromlist *)
  List.rev_map exactize (aux (List.rev_map intervise xs) (List.rev_map intervise ys))

(* bug/feature:   discovered by vlad rusu, my invariant for intervalle is
 * not very strong, should return (Interv (1,4)) *)
(* let _ = Example (union [Interv (1, 4)] [Interv (1, 3)] = ([Exact 4; Interv (1,3)])) *)

let diff xs ys =
  let rec aux = fun xs ys ->
    match (xs, ys) with
    | (vs, []) -> vs
    | ([],vs)  -> []
    | (x::xs, y::ys) ->
        (match (x, y) with
        | (Interv (i1, j1), Interv (i2, j2)) ->
            (match i1 <=> i2 with
            | Equal ->
                (match j1 <=> j2 with
                | Equal -> aux xs ys
                    (*  [  ] *)
                    (*  [  ] *)
                | Inf -> aux xs      ((Interv (j1+1, j2))::ys)
                    (*  [  ]  *)
                    (*  [      ] *)
                | Sup -> aux ((Interv (j2+1, j1))::xs) ys
                    (*  [    ] *)
                    (*  [ ]  *)
                )
            | Inf ->
                if j1 < i2 then Interv (i1, j1):: aux xs (y::ys)
                  (*  [    ] *)
                  (*         [ ] *)
                else
                  (match j1 <=> j2 with
                  | Equal -> (Interv (i1, i2-1))::aux xs ys (* -1 cos exlude [ *)
                      (*  [    ] *)
                      (*     [ ] *)
                  | Inf ->   (Interv (i1, i2-1))::aux xs ((Interv (j1+1, j2))::ys)
                      (*  [    ]  *)
                      (*     [     ]   *)
                  | Sup ->   (Interv (i1, i2-1))::aux ((Interv (j2+1, j1))::xs) ys
                      (*  [       ] *)
                      (*     [ ]  *)
                  )
            | Sup ->
                if j2 < i1 then aux (x::xs) ys
                  (*       [    ] *)
                  (*  [ ] *)
                else
                  (match j1 <=> j2 with
                  | Equal -> aux xs ys
                      (*         [    ] *)
                      (*     [        ] *)
                  | Inf ->   aux xs ((Interv (j1+1, j2))::ys)
                      (*         [    ]  *)
                      (*     [           ]   *)
                  | Sup ->   aux ((Interv (j2+1, j1))::xs) ys
                      (*         [    ] *)
                      (*     [      ]  *)
                  )
            )
        | _ -> raise (Impossible 9) (* intervise *)
        )
  in
(*       minus_set (tolist xs) (tolist ys) +> fromlist *)
  List.rev_map exactize (aux (List.rev_map intervise xs) (List.rev_map intervise ys))


(*     let _ = Example (diff [Interv (3,7)] [Interv (4,5)] = [Interv (6, 7); Exact 3]) *)

(*****************************************************************************)
let rec debug = function
  | [] -> ""
  | (Exact i)::xs -> (Printf.sprintf "Exact:%d;" i) ^  (debug xs)
  | (Interv (i,j)::xs) -> (Printf.sprintf "Interv:(%d,%d);" i j) ^ debug xs

(*****************************************************************************)
(* if operation return wrong result, then may later have to patch them *)
let patch1 xs = List.map exactize xs
let patch2 xs = xs +> List.map (fun e ->
  match e with
  | Interv (i,j) when i > j && i =|= j+1 ->
      let _ = pr2 (sprintf "i = %d, j = %d" i j) in
      Exact i
  | e -> e
)
let patch3 xs =
  let rec aux min xs =
    xs +> List.fold_left (fun (min,acc) e ->
      match e with
      | Exact i ->
          if i =|= min
          then (min, acc)
          else (i, (Exact i)::acc)
      | Interv (i,j) ->
          (j, (Interv (i,j)::acc))
    ) (min, [])
  in
  aux min_int (List.rev xs) +> snd


