(* O(i) *)
let log_choose n i = 
  let fn = float n
  and fi = float i in
  let ret = ref 0. in
  for j = 1 to i do
    ret := !ret +. log ((float (n - j +1)) /. (float j))
  done;!ret

(* O(n) *)
let rec log_factorial n = log (float n) +. log_factorial (n-1)

let log_choose_maps = ref (Array.create 0 (None : float option array option))

(* O(i) uncached 
   O(1) cached *)
let rec cached_log_choose n i = 
  if Array.length !log_choose_maps > n then
    match !log_choose_maps.(n) with
      Some ar -> begin
	match ar.(i) with 
	  Some valu -> valu
	| None -> 
	    let check_high = 
	      if i < n then
		match ar.(i+1) with 
		  Some valu -> Some (valu +. log (float (i+1)) -. log (float (n-i)))
		| None -> None 
	      else None
	    and check_low =
	      if i > 0 then 
		match ar.(i-1) with
		  Some valu -> Some (valu -. log (float i) +. log (float (n-i+1)))
		| None -> None
	      else None in
	    let ret = 
	      match check_high with 
		Some v -> v
	      | None -> 
		  match check_low with 
		    Some v -> v
		  | None -> log_choose n i in
	    ar.(i) <- Some ret;
	    ret
      end
    | None ->  
	!log_choose_maps.(n) <- Some (Array.create (n+1) None);
	cached_log_choose n i
  else begin
    log_choose_maps := Array.append !log_choose_maps (Array.create (max n 1) None);
    cached_log_choose n i
  end

let prime_cache n =
  for i = 0 to n do
    let ret = cached_log_choose n i in ()
  done

let store_cache _ = 
  let oc = open_out_bin "/tmp/choose_cache" in
  output_value oc !log_choose_maps;
  close_out oc

let restore_cache _ = 
  try
    let ic = open_in_bin "/tmp/choose_cache" in
    log_choose_maps := input_value ic;
    close_in ic
  with Sys_error _ -> ()

