function [ret_minval,final_xatmin,history] = Direct...
   (Problem,bounds,opts,varargin)
% Function   : Direct Version 4.0
% Written by : Dan Finkel (definkel@unity.ncsu.edu)
% Created on : 01/27/2003
% Last Update: 06/21/2004
%            : 01-July-2021 (AK, added the 'limit' option, and some more documentation)
% This code was taken from the following repository:
% https://github.com/industrial-optimization-group/SURROGATE-ASF-code
%
% This code comes with no guarantee or warranty of any kind
%
%
%
%          [x,fmin,history] = Direct(Problem,bounds,opts,varargin)
%
% Parameters:
%         IN: Problem   - Structure containing problem
%                    Problem.f              = Objective function handle
%
%                    NOTE: If you problem has no constraints (other than
%                          those on the bounds, this is the only field you
%                          need to add to Problem
%
%                    Problem.numconstraints = number of constraints
%                    Problem.constraint(i).func    = i-th constraint handle
%                    Problem.constraint(i).penalty = penalty parameter for
%                                                    i-th constraint
%                    Note: If f returns objective function AND constraints
%                          then set Problem.constraint(1).func = Problem.f
%             bounds    - an n x 2 vector of the lower and upper bounds.
%                         The first column is the lower bounds, and the second
%                         column contains the upper bounds
%             opts      - (optional) MATLAB structure.
%                    opts.ep        = Jones factor                      (default is 1e-4)
%                    opts.maxevals  = max. number of function evals     (default is 20)
%                    opts.maxits    = max. number of iterations         (default is 10)
%                    opts.maxdeep   = max. number of rect. divisions    (default is 100)
%                    opts.testflag  = 1 if globalmin known, 0 otherwise (default is 0)
%                    opts.showits   = 1 if disp. stats shown, 0 oth.
%                                      (default is 1)
%                    opts.globalmin = globalmin (if known)
%                                      (default is 0)
%                    opts.tol       = tolerance for term. if tflag=1
%                                      (default is 0.01)
%                    opts.impcons   = turns on implicit constraint capability
%                                      (default is 0)
%                                     If set to one, objective function
%                                     is expected to return a flag which represents
%                                     the feasibility of the point sampled
%                    opts.limit     = limit for the objective, after which
%                                     the iteration can be stopped, i.e.,
%                                     if a point x s.t. f(x) < limit is
%                                     found, the iteration is stopped. Set
%                                     to -inf by default, i.e., no limit is
%                                     selected.
%             varargin  - (optional) additional arguements to be passed to
%                         objective function
%
%                NOTE: If opts.tflag == 0, maxevals, maxevals and maxdeep are ignored.
%                      DIRECT will stop when the absolute error is less
%                      than tol. Also, preallocation will not occur, and the algorithm
%                      can run slower than if opts.tflag == 1
%                NOTE: opts.maxevals is an approximate stopping condition. DIRECT will
%                      exceed this budget by a slight amount
%
%        OUT: minval    -  minimum value found
%             xatmin    - (optional) location of minimal value
%             history      - (optional) array of iteration historyory, useful for tables and plots
%                The three columns are iteration, fcn evals, and min value found.
%
%                Direct may be called by
%
%                minval = Direct(Problem,bounds);
%                          or
%                with any variation of the optional arguments

%------------------------------------------------------------------%
%
% Implementation taken from:
% D.R. Jones, C.D. Perttunen, and B.E. Stuckman. "Lipschitzian
% Optimization Without the Lipschitz Constant". Journal of
% Optimization Theory and Application, 79(1):157-181, October 1993
%
%------------------------------------------------------------------%

%-- Initialize the variables --------------------------------------%
lengths = [];c = [];fc = [];
con = [];szes = [];feas_flags=[];
om_lower     = bounds(:,1);
om_upper     = bounds(:,2);
fcncounter   = 0;
perror       = 0;
itctr        = 1;
done         = 0;
g_nargout    = nargout;
n            = size(bounds,1);

% Determine option values
if nargin<3, opts=[]; end
if (nargin>=3) & (length(opts)==0), opts=[]; end
getopts(opts, ...
 'maxits',     20,...         % maximum of iterations
 'maxevals',   10,...         % maximum # of function evaluations
 'maxdeep',    100,...        % maximum number of side divisions
 'testflag',   0,...          % terminate if within a relative tolerence of f_opt
 'globalmin',  0,...          % minimum value of function
 'ep',         1e-4,...       % global/local weight parameter.
 'tol',        0.01,...       % allowable relative error if f_reach is set
 'showits',    1,...          % print iteration stats
 'impcons',    0,...          % flag for using implicit constraint handling
 'pert',       1e-6,...       % pertubation for implicit constraint handling
 'limit',      -inf);         % stop the iteration when a point x was found s.t. f(x) < limit
% 'maxflag',   0, ...         % set to 1 for max problems, 0 for min problems
% 'sizeconst', 0.5,...        % constant on rectangle size function
% 'distance',  1,...          % 1/0 for distance/volume measure of size
% 'minlength', 1e-4,...       % stop if best rectangle has all sides 1ess than this
% 'minevals',  0,...          % but must evaluate at least this many points

theglobalmin = globalmin;
tflag        = testflag;

%-- New 06/08/2004 Pre-allocate memory for storage vectors
if tflag == 0
    lengths    = zeros(n,maxevals + floor(.10*maxevals));
    c          = lengths;
    fc         = zeros(1,maxevals + floor(.10*maxevals));
    szes       = fc;
    con        = fc;
    feas_flags = fc;
end

%-- Call DIRini ---------------------------------------------------%
[thirds , lengths, c , fc, con, feas_flags minval,xatmin,perror,...
        history,szes,fcncounter,calltype] =...
        DIRini(Problem,n,bounds(:,1),bounds(:,2),...
        lengths,c,fc,con, feas_flags, szes,...
        theglobalmin,maxdeep,tflag,g_nargout, impcons, varargin{:});

ret_minval = minval;
ret_xatmin = xatmin;
%-- MAIN LOOP -----------------------------------------------------%
minval = fc(1) + con(1);
while perror > tol
   %-- Create list S of potentially optimal hyper-rectangles
   S = find_po(fc(1:fcncounter)+con(1:fcncounter),...
       lengths(:,1:fcncounter),minval,ep,szes(1:fcncounter));

   %-- Loop through the potentially optimal hrectangles -----------%
   %-- and divide -------------------------------------------------%
   for i = 1:size(S,2)
      [lengths,fc,c,con,feas_flags,szes,fcncounter,success] = ...
          DIRdivide(bounds(:,1),bounds(:,2),Problem,S(1,i),thirds,lengths,...
          fc,c,con,feas_flags,fcncounter,szes,impcons,calltype,varargin{:});
   end

   %-- update minval, xatmin --------------------------------------%
   [minval,fminindex] =  min(fc(1:fcncounter)+con(1:fcncounter));
   penminval = minval + con(fminindex);
   xatmin = (om_upper - om_lower).*c(:,fminindex) + om_lower;
   if (con(fminindex) > 0)|(feas_flags(fminindex) ~= 0)
       %--- new minval is infeasible, don't do anything
   else
       %--- update return values
       ret_minval = minval;
       ret_xatmin = xatmin;
   end

   %--see if we are done ------------------------------------------%
   if tflag == 1
      %-- Calculate error if globalmin known
      if theglobalmin ~= 0
          perror = 100*(minval - theglobalmin)/abs(theglobalmin);
      else
          perror = 100*minval;
      end
   else
      %-- Have we exceeded the maxits?
      if itctr >= maxits
         %disp('Exceeded max iterations. Increase maxits')
         done = 1;
      end
      %-- Have we exceeded the maxevals?
      if fcncounter > maxevals
         %disp('Exceeded max fcn evals. Increase maxevals')
         done = 1;
      end
      if done == 1
         perror = -1;
      end
   end
   if max(max(lengths)) >= maxdeep
      %-- We've exceeded the max depth
      %disp('Exceeded Max depth. Increse maxdeep')
      perror = -1;
   end
   if g_nargout == 3
      %-- Store History
      maxhist = size(history,1);
      history(maxhist+1,1) = itctr;
      history(maxhist+1,2) = fcncounter;
      history(maxhist+1,3) = minval;
  end

  %-- New, 06/09/2004
  %-- Call replaceinf if impcons flag is set to 1
  if impcons == 1
      fc = replaceinf(lengths(:,1:fcncounter),c(:,1:fcncounter),...
          fc(1:fcncounter),con(1:fcncounter),...
          feas_flags(1:fcncounter),pert);
  end
  
  %-- New, 01-July-2021, AK
  %-- Check if we have already passed the limit
  if minval < limit
      perror = -1;
  end

  %-- show iteration stats
  if showits == 1
    if  (con(fminindex) > 0) | (feas_flags(fminindex) == 1)
        fprintf('Iter: %4i   f_min: %15.10f*    fn evals: %8i\n',...
         itctr,minval,fcncounter);
    else
        fprintf('Iter: %4i   f_min: %15.10f    fn evals: %8i\n',...
         itctr,minval,fcncounter);
    end
  end
  itctr  = itctr + 1;
end

%-- Return values
if g_nargout == 2
    %-- return x*
    final_xatmin = ret_xatmin;
elseif g_nargout == 3
    %-- return x*
    final_xatmin = ret_xatmin;

    %-- chop off 1st row of history
    history(1:size(history,1)-1,:) = history(2:size(history,1),:);
    history = history(1:size(history,1)-1,:);
end
return
%------------------------------------------------------------------%
% Function:   DIRini                                               %
% Written by: Dan Finkel                                           %
% Created on: 10/19/2002                                           %
% Purpose   : Initialization of Direct                             %
%             to eliminate storing floating points                 %
%------------------------------------------------------------------%
function [l_thirds,l_lengths,l_c,l_fc,l_con, l_feas_flags, minval,xatmin,perror,...
        history,szes,fcncounter,calltype] = DIRini(Problem,n,a,b,...
        p_lengths,p_c,p_fc,p_con, p_feas_flags, p_szes,theglobalmin,...
        maxdeep,tflag,g_nargout,impcons,varargin)

l_lengths    = p_lengths;
l_c          = p_c;
l_fc         = p_fc;
l_con        = p_con;
l_feas_flags = p_feas_flags;
szes         = p_szes;


%-- start by calculating the thirds array
%-- here we precalculate (1/3)^i which we will use frequently
l_thirds(1) = 1/3;
for i = 2:maxdeep
   l_thirds(i) = (1/3)*l_thirds(i-1);
end

%-- length array will store # of slices in each dimension for
%-- each rectangle. dimension will be rows; each rectangle
%-- will be a column

%-- first rectangle is the whole unit hyperrectangle
l_lengths(:,1) = zeros(n,1);

%01/21/04 HACK
%-- store size of hyperrectangle in vector szes
szes(1,1) = 1;

%-- first element of c is the center of the unit hyperrectangle
l_c(:,1) = ones(n,1)/2;

%-- Determine if there are constraints
calltype = DetermineFcnType(Problem,impcons);

%-- first element of f is going to be the function evaluated
%-- at the center of the unit hyper-rectangle.
%om_point   = abs(b - a).*l_c(:,1)+ a;
%l_fc(1)    = feval(f,om_point,varargin{:});
[l_fc(1),l_con(1), l_feas_flags(1)] = ...
    CallObjFcn(Problem,l_c(:,1),a,b,impcons,calltype,varargin{:});
fcncounter = 1;


%-- initialize minval and xatmin to be center of hyper-rectangle
xatmin = l_c(:,1);
minval   = l_fc(1);
if tflag == 1
    if theglobalmin ~= 0
        perror = 100*(minval - theglobalmin)/abs(theglobalmin);
    else
        perror = 100*minval;
    end
else
   perror = 2;
end

%-- initialize history
%if g_nargout == 3
    history(1,1) = 0;
    history(1,2) = 0;
    history(1,3) = 0;
%end
%------------------------------------------------------------------%
% Function   :  find_po                                            %
% Written by :  Dan Finkel                                         %
% Created on :  10/19/2002                                         %
% Purpose    :  Return list of PO hyperrectangles                  %
%------------------------------------------------------------------%
function rects = find_po(fc,lengths,minval,ep,szes)

%-- 1. Find all rects on hub
diff_szes = sum(lengths,1);
tmp_max = max(diff_szes);
j=1;
sum_lengths = sum(lengths,1);
for i =1:tmp_max+1
    tmp_idx = find(sum_lengths==i-1);
    [tmp_n, hullidx] = min(fc(tmp_idx));
    if length(hullidx) > 0
        hull(j) = tmp_idx(hullidx);
        j=j+1;
        %-- 1.5 Check for ties
        ties = find(abs(fc(tmp_idx)-tmp_n) <= 1e-13);
        if length(ties) > 1
            mod_ties = find(tmp_idx(ties) ~= hull(j-1));
            hull = [hull tmp_idx(ties(mod_ties))];
            j = length(hull)+1;
        end
    end
end
%-- 2. Compute lb and ub for rects on hub
lbound = calc_lbound(lengths,fc,hull,szes);
ubound = calc_ubound(lengths,fc,hull,szes);

%-- 3. Find indeces of hull who satisfy
%--    1st condition
maybe_po = find(lbound-ubound <= 0);

%-- 4. Find indeces of hull who satisfy
%--    2nd condition
t_len  = length(hull(maybe_po));
if minval ~= 0
    po = find((minval-fc(hull(maybe_po)))./abs(minval) +...
        szes(hull(maybe_po)).*ubound(maybe_po)./abs(minval) >= ep);
else
    po = find(fc(hull(maybe_po)) -...
        szes(hull(maybe_po)).*ubound(maybe_po) <= 0);
end
final_pos      = hull(maybe_po(po));

rects = [final_pos;szes(final_pos)];
return
%------------------------------------------------------------------%
% Function   :  calc_ubound                                        %
% Written by :  Dan Finkel                                         %
% Created on :  10/19/2002                                         %
% Purpose    :  calculate the ubound used in determing potentially %
%               optimal hrectangles                                %
%------------------------------------------------------------------%
function ub = calc_ubound(lengths,fc,hull,szes)

hull_length  = length(hull);
hull_lengths = lengths(:,hull);
for i =1:hull_length
    tmp_rects = find(sum(hull_lengths,1)<sum(lengths(:,hull(i))));
    if length(tmp_rects) > 0
        tmp_f     = fc(hull(tmp_rects));
        tmp_szes  = szes(hull(tmp_rects));
        tmp_ubs   = (tmp_f-fc(hull(i)))./(tmp_szes-szes(hull(i)));
        ub(i)        = min(tmp_ubs);
    else
        ub(i)=1.976e14;
    end
end
return
%------------------------------------------------------------------%
% Function   :  calc_lbound                                        %
% Written by :  Dan Finkel                                         %
% Created on :  10/19/2002                                         %
% Purpose    :  calculate the lbound used in determing potentially %
%               optimal hrectangles                                %
%------------------------------------------------------------------%
function lb = calc_lbound(lengths,fc,hull,szes)

hull_length  = length(hull);
hull_lengths = lengths(:,hull);
for i = 1:hull_length
    tmp_rects = find(sum(hull_lengths,1)>sum(lengths(:,hull(i))));
    if length(tmp_rects) > 0
        tmp_f     = fc(hull(tmp_rects));
        tmp_szes  = szes(hull(tmp_rects));
        tmp_lbs   = (fc(hull(i))-tmp_f)./(szes(hull(i))-tmp_szes);
        lb(i)     = max(tmp_lbs);
    else
        lb(i)     = -1.976e14;
    end
end
return
%------------------------------------------------------------------%
% Function   :  DIRdivide                                          %
% Written by :  Dan Finkel                                         %
% Created on :  10/19/2002                                         %
% Purpose    :  Divides rectangle i that is passed in              %
%------------------------------------------------------------------%
function [lengths,fc,c,con,feas_flags,szes,fcncounter,pass] = ...
    DIRdivide(a,b,Problem,index,thirds,p_lengths,p_fc,p_c,p_con,...
    p_feas_flags,p_fcncounter,p_szes,impcons,calltype,varargin)

lengths    = p_lengths;
fc         = p_fc;
c          = p_c;
szes       = p_szes;
fcncounter = p_fcncounter;
con        = p_con;
feas_flags = p_feas_flags;

%-- 1. Determine which sides are the largest
li     = lengths(:,index);
biggy  = min(li);
ls     = find(li==biggy);
lssize = length(ls);
j = 0;

%-- 2. Evaluate function in directions of biggest size
%--    to determine which direction to make divisions
oldc       = c(:,index);
delta      = thirds(biggy+1);
newc_left  = oldc(:,ones(1,lssize));
newc_right = oldc(:,ones(1,lssize));
f_left     = zeros(1,lssize);
f_right    = zeros(1,lssize);
for i = 1:lssize
    lsi               = ls(i);
    newc_left(lsi,i)  = newc_left(lsi,i) - delta;
    newc_right(lsi,i) = newc_right(lsi,i) + delta;
    [f_left(i), con_left(i), fflag_left(i)]    = CallObjFcn(Problem,newc_left(:,i),a,b,impcons,calltype,varargin{:});
    [f_right(i), con_right(i), fflag_right(i)] = CallObjFcn(Problem,newc_right(:,i),a,b,impcons,calltype,varargin{:});
    fcncounter = fcncounter + 2;
end
w = [min(f_left, f_right)' ls];

%-- 3. Sort w for division order
[V,order] = sort(w,1);

%-- 4. Make divisions in order specified by order
for i = 1:size(order,1)

   newleftindex  = p_fcncounter+2*(i-1)+1;
   newrightindex = p_fcncounter+2*(i-1)+2;
   %-- 4.1 create new rectangles identical to the old one
   oldrect = lengths(:,index);
   lengths(:,newleftindex)   = oldrect;
   lengths(:,newrightindex)  = oldrect;

   %-- old, and new rectangles have been sliced in order(i) direction
   lengths(ls(order(i,1)),newleftindex)  = lengths(ls(order(i,1)),index) + 1;
   lengths(ls(order(i,1)),newrightindex) = lengths(ls(order(i,1)),index) + 1;
   lengths(ls(order(i,1)),index)         = lengths(ls(order(i,1)),index) + 1;

   %-- add new columns to c
   c(:,newleftindex)  = newc_left(:,order(i));
   c(:,newrightindex) = newc_right(:,order(i));

   %-- add new values to fc
   fc(newleftindex)  = f_left(order(i));
   fc(newrightindex) = f_right(order(i));

   %-- add new values to con
   con(newleftindex)  = con_left(order(i));
   con(newrightindex) = con_right(order(i));

   %-- add new flag values to feas_flags
   feas_flags(newleftindex)  = fflag_left(order(i));
   feas_flags(newrightindex) = fflag_right(order(i));

   %-- 01/21/04 Dan Hack
   %-- store sizes of each rectangle
   szes(1,newleftindex)  = 1/2*norm((1/3*ones(size(lengths,1),1)).^(lengths(:,newleftindex)));
   szes(1,newrightindex) = 1/2*norm((1/3*ones(size(lengths,1),1)).^(lengths(:,newrightindex)));
end
szes(index) = 1/2*norm((1/3*ones(size(lengths,1),1)).^(lengths(:,index)));
pass = 1;

return
%------------------------------------------------------------------%
% Function   :  CallConstraints                                    %
% Written by :  Dan Finkel                                         %
% Created on :  06/07/2004                                         %
% Purpose    :  Evaluate Constraints at pointed specified          %
%------------------------------------------------------------------%
function ret_value = CallConstraints(Problem,x,a,b,varargin)

%-- Scale variable back to original space
point = abs(b - a).*x+ a;

ret_value = 0;
if isfield(Problem,'constraint')
    if ~isempty(Problem.constraint)
        for i = 1:Problem.numconstraints
            if length(Problem.constraint(i).func) == length(Problem.f)
                if double(Problem.constraint(i).func) == double(Problem.f)                    
                    %-- Dont call constraint; value was returned in obj fcn
                    con_value = 0;
                else
                    con_value = feval(Problem.constraint(i).func,point,varargin{:});
                end
            else
                con_value = feval(Problem.constraint(i).func,point,varargin{:});
            end
            if con_value > 0
                %-- Infeasible, punish with associated pen. param
                ret_value = ret_value + con_value*Problem.constraint(i).penalty;
            end
        end
    end
end
return
%------------------------------------------------------------------%
% Function   :  CallObjFcn                                         %
% Written by :  Dan Finkel                                         %
% Created on :  06/07/2004                                         %
% Purpose    :  Evaluate ObjFcn at pointed specified               %
%------------------------------------------------------------------%
function [fcn_value, con_value, feas_flag] = ...
    CallObjFcn(Problem,x,a,b,impcon,calltype,varargin)

con_value = 0;
feas_flag = 0;

%-- Scale variable back to original space
point = abs(b - a).*x+ a;

if calltype == 1
    %-- No constraints at all
    fcn_value = feval(Problem.f,point,varargin{:});
elseif calltype == 2
    %-- f returns all constraints
    [fcn_value, cons] = feval(Problem.f,point,varargin{:});
    for i = 1:length(cons)
        if cons > 0
            con_value = con_value + Problem.constraint(i).penalty*cons(i);
        end
    end
elseif calltype == 3
    %-- f returns no constraint values
    fcn_value = feval(Problem.f,point,varargin{:});
    con_value = CallConstraints(Problem,x,a,b,varargin{:});
elseif calltype == 4
    %-- f returns feas flag
    [fcn_value,feas_flag] = feval(Problem.f,point,varargin{:});
elseif calltype == 5
    %-- f returns feas flags, and there are constraints
    [fcn_value,feas_flag] = feval(Problem.f,point,varargin{:});
    con_value = CallConstraints(Problem,x,a,b,varargin{:});
end
if feas_flag == 1
    fcn_value = 10^9;
    con_value = 0;
end
return
%------------------------------------------------------------------%
% Function   :  replaceinf                                         %
% Written by :  Dan Finkel                                         %
% Created on :  06/09/2004                                         %
% Purpose    :  Assign R. Carter value to given point              %
%------------------------------------------------------------------%
function fcn_values = replaceinf(lengths,c,fc,con,flags,pert)

%-- Initialize fcn_values to original values
fcn_values = fc;

%-- Find the infeasible points
infeas_points = find(flags == 1);

%-- Find the feasible points
feas_points   = find(flags == 0);

%-- Calculate the max. value found so far
if ~isempty(feas_points)
    maxfc = max(fc(feas_points) + con(feas_points));
else
    maxfc = max(fc + con);
end

for i = 1:length(infeas_points)
    if isempty(feas_points)
        %-- no feasible points found yet
        found_points = [];found_pointsf = [];
        index = infeas_points(i);
    else
        index = infeas_points(i);

        %-- Initialize found points to be entire set
        found_points  = c(:,feas_points);
        found_pointsf = fc(feas_points) + con(feas_points);

        %-- Loop through each dimension, and find points who are close enough
        for j = 1:size(lengths,1)
            neighbors = find(abs(found_points(j,:) - c(j,index)) <= ...
                3^(-lengths(j,index)));
            if ~isempty(neighbors)
                found_points  = found_points(:,neighbors);
                found_pointsf = found_pointsf(neighbors);
            else
                found_points = [];found_pointsf = [];
                break;
            end
        end
    end

    %-- Assign Carter value to the point
    if ~isempty(found_pointsf)
        %-- assign to index the min. value found + a little bit more
        fstar = min(found_pointsf);
        if fstar ~= 0
            fcn_values(index) = fstar + pert*abs(fstar);
        else
            fcn_values(index) = fstar + pert*1;
        end
    else
        fcn_values(index) = maxfc+1;
        maxfc             = maxfc+1;
    end
end
return
%------------------------------------------------------------------%
% Function   :  DetermineFcnType                                   %
% Written by :  Dan Finkel                                         %
% Created on :  06/25/2004                                         %
% Purpose    :  Determine how constraints are handled              %
%------------------------------------------------------------------%
function retval = DetermineFcnType(Problem,impcons)

retval = 0;
if (~isfield(Problem,'constraint'))&(~impcons)
    %-- No constraints at all
    retval = 1;
end
if isfield(Problem,'constraint')
    %-- There are explicit constraints. Next determine where
    %-- they are called
    if ~isempty(Problem.constraint)
        if length(Problem.constraint(1).func) == length(Problem.f)
            %-- Constraint values may be returned from objective
            %-- function. Investigate further
            if double(Problem.constraint(1).func) == double(Problem.f)
                %-- f returns constraint values
                retval = 2;
            else
                %-- f does not return constraint values
                retval = 3;
            end
        else
            %-- f does not return constraint values
            retval = 3;
        end
    else
        if impcons
            retval = 0;
        else
            retval = 1;
        end
    end
end

if (impcons)
    if ~retval
        %-- only implicit constraints
        retval = 4;
    else
        %-- both types of constraints
        retval = 5;
    end
end
%------------------------------------------------------------------%
% GETOPTS Returns options values in an options structure
% USAGE
%   [value1,value2,...]=getopts(options,field1,default1,field2,default2,...)
% INPUTS
%   options  : a structure variable
%   field    : a field name
%   default  : a default value
% OUTPUTS
%   value    : value in the options field (if it exists) or the default value
%
% Variables with the field names will be created in the caller's workspace
% and set to the value in the option variables field (if it exists) or to the
% default value.
%
% Example called from a function:
%   getopts(options,'tol',1e-8,'maxits',100);
% where options contains the single field 'tol' with value equal to 1
% The function have two variable defined in the local workspace, tol with a
% value of 1 and maxits with a value of 100.
%
% If options contains a field name not in the list passed to getopts, a
% warning is issued.
%
%
% Many thanks to the author of this function,
% Paul Fackler (pfackler@ncsu.edu)
%
%
%------------------------------------------------------------------%
function varargout=getopts(options,varargin)
K=fix(nargin/2);
if nargin/2==K
  error('fields and default values must come in pairs')
end
if isa(options,'struct'), optstruct=1; else, optstruct=0; end
varargout=cell(K,1);
k=0;
ii=1;
for i=1:K
  if optstruct & isfield(options,varargin{ii})
    assignin('caller',varargin{ii},getfield(options,varargin{ii}));
    k=k+1;
  else
    assignin('caller',varargin{ii},varargin{ii+1});
  end
  ii=ii+2;
end

if optstruct & k~=size(fieldnames(options),1)
  warning('options variable contains improper fields')
end

return
%------------------------------------------------------------------%
% Versions  : 1.0 - 1st successful implemenation of DIRect
%           : 2.0 - Removed floating point arithmetic
%                   duplicated Table 5 of Jones et al.
%           : 2.1 - increased speed by storing size calcs.
%           : 2.2 - utitilized linked lists to increase speed
%           : 2.3 - rewrote ubound to increase speed
%           : 2.4 - rewrote lbound to increase speed
%           : 2.5 - removed call to calcsize
%           : 2.6 - added check_for_ties
%           : 2.7 - rewrote check_for_ties to compare fp correctly
%           : 2.8 - changed output arguments, rewrote help
%           : 3.0 - simplified input/output. Put on web.
%           : 3.1 - Performanced Tuned! Tremendous speed increase
%           : 3.2 - Removed llists; performance tuned
%                   Many thanks to Ray Muzic and Paul Fackler
%                   for their suggestions to improve this code
%           : 4.0 - Sped up code, and added 2 constraint handling
%                   mechanisms.
%------------------------------------------------------------------%