function options = optimset(varargin)
%MATLAB Code Generation Library Function

% Limitations:
%   1. The 0 input 0 output syntax which prints all availble fields and values:
%
%     >> optimset
%
%   is not supported.
%   2. When passing a function handle or function name only functions supported
%   for code generation are allowed.
%   3. Properties of an existing options struct must be fixed-size
%   4. The 'Display' field is ignored by optimization functions in code
%   generation.  Use the 'OutputFcn' instead to print or log intermediate
%   states.
%   5. The Optimization Toolbox fields are not supported for code generation
%   6. If passed a large options struct from the Optimization Toolbox, only the
%   codegen supported fields are returned.

%   Copyright 2013-2017 The MathWorks, Inc.
%#codegen
coder.internal.prefer_const(varargin);

% Ensure we have one output and return defaults
if nargin == 0
    % optimset() syntax
    eml_invariant(nargout > 0, 'Coder:toolbox:OptimsetPrintFieldUnsupported');
    options = parseAndCreateOpts(varargin{:});
elseif nargin == 1
    % optimset(optimfunction) syntax
    fcn = varargin{1};
    eml_invariant(coder.internal.isCharOrScalarString(fcn) || isa(fcn, 'function_handle'), 'MATLAB:optimfun:optimset:NoParamNameOrStruct',1);
    
    % Ensure that the function specified is one supported for code generation when
    % passed a string
    if coder.internal.isCharOrScalarString(fcn)
        switch fcn
            case 'fminbnd'
                options = fminbnd('defaults');
            case 'fminsearch'
                options = fminsearch('defaults');
            case 'fzero'
                options = fzero('defaults');
            case 'lsqnonneg'
                options = lsqnonneg('defaults');
            otherwise
                eml_invariant(false, ...
                    'Coder:toolbox:OptimFunctionUnsupported', ...
                    fcn);
        end
    else
        % We have a function handle. Note we cannot error-check when given a function
        % handle.  Query the function for its defaults
        options = fcn('defaults');
    end
elseif nargin == 2 && isstruct(varargin{1}) && isstruct(varargin{2})
    % optimset(oldopts, newopts)
    oldopts = checkStruct(varargin{1});
    newopts = checkStruct(varargin{2});
    params = optimOptNames();
    
    % Copy fields into the output preferring any non-empty fields from newopts over
    % oldopts
    for k = coder.unroll(0:eml_numfields(params) - 1)
        if isfield(newopts, eml_getfieldname(params, k)) && ~isempty(newopts.(eml_getfieldname(params, k)))
            options.(eml_getfieldname(params, k)) = checkParam(newopts.(eml_getfieldname(params, k)), eml_getfieldname(params, k));
        elseif isfield(oldopts, eml_getfieldname(params, k)) && ~isempty(oldopts.(eml_getfieldname(params, k)))
            options.(eml_getfieldname(params, k)) = checkParam(oldopts.(eml_getfieldname(params, k)), eml_getfieldname(params, k));
        else
            options.(eml_getfieldname(params, k)) = defaultOpt();
        end
    end
else
    % At this point we have param value pairs to parse.
    options = parseAndCreateOpts(varargin{:});
end

%--------------------------------------------------------------------------

function options = parseAndCreateOpts(varargin)
% Parses the param value pairs in varargin and handles updating an existing
% struct if one is passed as the first argument
coder.inline('always');

% Set up parameter struct
params = optimOptNames();
opts = struct( ...
    'CaseSensitivity', false, ...
    'PartialMatching', 'unique');

if nargin > 1 && coder.internal.isCharOrScalarString(varargin{1}) || nargin == 0
    % optimset('PARAM1', VALUE1, ...) or x = optimset()
    %
    % We just need to parse the arguments and construct the corresponding struct
    pstruct = coder.internal.parseParameterInputs(params, opts, varargin{:});
    % Construct the output struct
    options = getParamArgs(pstruct, varargin{:});
elseif nargin > 1 && isstruct(varargin{1})
    % optimset(oldopts, 'PARAM1', VALUE1, ...)
    oldopts = checkStruct(varargin{1});
    pstruct = coder.internal.parseParameterInputs(params, opts, varargin{2:end});
    for k = coder.unroll(0:eml_numfields(pstruct) - 1)
        if paramFound(pstruct, eml_getfieldname(pstruct, k))
            % A value was passed for the parameter
            options.(eml_getfieldname(pstruct, k)) = ...
                getCheckParam(pstruct, eml_getfieldname(pstruct, k), varargin{2:end});
        elseif isfield(oldopts, eml_getfieldname(pstruct, k))
            % Carry the existing value over
            options.(eml_getfieldname(pstruct, k)) = checkParam(oldopts.(eml_getfieldname(pstruct, k)), eml_getfieldname(pstruct, k));
        else
            % Use the default
            options.(eml_getfieldname(pstruct, k)) = defaultOpt();
        end
    end
end

%--------------------------------------------------------------------------

function opts = getParamArgs(params, varargin)
% Takes a params struct obtained from eml_parse_parameter_inputs, error-checks
% the provided values and returns a struct obtained from those values
coder.inline('always');
coder.internal.prefer_const(varargin,params);
for k = coder.unroll(0:eml_numfields(params) - 1)
    opts.(eml_getfieldname(params, k)) = ...
        getCheckParam(params, eml_getfieldname(params, k), varargin{:});
