function y = iterapp(op, afun, atype, x, varargin)
%ITERAPP   Apply matrix operator to vector and error gracefully.
%   ITERAPP(OP,AFUN,ATYPE,X) applies matrix operator AFUN to vector
%   X. If ATYPE is 'matrix, then AFUN is a matrix and the OP is applied
%   directly. OP is either 'mtimes' or 'mldivide'.
%   ATYPE and AFCNSTR are used in case of error.
%   ITERAPP(OP,AFUN,ATYPE,X,P1,P2,...) allows extra arguments to
%   AFUN(X,P1,P2,...) although this usage is now discouraged in favor of
%   using anonymous functions.
%   AFUN(X,P1,P2,...,PN,TFLAG) should accept a TFLAG as its final input if
%   the calling function is BICG, LSQR or QMR. TFLAG is either 'transp' or
%   'notransp' depending on whether A' OP X or A OP X is required.  For
%   matrices, the flag is unnecessary, because the transposed version of
%   afun is presented when it is required.
%   ITERAPP is designed for use by iterative methods like PCG which
%   require matrix operators AFUN representing matrices A to operate on
%   vectors X and return A*X and may also have operators MFUN representing
%   preconditioning matrices M operate on vectors X and return M\X.
%
%   See also BICG, BICGSTAB, CGS, GMRES, LSQR, MINRES, PCG, QMR, SYMMLQ.

%   Copyright 1984-2022 The MathWorks, Inc.

if strcmp(atype,'matrix')
    switch lower(op)
        case 'mtimes'
            y = afun * x;
        case 'mldividewithrcondcheck'
            % Directly call mldivide which also calculates the reciprocal
            % condition number and warns for ill-conditioned inputs.
            y = afun\x;
        case 'mldivide'
            % Directly call hMldivideAndMrdivide with 1 output to avoid
            % calculation of reciprocal condition number (2nd output).
            isMrdivide = false;
            y = hMldivideAndMrdivide(...
                distributedutil.TransposeFlags.NO_TRANSPOSE, afun, x, isMrdivide);
        otherwise
            error(message('MATLAB:iterapp:InvalidOp'))
    end
else
    try
        y = afun(x, varargin{:});
    catch ME
        if numel(varargin)+1 > nargin(afun) && nargin(afun) >= 0
            error(message('MATLAB:iterapp:TooManyInputs', numel(varargin)+1))
        else
            throwAsCaller(addCause(MException(message('MATLAB:iterapp:InvalidInput')),ME))
        end
    end

    if ~iscolumn(y)
        error(message('MATLAB:iterapp:MustReturnColumn'));
    end
end

