(* $Id: absOrd.ml,v 1.5 2003/06/18 15:11:06 yori Exp $ *)
(* Copyright 2002 Yamagata Yoriyuki *)

type point = int

module Int = struct type t = int let compare = (-) end

module Set = ISet
module Map = Map.Make (Int)
module IntSet = Set
module IntMap = Map

type node = 
    Empty | Leaf of point
  | Node of Set.t * node * node * int

module Node =
struct
let height = function
    Empty -> 0
  | Leaf _  -> 1
  | Node (_, _, _, h) -> h

let elts = function
    Empty -> Set.empty
  | Leaf p -> Set.add p Set.empty
  | Node (s, _, _, _) -> s

let create l r =
  match l, r with
    Empty, _ -> r
  | _, Empty -> l
  | _ ->
      let hl = height l in
      let hr = height r in
      let h = 1 + max hl hr in
      let s = Set.union (elts l) (elts r) in
      Node (s, l, r, h)
	
let rec bal = function
    Empty -> Empty
  | Leaf _ as s -> s
  | Node (s, l, r, _) as node ->
  let hl = height l in
  let hr = height r in
  if hl - hr > 2 then
    match l with
      Node (ls, ll, lr, _) ->
	let hll = height ll in
	let hlr = height lr in
	if hll >= hlr then
	  Node (s, ll, concat lr r, 1 + hll)
	else
	  (match lr with
	    Node (_, lrl, lrr, _) ->
	      let l' = concat ll lrl in
	      let r' = concat lrr r in
	      let h = 1 + max (height l') (height r') in
	      Node (s, l', r', h)
	  | _ -> assert false)
    |	_ -> assert false
  else if hr - hl > 2 then
    match r with
      Node (rs, rl, rr, _) ->
	let hrl = height rl in
	let hrr = height rr in
	if hrl <= hrr then
	  Node (s, concat l rl, rr, 1 + hrr)
	else
	  (match rl with
	    Node (_, rll, rlr, _) ->
	      let l' = concat l rll in
	      let r' = concat rlr rr in
	      let h = 1 + max (height l') (height r') in
	      Node (s, l', r', h)
	  | _ -> assert false)
    |	_ -> assert false
  else node

and concat l r = bal (create l r)
 
let mem p  = function
    Empty -> false
  | Leaf p' -> (p = p')
  | Node (s, _, _, _) -> Set.mem p s

let rec compare p1 p2 = function
    Empty -> raise Not_found
  | Leaf p ->
      if p1 = p && p2 = p then 0 else 
      raise Not_found
  | Node (s, s1, s2, _) ->
      if mem p1 s1 then
	if mem p2 s1 then compare p1 p2 s1 else
	if mem p2 s2 then -1 else
	raise Not_found
      else if mem p1 s2 then
	if mem p2 s1 then 1 else
	if mem p2 s2 then compare p1 p2 s2 else
	raise Not_found
      else
	raise Not_found

let rec top = function
    Empty -> raise Not_found
  |	Leaf p -> p
  |	Node (_, s1, s2, _) -> 
      try top s2 with Not_found -> top s1

let rec top_in m = function
    Empty -> raise Not_found
  | Leaf p ->
      if Map.mem p m then p else raise Not_found
  | Node (_, s1, s2, _) ->
      try top_in m s2 with Not_found -> top_in m s1

let rec bottom = function
    Empty -> raise Not_found
  |	Leaf p -> p
  |	Node (_, s1, s2, _) -> 
      try bottom s1 with Not_found -> bottom s2

let rec bottom_in m = function
    Empty -> raise Not_found
  | Leaf p ->
      if Map.mem p m then p else raise Not_found
  | Node (_, s1, s2, _) ->
      try bottom_in m s1 with Not_found -> bottom_in m s1

let rec next p = function
    Empty -> raise Not_found
  |	Leaf _ -> raise Not_found
  |	Node (_, s1, s2, _) ->
      if mem p s1 then try next p s1 with Not_found -> bottom s2 else
      next p s2

let rec prev p = function
    Empty -> raise Not_found
  |	Leaf _ -> raise Not_found
  |	Node (_, s1, s2, _) ->
      if mem p s2 then try prev p s2 with Not_found -> top s1 else
      prev p s1

let rec before p = function
    Empty -> Empty
  | Leaf p' ->
      if p = p' then Empty else raise Not_found
  | Node (_, s1, s2, h) ->
      if mem p s1 then
	before p s1
      else
	concat s1 (before p s2)