end

%--------------------------------------------------------------------------

function val = getCheckParam(params, name, varargin)
% Takes a params struct and a parameter name.  This error-checks the parameter
% and returns it
coder.inline('always');
coder.internal.prefer_const(name,params);
tVal = coder.internal.getParameterValue(eml_getfield(params, name), defaultOpt, varargin{:});
if ischar(tVal)
    val = lower(deblank(tVal));
elseif isstring(tVal) && isscalar(tVal)
    val = lower(deblank(char(tVal)));
else
    val = tVal;
end
val = checkParam(val, name);

%--------------------------------------------------------------------------

function val = checkParam(val, name)
% Error check the value, val
% Values must be fixed size
eml_invariant(checkFixedSize(val), ...
    'Coder:toolbox:OptimsetgetFixedSizeFields', ...
    name, ...
    'IfNotConst', 'Fail');

if ~isempty(val)                        % empty is always allowed
    % Check various properties of the value
    switch name
        case 'Display'                        % string
            eml_invariant(checkDisplay(val), 'MATLAB:optimfun:optimset:notADisplayType', name);
        case {'MaxIter', 'MaxFunEvals'}       % integer scalar, Inf or special string
            % FMINSEARCH allows the string 200*numberofvariables
            eml_invariant(checkNonNegInt(val,'200*numberofvariables'), ...
                'MATLAB:optimfun:optimoptioncheckfield:nonNegIntegerStringType', ...
                name);
        case 'TolX'                           % real scalar or special string
            % LSQNONNEG uses the string
            eml_invariant(checkNonNegReal(val,'10*eps*norm(c,1)*length(c)'), ...
                'MATLAB:optimfun:optimoptioncheckfield:nonNegRealStringType', ...
                name);
        case 'TolFun'                         % real scalar
            eml_invariant(checkNonNegReal(val), ...
                'MATLAB:optimfun:optimoptioncheckfield:nonNegRealStringType', ...
                name);
            
        case 'FunValCheck'                    % 'on' or 'off'
            eml_invariant(checkOnOff(val), ...
                'MATLAB:optimfun:optimset:notOnOffType', ...
                name);
        case {'OutputFcn', 'PlotFcns'}        % function handle or string
            eml_invariant(checkFcn(val), ...
                'MATLAB:optimfun:optimset:notAFunctionOrCellArray', ...
                name);
            
        otherwise
            eml_invariant(false, 'MATLAB:optimfun:optimset:InvalidParamName', name);
    end
end

%--------------------------------------------------------------------------

function p = checkFcn(v)
% v must be a function handle or a string
coder.inline('always');
coder.internal.prefer_const(v);
p = coder.internal.isCharOrScalarString(v) || isa(v, 'function_handle');

%--------------------------------------------------------------------------

function p = checkOnOff(v)
% v must be 'on' or 'off'
coder.inline('always');
coder.internal.prefer_const(v);
p = coder.internal.isCharOrScalarString(v) && (strcmp(v, 'on') || strcmp(v, 'off'));

%--------------------------------------------------------------------------

function p = checkNonNegReal(v,str)
% v must be a non negative real scalar or the special string str
coder.inline('always');
coder.internal.prefer_const(v);
if nargin == 2 && coder.internal.isCharOrScalarString(v)
    coder.internal.prefer_const(str);
    p = strcmp(v, str);
else
    p = isreal(v) && isscalar(v) && v >= zeros('like',v);
end

%--------------------------------------------------------------------------

function p = checkNonNegInt(v,str)
% v must be a non negative real integer scalar, Inf or the special string str
coder.inline('always');
coder.internal.prefer_const(v);
if nargin == 2 && coder.internal.isCharOrScalarString(v)
    coder.internal.prefer_const(str);
    p = strcmp(v, str);
else
    p = checkNonNegReal(v) && v == floor(v);
end

%--------------------------------------------------------------------------

function p = checkDisplay(v)
% v must be one of the possible display values listed below
coder.inline('always');
coder.internal.prefer_const(v);
if coder.internal.isCharOrScalarString(v)
    switch v
        case {'on','off','none','iter','iter-detailed','final','final-detailed','notify','notify-detailed','testing','simplex'}
            p = true;
        otherwise
            p = false;
    end
else
    p = false;
end

%--------------------------------------------------------------------------

function p = paramFound(params, name)
% Takes a struct returned by eml_parse_parameter_inputs and checks to see if the
% parameter name was specified.
coder.inline('always');
idx = params.(name);
p = idx > zeros('like',idx);

%--------------------------------------------------------------------------

function opt = checkStruct(opt)
% Checks an input struct to verify that it has fixed-size fields.  This allows
% the much-used isempty checks to constant-fold to provide for fields which
% change type.
coder.inline('always');
for k = coder.unroll(0:eml_numfields(opt) - 1)
    eml_invariant(checkFixedSize(opt.(eml_getfieldname(opt,k))), ...
        'Coder:toolbox:OptimsetgetFixedSizeFields', ...
        eml_getfieldname(opt,k), ...
        'IfNotConst', 'Fail');
end

%--------------------------------------------------------------------------

function p = checkFixedSize(v)
coder.inline('always');
p = eml_is_const(size(v));

%--------------------------------------------------------------------------
