function [V,D,W] = eig(A,B,varargin)
%MATLAB Code Generation Library Function

%   Limitations:
%
%   1. If there is one input and if that input is Hermitian, SCHUR is used,
%      otherwise the QZ algorithm is used. MATLAB may use different
%      algorithms. Consequently, V may represent a different basis of
%      eigenvectors, and the eigenvalues in D may not be in the same order
%      as in MATLAB. More specifically:
%      * If A is Hermitian, the results from [V,D] = eig(A) will be similar
%        to [V,D] = schur(A,'complex'). Both V and D will be complex.
%      * If A is non-Hermitian, the results from [V,D] = eig(A) will be
%        similar to [V,D] = eig(A,eye(size(A)),'qz'), except that MATLAB
%        Coder normalizes the columns of V.
%
%   2. Options 'balance' and 'nobalance' for the standard eigenvalue
%      problem are only supported for MEX or targets that have LAPACK
%      configured.
%
%   3. Outputs are always of the complex type.
%
%   4. Left eigenvectors are supported for MEX and targets that have LAPACK
%      configured.

%   Copyright 2005-2020 The MathWorks, Inc.
%#codegen

coder.columnMajor;
eml_invariant(nargin > 0,'MATLAB:minrhs');
coder.internal.prefer_const(varargin);
eml_invariant(isa(A,'float'), ...
    'Coder:toolbox:unsupportedClass','eig',class(A));
ISGEN = coder.const(nargin >= 2 && ~coder.internal.isCharOrScalarString(B));
% Parse flag inputs. First count the number of times each flag appears.
if ISGEN || nargin == 1
    [VC,MC,BC,NC,QC,CC,IC] = countFlags(varargin{:});
else
    coder.internal.prefer_const(B); % B is a flag.
    [VC,MC,BC,NC,QC,CC,IC] = countFlags(B,varargin{:});
end
% Assert that the flag inputs are valid and compatible.
assertValidFlags(ISGEN,VC,MC,BC,NC,QC,CC,IC);
% Distill the count results to the logical switches we actually need.
VECTOR = coder.const((nargout <= 1 && ~logical(MC)) || logical(VC));
BALANCE = coder.const(~logical(NC));
QZ = coder.const(logical(QC));
% Finish input validation.
n = coder.internal.indexInt(size(A,1));
if ISGEN
    OUTPUTEG = complex(coder.internal.scalarEg(A,B));
    eml_invariant(isa(B,'float'), ...
        'Coder:toolbox:unsupportedClass','eig',class(B));
    coder.internal.assert( ...
        ismatrix(A) && size(A,1) == size(A,2) && ...
        ismatrix(B) && size(B,1) == size(B,2), ...
        'MATLAB:eig:inputMustBeSquareGeneralized');
    coder.internal.assert(size(B,1) == n, ...
        'MATLAB:eig:inputMustBeSameSizeGeneralized');
else
    OUTPUTEG = complex(zeros('like',A));
    coder.internal.assert(ismatrix(A) && size(A,1) == size(A,2), ...
        'MATLAB:eig:inputMustBeSquareStandard');
end

% Set the output sizes and types.
if nargout >= 2
    V = coder.nullcopy(zeros(n,'like',OUTPUTEG));
    if VECTOR
        D = coder.nullcopy(zeros(n,1,'like',OUTPUTEG));
    else
        D = coder.nullcopy(zeros(n,'like',OUTPUTEG));
    end
    if nargout >= 3
        W = coder.nullcopy(zeros(n,'like',OUTPUTEG));
    end
elseif VECTOR
    V = coder.nullcopy(zeros(n,1,'like',OUTPUTEG));
else
    V = coder.nullcopy(zeros(n,'like',OUTPUTEG));
end

if isempty(A) || (ISGEN && isempty(B))
    % Do nothing.
elseif coder.internal.isAmbiguousTypes
    V(:) = 0;
    if nargout >= 2
        D(:) = 0;
        if nargout >= 3
            W(:) = 0;
        end
    end
