let find_min_range ?(init_direction="fwd") v pred i =
  if i < v.lo || i > v.hi then
    invalid_arg (sprintf "%d not in range %s" i (to_string v))
  ;
  let rec loop (dir:string) ans =
    if pred ans then Some ans
    else if equal ans v then None
    else
      match dir with
      | "fwd" ->
        let hi' = if ans.hi = v.hi then ans.hi else ans.hi+1 in
        loop "rev" {ans with hi = hi'}
      | "rev" ->
        let lo' = if ans.lo = v.lo then ans.lo else ans.lo-1 in
        loop "fwd" {ans with lo = lo'}
      | _ -> invalid_arg (
        sprintf
          "valid directions are \"fwd\" or \"rev\" but given \"%s\""
          dir
      )
  in loop init_direction {lo=i; hi=i}