function varargout = qr(varargin)
%QR Orthogonal-triangular decomposition for codistributed matrix
%   [Q,R] = QR(A)
%   [Q,R] = QR(A,"econ")
%   [Q,R] = QR(A,0)
%     Note: This syntax is not recommended. Use the "econ" and "vector"
%           options instead.
%   [Q,R,P] = QR(A)
%   [Q,R,P] = QR(A,"econ")
%   [Q,R,P] = QR(A,0)
%     Note: This syntax is not recommended. Use the "econ" and "vector"
%           options instead.
%   [Q,R,P] = QR(...,"vector")
%   [Q,R,P] = QR(...,"matrix")
%   
%   Limitations:
%   1. A must be a full codistributed matrix of floating point numbers (single or
%   double). Sparse arrays are not supported.
%   
%   2. The following syntax is not supported:
%       X = QR(A)
%   
%   See also QR, CODISTRIBUTED.


%   Copyright 2010-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}] = qr(A, argList{:});
        return;
    end

    if ~isfloat(A)
        error(message("MATLAB:qr:inputType"));
    elseif ~ismatrix(A)
        error(message("MATLAB:qr:inputMustBe2D"));
    end

    if issparse(A)
        error(message("parallel:distributed:QrUnsupportedSparse"));
    end

    if nargout < 2
        error(message("parallel:distributed:QrTooFewOutputs"));
    end

    % Parse and canonicalize optional flags.
    argList = iParseAndCanonicalizeFlags(argList);

    if isempty(A)
        [varargout{1:nargout}] = iQrEmpty(A, argList{:});
    else
        [varargout{1:nargout}] = scalaQr(A, argList{:});
    end
end

function varargout = iQrEmpty(A, varargin)
%iQrEmpty Produce appropriate outputs in case input was empty
%   Inputs: A - the input matrix
%           flag - flag that was passed or "matrix" if none
%   Output: Q, R, (E)
    distA = getCodistributor(A);
    typeA = underlyingType(A);
    [mA, nA] = size(A);
    if nA == 0 && ismember('econ', varargin)
        innerDim = 0;
    else
        innerDim = mA;
    end
    codistr = distA.hGetNewForSize([mA, innerDim]);
    varargout{1} = eye(mA, innerDim, typeA, codistr);
    codistr = distA.hGetNewForSize([innerDim, nA]);
    varargout{2} = zeros(innerDim, nA, typeA, codistr);
    if nargout == 3  % want pivots
        if ismember('matrix', varargin)
            codistr = distA.hGetNewForSize([nA, nA]);
            varargout{3} = eye(nA, typeA, codistr);
        else
            codistr = distA.hGetNewForSize([1, nA]);
            varargout{3} = colon(1, nA, codistr);
        end
    end
end

function argList = iParseAndCanonicalizeFlags(argList)
% Parse and canonicalize the flags, and error accordingly if something is wrong.

econFlag = [];
zeroFlag = [];
vectorFlag = [];
matrixFlag = [];

% Early exit if no flag passed, default is "matrix".
if isempty(argList)
    argList{1} = 'matrix';
    return
end

for ii = 1:numel(argList)
    if (isnumeric(argList{ii}) && isscalar(argList{ii}) && argList{ii} == 0) % old syntax for economic form
        if ~isempty(econFlag)
            error(message("MATLAB:qr:repeatedEcon"));
        elseif  ~isempty(zeroFlag)
            % Mimics in-memory behavior.
            error(message("MATLAB:qr:notSupportFill"));
        end
        argList{ii} = 'econ';
        zeroFlag = true;
    elseif isScalarString(argList{ii})
        if startsWith('econ', argList{ii}, 'IgnoreCase', true)
            if ~isempty(econFlag) || ~isempty(zeroFlag)
                error(message("MATLAB:qr:repeatedEcon"));
            end
            argList{ii} = 'econ';
            econFlag = true;
        elseif startsWith('matrix', argList{ii}, 'IgnoreCase', true)
            if ~isempty(vectorFlag) || ~isempty(matrixFlag)
                error(message("MATLAB:qr:repeatedOutputForm"));
            end
            argList{ii} = 'matrix';
            matrixFlag = true;
        elseif startsWith('vector', argList{ii}, 'IgnoreCase', true)
            if ~isempty(vectorFlag) || ~isempty(matrixFlag)
                error(message("MATLAB:qr:repeatedOutputForm"));
            end
            argList{ii} = 'vector';
            vectorFlag = true;
        else
            error(message("MATLAB:qr:unknownOption"));
        end
    elseif ii == 1 && isnumeric(argList{ii})
        % Special syntax, only for sparse.
        error(message("MATLAB:qr:notSupportFill"));
    else
        error(message("MATLAB:qr:unknownOption"));
    end
end

% 0 flag is not supported with other flags.
if ~isempty(zeroFlag) && zeroFlag && numel(argList) == 2
    error(message("MATLAB:qr:noCombiningZero"));
end

% Default output is 'matrix', but 'vector' for 0
if ~isempty(zeroFlag)
    argList{end+1} = 'vector';
elseif isempty(vectorFlag) && isempty(matrixFlag)
    argList{end+1} = 'matrix';
end

end