function  [br_min, br_mid, br_max, num_evals] = minbrack(f, a, b, fa,  ...
			 varargin)
%MINBRACK Bracket a minimum of a function of one variable.
%
%	Description
%	BRMIN, BRMID, BRMAX, NUMEVALS] = MINBRACK(F, A, B, FA) finds a
%	bracket of three points around a local minimum of F.  The function F
%	must have a one dimensional domain. A < B is an initial guess at the
%	minimum and maximum points of a bracket, but MINBRACK will search
%	outside this interval if necessary. The bracket consists of three
%	points (in increasing order) such that F(BRMID) < F(BRMIN) and
%	F(BRMID) < F(BRMAX). FA is the value of the function at A: it is
%	included to avoid unnecessary function evaluations in the
%	optimization routines. The return value NUMEVALS is the number of
%	function evaluations in MINBRACK.
%
%	MINBRACK(F, A, B, FA, P1, P2, ...) allows additional arguments to be
%	passed to F
%
%	See also
%	LINEMIN, LINEF
%

%	Copyright (c) Ian T Nabney (1996-2001)

% Check function string
f = fcnchk(f, length(varargin));

% Value of golden section (1 + sqrt(5))/2.0
phi = 1.6180339887499;

% Initialise count of number of function evaluations
num_evals = 0;

% A small non-zero number to avoid dividing by zero in quadratic interpolation
TINY = 1.e-10;

% Maximal proportional step to take: don't want to make this too big
% as then spend a lot of time finding the minimum inside the bracket
max_step = 10.0;

fb = feval(f, b, varargin{:});
num_evals = num_evals + 1;

% Assume that we know going from a to b is downhill initially 
% (usually because gradf(a) < 0).
if (fb > fa)
  % Minimum must lie between a and b: do golden section until we find point
  % low enough to be middle of bracket
  c = b;
  b = a + (c-a)/phi;
  fb = feval(f, b, varargin{:});
  num_evals = num_evals + 1;
  while (fb > fa)
    c = b;
    b = a + (c-a)/phi;
    fb = feval(f, b, varargin{:});
    num_evals = num_evals + 1;
  end
else  
  % There is a valid bracket upper bound greater than b
  c = b + phi*(b-a);
  fc = feval(f, c, varargin{:});
  num_evals = num_evals + 1;
  bracket_found = 0;
  
  while (fb > fc)
    % Do a quadratic interpolation (i.e. to minimum of quadratic)
    r = (b-a).*(fb-fc);
    q = (b-c).*(fb-fa);
    u = b - ((b-c)*q - (b-a)*r)/(2.0*(sign(q-r)*max([abs(q-r), TINY])));
    ulimit = b + max_step*(c-b);
    
    if ((b-u)'*(u-c) > 0.0)
      % Interpolant lies between b and c
      fu = feval(f, u, varargin{:});
      num_evals = num_evals + 1;
      if (fu < fc)
	% Have a minimum between b and c
	br_min = b;
	br_mid = u;
	br_max = c;
	return;
      elseif (fu > fb)
	% Have a minimum between a and u
	br_min = a;
	br_mid = c;
	br_max = u;
	return;
      end
      % Quadratic interpolation didn't give a bracket, so take a golden step
      u = c + phi*(c-b);
    elseif ((c-u)'*(u-ulimit) > 0.0)
      % Interpolant lies between c and limit
      fu = feval(f, u, varargin{:});
      num_evals = num_evals + 1;
      if (fu < fc)
	% Move bracket along, and then take a golden section step
	b = c;
	c = u;
	u = c + phi*(c-b);
      else
	bracket_found = 1;
      end
    elseif ((u-ulimit)'*(ulimit-c) >= 0.0)
      % Limit parabolic u to maximum value
      u = ulimit;
    else
      % Reject parabolic u and use golden section step
      u = c + phi*(c-b);
    end
    if ~bracket_found
      fu = feval(f, u, varargin{:});
      num_evals = num_evals + 1;
    end
    a = b; b = c; c = u;
    fa = fb; fb = fc; fc = fu;
  end % while loop
end   % bracket found
br_mid = b;
if (a < c)
  br_min = a;
  br_max = c;
else
  br_min = c; 
  br_max = a;
end
