function varargout = svd(varargin)
%SVD Singular value decomposition of codistributed array
%   S = SVD(A)
%   [U,S,V] = SVD(A)
%   [...] = SVD(A,"econ")
%   [...] = SVD(...,"vector")
%   [...] = SVD(...,"matrix")
%   
%   Limitations:
%   1. If A is rectangular, you must specify the "econ" option.
%   
%   See also SVD, CODISTRIBUTED, CODISTRIBUTED/RAND.


%   Copyright 2006-2022 The MathWorks, Inc.

narginchk(1, 3);
nargoutchk(0, 3);

distributedutil.CodistParser.verifyNonCodistributedInputs(varargin);

A = varargin{1};
argList = distributedutil.CodistParser.gatherElements(varargin(2:end));
if ~isa(A, 'codistributed')
    [varargout{1:nargout}] = svd(A, argList{:});
    return;
end

if ~ismatrix(A)
    error(message('MATLAB:svd:inputMustBe2D'));
elseif issparse(A)
    error(message('MATLAB:svd:noSVDsparse'));
elseif ~isfloat(A)
    error(message('MATLAB:svd:inputType'));
end

isvec = iCheckFlags(argList, (size(A,1) ~= size(A,2)), nargout);

% Input contains Infs or NaNs, special case not handled by scalaSvd
if hasNaN(A) || hasInf(A)
    % We already made sure A is float so output type is underlyingType
    [varargout{1:nargout}] = eigSvdInfOrNaN(isvec, underlyingType(A), getCodistributor(A));
    return;
end

[varargout{1:nargout}] = scalaSvd(A, isvec);
end

function isvec = iCheckFlags(flags, isARectangular, numOut)
% Check flags and errors out accordingly, and extract vector/matrix output
% form type. At most 2 flags are being passed here.

% Call "in memory" MATLAB to detect incorrect flag combinations.
try
    svd([], flags{:});
catch ME
    rethrow(ME);
end

% The "in memory" MATLAB will allow the first flag to be the zero economy
% argument, which isn't supported here.  Perform an additional check to
% protect against that here.
if ~isempty(flags) && ~matlab.internal.datatypes.isScalarText(flags{1})
    error(message('MATLAB:svd:invalidOption'));
end

% All flags now should be scalar text
assert(all(cellfun(@(x) matlab.internal.datatypes.isScalarText(x), flags)), ...
    "All flags should be scalar text");

% 'econ' must be provided for rectangular matrices, full SVD is not
% supported. Note that 'econ' makes no difference to the 1-output case so
% we ignore that.
isEcon = iIsFlagSpecified("econ", flags);
if (numOut > 1) && isARectangular && ~isEcon
    error(message('parallel:distributed:SvdOnlyEconomySvd'));
end

% Default for S is vector for less than 2 outputs, matrix othwerwise.
isvec = (numOut < 2);
if iIsFlagSpecified("vector", flags)
    isvec = true;
end
if iIsFlagSpecified("matrix", flags)
    isvec = false;
end
end

function tf = iIsFlagSpecified(flagName, flags)
tf = any(cellfun(@(x) startsWith(flagName, x, 'IgnoreCase', true), flags));
end