let rec upto p = function
    Empty -> Empty
  | Leaf p' as s -> 
      if p = p' then s else raise Not_found
  | Node (_, s1, s2, h) ->
      if mem p s1 then
	upto p s1
      else
	concat s1 (upto p s2)

let rec after p = function
    Empty -> Empty
  | Leaf p' ->
      if p = p' then Empty else raise Not_found
  | Node (_, s1, s2, h) ->
      if mem p s1 then
	concat (after p s1) s2
      else
	after p s2

let rec from p = function
    Empty -> Empty
  | Leaf p' as s ->
      if p = p' then s else raise Not_found
  | Node (_, s1, s2, h) ->
      if mem p s1 then
	concat (from p s1) s2
      else
	from p s2

let rec iter proc = function
    Empty -> ()
  | Leaf p -> proc p
  | Node (_, s1, s2, _) -> 
      iter proc s1; 
      iter proc s2

let rec fold f s init =
  match s with
    Empty -> init
  | Leaf p -> f p init
  | Node (_, s1, s2, _) ->
      fold f s2 (fold f s1 init)

let rec put_to_top p = function
    Empty -> Leaf p
  | Leaf _ as s1 -> create s1 (Leaf p)
  | Node (s, s1, s2, h) ->
      let s' = Set.add p s in
      let s2' = put_to_top p s2 in
      let h = 1 + max (height s1) (height s2') in
      bal (Node (s', s1, s2', h))

let rec put_to_bottom p = function
    Empty -> Leaf p
  | Leaf _ as s2 -> create (Leaf p) s2
  | Node (s, s1, s2, h) ->
      let s' = Set.add p s in
      let s1' = put_to_bottom p s1 in
      let h = 1 + max (height s1') (height s2) in
      bal (Node (s', s1', s2, h))

let rec put_before p0 p = function
    Empty -> raise Not_found
  | Leaf p1 as s ->
      if p1 = p0 then create (Leaf p) s else raise Not_found
  | Node (s, s1, s2, h) ->
      let s' = Set.add p s in
      let s1', s2' =
	if mem p0 s1 then (put_before p0 p s1), s2 else
	s1, (put_before p0 p s2)
      in
      let h = 1 + max (height s1') (height s2') in
      bal (Node (s', s1', s2', h))

let rec put_after p0 p = function
    Empty -> raise Not_found
  | Leaf p1 as s ->
      if p1 = p0 then create s (Leaf p) else raise Not_found
  | Node (s, s1, s2, h) ->
      let s' = Set.add p s in
      let s1', s2' =
	if mem p0 s1 then (put_after p0 p s1), s2 else
	s1, (put_after p0 p s2)
      in
      let h = 1 + max (height s1') (height s2') in
      bal (Node (s', s1', s2', h))

end

type t = node * int

let compare p1 p2 (node, _) = Node.compare p1 p2 node
let top (node, _) = Node.top node
let bottom (node, _) = Node.bottom node
let next p (node, _) = Node.next p node
let prev p (node, _) = Node.prev p node

let add_top (node, id) = (id, (Node.put_to_top id node, id + 1))

let add_bottom (node, id) = (id, (Node.put_to_bottom id node, id + 1))

let add_before p (node, id) =
  let l = Node.before p node in
  let r = Node.from p node in
  (id, (Node.put_before p id node, id + 1))

let add_after p (node, id) =
  let l = Node.upto p node in
  let r = Node.after p node in
  (id, (Node.put_after p id node, id + 1))

let iter proc (node, _) = Node.iter proc node
let fold f (node, _) init = Node.fold f node init

let rec import_aux a i j w2p p2w id =
  if i = j then
    (Leaf id, IntMap.add a.(i) id w2p, Map.add id a.(i) p2w, id + 1)
  else
    let i' = i + (j - i) / 2 in
    let ord1, w2p, p2w, id = import_aux a i i' w2p p2w id in
    let ord2, w2p, p2w, id = import_aux a (i' + 1) j w2p p2w id in
    (Node.concat ord1 ord2, w2p, p2w, id)

let import weights =
  let set = List.fold_left (fun set w ->
    IntSet.add w set) 
      IntSet.empty 
      weights 
  in
  let weights = IntSet.fold (fun w ws -> w :: ws) set [] in
  let a = Array.of_list weights in
  Array.sort (-) a;
  let node, w2p, p2w, id = 
    import_aux a 0 (Array.length a - 1) IntMap.empty Map.empty 0
  in
  ((node, id), w2p, p2w)
