function [funcOut, x, funcName] = processFuncX0(userfunc, x0, IsConstraint)
%PROCESSFUNCX0 Preprocess FUNC and X0 for evaluation
%   Process X0 for lsqcurvefit.
%   Process userfunc to check for Inf and NaN.

%   Copyright 2023 The MathWorks, Inc.

caller = 'checkGradients';

% If X0 is a cell, user is validating function for lsqcurvefit.
% X0 is of the form {X0, XDATA}
funcName = func2str(userfunc);
if iscell(x0)
    if IsConstraint % Constraint does not allow cell array x0
        error(message('optim:checkGradients:CellArrayNotSupportedWithConstraint'));
    end
    x = x0{1};
    XDATA = x0{2};
    funcIn = @(x) objectiveAndJacobian(x, userfunc, XDATA);
else
    % Do nothing for other functions
    x = x0;
    funcIn = userfunc;
end

funcOut = @(x) checkfun(x, funcIn, funcName, caller);
end

function [varargout] = checkfun(x, funcIn, funcName, caller)
    % CHECKFUN checks for Inf or NaN results from userfcn.
    % Inputs FUNCIN and CALLER come from the scope of PROCESSFUNCX0.
    % checkfun will only be called with 1/2/4 outputs
    if nargout == 1
        f = funcIn(x);
        varargout{1} = f;
    elseif nargout == 2 
        % Two output could be f,g (from objective fcn) or [c,ceq]
        [f,g] = funcIn(x);
        varargout{1} = f;
        varargout{2} = g;
    elseif nargout == 4
        % Four outputs are [c,ceq,gc,gceq]
        [c,ceq,gc,gceq] = funcIn(x); 
        varargout{1} = c;
        varargout{2} = ceq;
        varargout{3} = gc;
        varargout{4} = gceq;
    end
    
    for k = 1:nargout
        thisArg = varargout{k};
        if anynan(thisArg)
            error(message('optimlib:optimfcnchk:checkfun:NaNFval', funcName, caller));
        elseif ~allfinite(thisArg)
            error(message('optimlib:optimfcnchk:checkfun:InfFval', funcName, caller));
        end
    end
end

function [fval, jac] = objectiveAndJacobian(x, userfunc, XDATA)
    % Function value and Jacobian returned together
    [fval, jac] = userfunc(x, XDATA);
end

