(* O(f * log (too_tiny) ) 
   = O(1) (agnostic/relative_entropy/cached binomial)
   = O(i + n^.5) (uncached binomial)
*)
let rec monotone_increasing f (target : float) min max =
  if max -. min <= Precision.too_tiny then max
  else 
    let mid = (max +. min) /. 2. in
    let v = f mid in
    if v < target then monotone_increasing f target mid max
    else monotone_increasing f target min mid  

(*
   O(f * log (too_tiny))
   = O(1) (agnostic/relative_entropy/cached binomial)
   = O(i + n^.5) (uncached binomial)
   = O(lower * error_log_counts) shell cached
   = O(lower * error_log_counts + train_examples * error_log_counts) shell uncached
   = O(lower) = O(1) (agnostic/relative_entropy/cached binomial)
              = O(i + n^.5) (binomial uncached)
*)
let rec monotone_decreasing f (target : float) min max =
  if max -. min <= Precision.too_tiny then min
  else 
    let mid = (max +. min) /. 2. in
    let v = f mid in
    if v < target then monotone_decreasing f target min mid
    else monotone_decreasing f target mid max
