function varargout = ilu(A, varargin)
%ILU  Incomplete LU factorization of codistributed array
%   W = ILU(A)
%   [L,U] = ILU(A)
%   [L,U,P] = ILU(A)
%   [...] = ILU(A,setup)
%   
%   Limitation:
%   The "type" field of struct array "setup", if provided, must be "nofill". 
%       
%   Example:
%   spmd
%       A = codistributed(gallery('neumann', 1600)) + codistributed.speye(1600);
%       setup.type = 'nofill';
%       W = ilu(A, setup);
%   
%   W is "L+U-speye(sizeA))" where L is unit lower triangular and U is
%   upper triangular.
%   
%   See also ILU, CODISTRIBUTED, GALLERY.
%   


%   Copyright 2019-2020 The MathWorks, Inc.

narginchk(1, 2);
nargoutchk(0, 3);
numout = max(1,nargout);
varargout = cell(1,numout);

% Make sure all arguments other than A are gathered.
if nargin > 1
    distributedutil.CodistParser.verifyNonCodistributedInputs([{A}, varargin]);
    varargin = distributedutil.CodistParser.gatherElements(varargin);
end

% If A isn't codistributed, call MATLAB function.
if ~iscodistributed(A)
    % Only the nondata argument was codistributed.
    [varargout{:}] = ilu(A, varargin{:});
    return
end

% Check syntax
setup = iCheckArgs(A, varargin);

codistrA = getCodistributor(A);
codistributed.pVerifyUsing1d("ilu", A);
distDim = codistrA.Dimension;
localA = getLocalPart(A);
localM = cell(1,min(2,numout));
if distDim == 2
    % A is distributed by columns
    if strcmpi(setup.milu, "off") || strcmpi(setup.milu, "col")
        [localM{:}] = codistrA.hIluColWise(localA, setup);
    else
        [localM{:}] = codistrA.hIluColImplLegacy(localA, setup);
    end
else
    % A is distributed by rows
    if strcmpi(setup.milu, "off") || strcmpi(setup.milu, "row")
        [localM{:}] = codistrA.hIluRowWise(localA, setup);
    else
        [localM{:}] = codistrA.hIluRowImplLegacy(localA, setup);
    end
end
varargout{1} = codistributed.pDoBuildFromLocalPart(localM{1}, codistrA);
if numout > 1
    varargout{2} = codistributed.pDoBuildFromLocalPart(localM{2}, codistrA);
    if numout > 2
        % Only setup.type allowed here is 'nofill' so no pivoting
        varargout{3} = codistributed.speye(size(A,1));
    end
end
end

function setup = iCheckArgs(A, argList)
% Check that A is a sparse square matrix and that setup, if given, has
% field values we support
if ~issparse(A)
    ex = MException(message("MATLAB:ilu:FirstArgNotSparse"));
    throwAsCaller(ex);
end
if ~isUnderlyingType(A, "double")
    ex = MException(message("MATLAB:ilu:FirstArgNotDouble"));
    throwAsCaller(ex);
end
[m, n] = size(A);
if m ~= n
    ex = MException(message("MATLAB:square"));
    throwAsCaller(ex);
end
% Use ILU itself on some in-memory data to get free argument parsing!
try
    ilu(speye(1), argList{:});
catch err
    throwAsCaller(err);
end

if ~isempty(argList)
    setup = argList{1};
    if isfield(setup, "type") && ~strcmpi(setup.type, "nofill")
        ex = MException(message("parallel:distributed:IluIcholUnsupportedSetupTypeOption", "ILU"));
        throwAsCaller(ex);
    end
else
    setup = struct('type', 'nofill', 'milu', 'off');
end
end
