let loc = ref ""

let finish tags rate (true_error_upper_bound, true_error_lower_bound) = 
  Binomial.store_cache ();
  Choose.store_cache ();
  Parse_types.print_tags tags;
  print_string "true_error = ";
  print_float rate;
  print_string " ";
  print_float true_error_upper_bound;
  print_string " ";
  print_float true_error_lower_bound;
  print_newline()

exception Error of string
    
let _ =
  Arg.parse [] (fun x -> loc := x) "";
  let ic = if !loc <> "" then open_in !loc else stdin in
  let parameters = 
    Parse_parameters.main Lex_parameters.token (Lexing.from_channel ic) in
  close_in ic;

  Binomial.restore_cache ();
  Choose.restore_cache ();
  
  let parameters = Parse_types.convert parameters in
  let log_delta = log (parameters.Parse_types.delta) 
  and log_lower_delta = log (parameters.Parse_types.lower_delta) 
  and test_errors = parameters.Parse_types.test_errors 
  and test_examples = parameters.Parse_types.test_examples
  and train_examples = parameters.Parse_types.train_examples
  and train_errors = parameters.Parse_types.train_errors
  and log_hypothesis = parameters.Parse_types.log_hypothesis_size
  and log_prior = parameters.Parse_types.log_prior 
  and error_log_count = parameters.Parse_types.error_log_count 
  and sample_space_log_size = parameters.Parse_types.sample_space_log_size in
  let len = Array.length error_log_count in

  if test_errors > test_examples then raise (Parse_types.Error "more test errors than examples");
  if train_errors > train_examples then raise (Parse_types.Error "more train errors than examples");
  if len > 0 & len <> train_examples + 1 then raise (Parse_types.Error "Missing log counts");
  
  if Array.length error_log_count > 0 then Choose.prime_cache train_examples;

  begin  match parameters.Parse_types.approx with 
    Parse_types.Hoeffding -> 	print_string "Applying agnostic bound\n";
  | Parse_types.Relative_Entropy -> print_string "Applying relative_entropy bound\n";
  | Parse_types.Exact -> print_string "Applying exact tail bound\n"; 
  | Parse_types.Automatic -> print_string "Applying varying approximation tail bound\n"; 
  end;
  
  let upper_bound, lower_bound = 
    match parameters.Parse_types.approx with 
      Parse_types.Hoeffding -> 	
	Agnostic.agnostic_upper, Agnostic.agnostic_lower
    | Parse_types.Relative_Entropy -> 
	Relative_entropy.relative_entropy_upper, Relative_entropy.relative_entropy_lower
    | Parse_types.Exact -> 
	Binomial.binom_upper, Binomial.binom_lower 
    | Parse_types.Automatic -> 
	Auto_approx.auto_upper, Auto_approx.auto_lower in
  
  let rate, true_error_upper_bound, true_error_lower_bound = 
    if parameters.Parse_types.test_examples <> 0 then
      if train_examples = 0 then
	float test_errors /. float test_examples, upper_bound 0. test_examples test_errors, lower_bound 0. test_examples test_errors
      else
	let size = log_hypothesis -. log_prior in
	
	if Array.length error_log_count > 0 then begin 
	  print_string "Applying shell bound\n";
	  if sample_space_log_size <> Log_float.zero then begin
	    let log_delta = log_delta -. log 2. in
	    let log_lower_delta = log_lower_delta -. log 2. in
	    let error_log_count = Shell.bound_sample upper_bound error_log_count log_delta sample_space_log_size in 
	    float test_errors /. float test_examples,
	    Train_n_test.bound_upper
	      (Shell.computable_shell (lower_bound 0. train_examples) (log_delta -. log 2.) error_log_count train_examples, 
	       upper_bound 0. test_examples)
	      (train_examples, test_examples) (train_errors,test_errors),
	    Train_n_test.bound_lower (lower_bound size train_examples, lower_bound 0. test_examples) (train_examples, test_examples) (train_errors,test_errors) end
	  else begin
	    float test_errors /. float test_examples,
	    Train_n_test.bound_upper
	      (Shell.computable_shell (lower_bound 0. train_examples) (log_delta -. log 2.) error_log_count train_examples, 
	       upper_bound 0. test_examples) 
	      (train_examples, test_examples) (train_errors,test_errors),
	    Train_n_test.bound_lower (lower_bound size train_examples, lower_bound 0. test_examples) (train_examples, test_examples) (train_errors,test_errors) end end
	else
	  float test_errors /. float test_examples,
	  Train_n_test.bound_upper (upper_bound size train_examples, upper_bound 0. test_examples) (train_examples, test_examples) (train_errors,test_errors),
	  Train_n_test.bound_lower (lower_bound size train_examples, lower_bound 0. test_examples) (train_examples, test_examples) (train_errors,test_errors)
    else if train_examples <> 0 then
      let size = log_hypothesis -. log_prior in
      
      if Array.length error_log_count > 0 then begin
	print_string "Applying shell bound\n";
	if sample_space_log_size <> Log_float.zero then begin
	  let log_delta = log_delta -. log 2. in
	  let error_log_count = Shell.bound_sample upper_bound error_log_count log_delta sample_space_log_size in 
	  float train_errors /. float train_examples, 
	  Shell.computable_shell (lower_bound 0. train_examples) (log_delta -. log 2.) error_log_count train_examples train_errors,
	  lower_bound size train_examples train_errors end
	else begin
	  float train_errors /. float train_examples, 
	  Shell.computable_shell (lower_bound 0. train_examples) (log_delta -. log 2.) error_log_count train_examples train_errors,
	  lower_bound size train_examples train_errors end end
      else
	float train_errors /. float train_examples, 
	upper_bound size train_examples train_errors, 
	lower_bound size train_examples train_errors
    else raise (Error "Not enough information to state nontrivial bound") in
  let upper,lower = Search.monotone_decreasing true_error_upper_bound log_delta 0. 1.,
    Search.monotone_increasing true_error_lower_bound log_lower_delta 0. 1. in

  finish parameters rate (upper, lower)