elseif coder.internal.anyNonFinite(A) || ...
        (ISGEN && coder.internal.anyNonFinite(B))
    NAN = coder.internal.nan('like',OUTPUTEG);
    if nargout >= 2
        V(:) = NAN;
        if VECTOR
            D(:) = NAN;
        else
            D(:) = 0;
            for k = 1:n
                D(k,k) = NAN;
            end
        end
        if nargout >= 3
            W(:) = NAN;
        end
    elseif VECTOR
        V(:) = NAN;
    else
        V(:) = 0;
        for k = 1:n
            V(k,k) = NAN;
        end
    end
elseif ISGEN && ~QZ && n > 1 && ishermitian(A) && ishermitian(B)
    if nargout >= 3
        [V,D,W] = eigHermitianGeneralized(A,B,VECTOR);
    elseif nargout == 2
        [V,D] = eigHermitianGeneralized(A,B,VECTOR);
    else
        V = eigHermitianGeneralized(A,B,VECTOR);
    end
elseif ISGEN
    if nargout >= 3
        [V,D,W] = eigGeneralized(A,B,VECTOR);
    elseif nargout == 2
        [V,D] = eigGeneralized(A,B,VECTOR);
    else
        V = eigGeneralized(A,B,VECTOR);
    end
elseif ishermitian(A)
    if nargout >= 3
        [V,D,W] = eigHermitianStandard(A,VECTOR,BALANCE);
    elseif nargout == 2
        [V,D] = eigHermitianStandard(A,VECTOR,BALANCE);
    else
        V = eigHermitianStandard(A,VECTOR,BALANCE);
    end
else
    if nargout >= 3
        [V,D,W] = eigStandard(A,VECTOR,BALANCE);
    elseif nargout == 2
        [V,D] = eigStandard(A,VECTOR,BALANCE);
    else
        V = eigStandard(A,VECTOR,BALANCE);
    end
end

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

function [VECTOR,MATRIX,BALANCE,NOBALANCE,QZ,CHOL,INVALID] = countFlags(varargin)
coder.internal.prefer_const(varargin);
ZERO = coder.internal.indexInt(0);
VECTOR = ZERO;
MATRIX = ZERO;
BALANCE = ZERO;
NOBALANCE = ZERO;
QZ = ZERO;
CHOL = ZERO;
INVALID = ZERO;
coder.unroll;
for k = 1:nargin
    coder.internal.assert(coder.internal.isConst(varargin{k}), ...
        'Coder:toolbox:OptionInputsMustBeConstant','eig');
    if coder.const(strcmpi(varargin{k},'vector'))
        VECTOR = VECTOR + 1;
    elseif coder.const(strcmpi(varargin{k},'matrix'))
        MATRIX = MATRIX + 1;
    elseif coder.const(strcmpi(varargin{k},'chol'))
        CHOL = CHOL + 1;
    elseif coder.const(strcmpi(varargin{k},'qz'))
        QZ = QZ + 1;
    elseif coder.const(strcmpi(varargin{k},'balance'))
        BALANCE = BALANCE + 1;
    elseif coder.const(strcmpi(varargin{k},'nobalance'))
        NOBALANCE = NOBALANCE + 1;
    else
        INVALID = INVALID + 1;
    end
end

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

function assertValidFlags(ISGEN,VECTOR,MATRIX,BALANCE,NOBALANCE,QZ,CHOL,INVALID)
coder.internal.prefer_const(ISGEN,VECTOR,MATRIX,BALANCE,NOBALANCE,QZ,CHOL,INVALID)
coder.internal.assert(INVALID == 0, ...
    'MATLAB:eig:unknownArgumentStandard','IfNotConst','Fail');
if ISGEN
    coder.internal.assert( ...
        VECTOR + MATRIX <= 1 && ...
        BALANCE + NOBALANCE == 0 && ...
        QZ + CHOL <= 1 && ...
        INVALID == 0, ...
        'MATLAB:eig:unknownArgumentCombinationGeneralized');
else
    coder.internal.assert( ...
        VECTOR + MATRIX <= 1 && ...
        BALANCE + NOBALANCE <= 1 && ...
        QZ + CHOL == 0 && ...
        INVALID == 0, ...
        'MATLAB:eig:unknownArgumentCombinationStandard');
    if NOBALANCE > 0 && ~coder.internal.isAmbiguousTypes
        coder.internal.assert(coder.internal.lapack.use_lapack(),...
            'Coder:toolbox:EigOptionRequiresLAPACK','nobalance');
    end
end

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