(**************************************************************************)
(*                                                                        *)
(*  Ocamlviz --- real-time profiling tools for Objective Caml             *)
(*  Copyright (C) by INRIA - CNRS - Universite Paris Sud                  *)
(*  Authors: Julien Robert                                                *)
(*           Guillaume Von Tokarski                                       *)
(*           Sylvain Conchon                                              *)
(*           Jean-Christophe Filliatre                                    *)
(*           Fabrice Le Fessant                                           *)
(*  GNU Library General Public License version 2                          *)
(*  See file LICENSE for details                                          *)
(*                                                                        *)
(**************************************************************************)

open Ocamlviz

let memo = Point.create "memo"
let memo_nohit = Point.create "memo_nohit"
let tag_list = Tag.create "lists"
let tag_hash = Tag.create ~size:false "hash"

open Format

let values = Array.init 101 (fun i -> i*i)
(* is it actually faster to store them in this order? *)
let values = Array.init 101 
  (fun i -> let j = if i <= 50 then 2*i-1 else 2*i-100 in j*j)

let hi = 50
let n = 20

let () = 
  for i = 1 to hi do printf "%d " values.(i) done; printf "@."

let rec foldint i j f acc = if i > j then acc else foldint (i+1) j f (f i acc)

let memo_t = Hashtbl.create 5003
let () = ignore (Tag.mark tag_hash memo_t)

let max_sum = n * foldint 1 hi (fun i acc -> max values.(i) acc) 0
let max_sum = 295425
let () = printf "max sum = %d@." max_sum

let iteri t f =
  for i = 0 to Array.length t - 1 do
    let v = t.(i) in
    if v >= 1 then f i v
  done

let to_list t =
  let len = Array.length t in
  let rec loop acc i = 
    if i = len then acc 
    else loop (let v = t.(i) in if v >= 1 then (i, v) :: acc else acc) (i+1) 
  in
  loop [] 0

let rec uss lo hi n =
  let len = hi - lo + 1 in
  assert (n <= len);
  let t = Array.create (max_sum + 1) 0 in
  if n = 0 then
    t
  else if n = 1 then begin
    for i = lo to hi do t.(values.(i)) <- 1 done;
    t
  end else begin
    let mid = (lo + hi) / 2 in
    assert (lo <= mid && mid <= hi);
    let len1 = mid - lo + 1 in
    let len2 = hi - mid in
    assert (len = len1 + len2);
    let mark i v = let v = v + t.(i) in t.(i) <- min 2 v in
    if n <= len1 then begin
      let s1 = memo_uss (lo, mid, n) in
      iteri s1 mark
    end;
    if n <= len2 then begin
      let s2 = memo_uss (mid+1, hi, n) in
      iteri s2 mark
    end;
    for n1 = max 1 (n - len2) to min len1 (n-1) do
      let s1 = memo_uss (lo, mid, n1) in
      let s2 = memo_uss (mid+1, hi, n-n1) in
      let s2 = to_list s2 in
      iteri s1
	(fun i1 v1 -> 
	   List.iter
	     (fun (i2, v2) -> 
		mark (i1 + i2) (max v1 v2)) s2)
    done;
    (* printf "lo=%2d hi=%3d n=%d ==> @." lo hi n; *)
    t
  end

and memo_uss ((lo,hi,n) as k) =
  Point.observe memo;
  try 
    Hashtbl.find memo_t k
  with Not_found -> 
    Point.observe memo_nohit;
    let v = uss lo hi n in Hashtbl.add memo_t k v; v

let () = 
  let s = uss 1 hi n in
  let sum = ref 0L in
  iteri s (fun i v -> if v = 1 then sum := Int64.add (Int64.of_int i) !sum);
  printf "sum = %Ld@." !sum;
  wait_for_killed_clients ()




(*
Local Variables: 
compile-command: "unset LANG; make -C .. test-memo1"
End: 
*)
