function [funfcn,mtxmpy,flags,sizes,funValCheck,xstart,lb,ub,EXITFLAG,Resnorm,FVAL, ...
    LAMBDA,JACOB,OUTPUT,earlyTermination] = lsqnsetup(FUN,xC,LB,UB,options, ...
    defaultopt,optimgetFlag,caller,callerNargout,lengthVarargin)
%

%LSQNSETUP Helper function for lsqcurvefit and lsqnonlin.
% This function performs setup tasks common to both solvers.

%   Copyright 2008-2022 The MathWorks, Inc.

display = optimget(options,'Display',defaultopt,optimgetFlag);
flags.detailedExitMsg = false;
switch display
    case {'off','none'}
        flags.verbosity = 0;
    case 'iter'
        flags.verbosity = 2;        
    case 'iter-detailed'
        flags.verbosity = 2;
        flags.detailedExitMsg = true;
    case 'final'
        flags.verbosity = 1;
    case'final-detailed'
        flags.verbosity = 1;
        flags.detailedExitMsg = true;
    case 'testing'
        flags.verbosity = Inf;
    otherwise
        flags.verbosity = 1;
end

funValCheck = strcmp(optimget(options,'FunValCheck',defaultopt,optimgetFlag),'on');
flags.grad =  strcmp(optimget(options,'Jacobian',defaultopt,optimgetFlag),'on');

if callerNargout > 5
   flags.computeLambda = true;
else 
   flags.computeLambda = false;
end

% if we have a string object input, we need to convert to char arrays
if isstring(FUN)
    if isscalar(FUN)
        FUN = char(FUN);
    else
        FUN = cellstr(FUN);
    end
end

% Process objective function
if ~isempty(FUN)  % will detect empty string, empty matrix, empty cell array
    funfcn = lsqfcnchk(FUN,caller,lengthVarargin,funValCheck,flags.grad);
else
    if strcmp(caller,'lsqnonlin')
        mexcptn = MException('optimlib:lsqnonlin:InvalidFunc', ...
            getString(message('optimlib:lsqnonlin:InvalidFunc')));
    else
        mexcptn = MException('optimlib:lsqcurvefit:InvalidFunc', ...
            getString(message('optimlib:lsqcurvefit:InvalidFunc')));
    end
    throwAsCaller(mexcptn);
end

% Process function JacobMult
mtxmpy = optimget(options,'JacobMult',defaultopt,optimgetFlag); 
% Use internal Jacobian-multiply function if user does not provide JacobMult function 
% or options.Jacobian is off
if isempty(mtxmpy) || (~strcmpi(funfcn{1},'fungrad') && ~strcmpi(funfcn{1},'fun_then_grad'))
    mtxmpy = @atamult;
else
    % Check if name clash
    functionNameClashCheck('JacobMult',mtxmpy,'atamult','optimlib:lsqnsetup:JacobMultNameClash');
end

sizes.nVar = numel(xC);

% Check for empty X
if sizes.nVar == 0
   error('optimlib:lsqnsetup:EmptyX',getString(message('optimlib:fmincon:EmptyX')));
end

[xstart,lb,ub,msg] = checkbounds(xC,LB,UB,sizes.nVar);
if ~isempty(msg)
    EXITFLAG = -2;
    [Resnorm,FVAL,LAMBDA,JACOB] = deal([]);
    OUTPUT.firstorderopt = [];
    OUTPUT.iterations = 0;
    OUTPUT.funcCount = 0;
    OUTPUT.constrviolation = [];
    OUTPUT.cgiterations = [];
    OUTPUT.algorithm = ''; % Not known at this stage
    OUTPUT.message = msg;
    OUTPUT.bestfeasible = [];
    if flags.verbosity > 0
        disp(msg)
    end
    earlyTermination = true; % exit this helper function and return
else
    % All outputs need to be assigned to a value
    Resnorm = []; FVAL = []; LAMBDA = []; JACOB = []; OUTPUT = [];
    EXITFLAG = []; earlyTermination = false;
    
    xinitOutOfBounds_idx = xstart < lb | xstart > ub;
    if any(xinitOutOfBounds_idx)
        if any(strcmp(options.Algorithm,'levenberg-marquardt'))
            xstart = projectBox(zeros(sizes.nVar,1),xstart,lb,ub);
            warning(message('optimlib:levenbergMarquardt:InfeasibleX0'));
        else
            % If components of initial x not within bounds, set those components
            % of initial point to a "box-centered" point
            xstart = startx(ub,lb,xstart,xinitOutOfBounds_idx);
        end
    end
        
end


