function varargout = histc(X, edges, dim)
%HISTC overloaded for codistributed arrays
%   N = HISTC(X,EDGES), for vector X, counts the number of values in X that
%   fall between the elements in the EDGES vector. For a matrix or for N-D
%   array X, the same is done but only along the first non-singleton dimension
%   of X.
%   
%   N = HISTC(X,EDGES,DIM) counts the number of values in X that fall between
%   the elements in the EDGES vector along dimension DIM.
%   
%   [N,BIN] = HISTC(...) returns the bin index that each value of X falls into.
%   
%   Example:
%   spmd
%       N = codistributed(pascal(3));
%       h = histc(N, 1:6)
%   end
%   
%   See also HISTC, CODISTRIBUTED.


%   Copyright 2013-2022 The MathWorks, Inc.

varargout = cell(max(1, nargout));
nOut = max(1, nargout);
% Set default dim if not specified.
if nargin == 2
    dim = 1 + isrow(X);
end
% Gather trailing arguments.
trailingArgs = distributedutil.CodistParser.gatherElements({edges, dim});
[edges, dim] = deal(trailingArgs{:});
if ~iscodistributed(X)
    % Call builtin HISTC if X is not distributed.
    [varargout{1:nOut}] = histc(X, edges, dim);
else
    codistrX = getCodistributor(X);
    % If X is 2dbc codistributed, we redistribute it as in most of the
    % cases, it saves communication afterwards to have it 1d codistributed.
    if isa(codistrX, "codistributor2d")
        X = redistribute(X, codistributor1d());
    end
    localX = getLocalPart(X);
    [varargout{1:nOut}] = distributedutil.syncOnError(@() histc(localX, edges, dim));
    szFirstOutput = iGetFirstOutputGlobalSize(size(X), edges, dim);
    % If dim is not provided (3rd input) and the local first output is a
    % vector then it may have the wrong orientation. But It should always
    % have the same orientation as X so we ensure that.
    if nargin ~= 3 && ...
            isvector(varargout{1}) && ...
            isrow(varargout{1}) ~= isrow(X)
        varargout{1} = varargout{1}.';
    end
    if isequal(szFirstOutput, size(varargout{1}))
        % If varargout already has the correct global size, it means that
        % the local call to HISTC returned truncated counts: some X values
        % are missing. Moreover each worker must have the same-sized
        % varargout{1}.
        % Sum up these partial counts to get the correct one replicated,
        % and then distribute it.
        out = spmdPlus(varargout{1});
        varargout{1} = codistributed(out);
    else
        % If varargout has not the correct global size, it means that the 
        % local call to HISTC returned local counts: each worker owns a
        % local part of the array of counts. Moreover these local parts are
        % distributed in the same fashion as X, except the global sizes may
        % be different.
        varargout{1} = codistributed.pDoBuildFromLocalPart(varargout{1}, ...
            codistributor1d(codistrX.Dimension, codistrX.Partition, szFirstOutput));
    end
    if nargout >= 2
        % Distribute the bins like X.
        varargout{2} = codistributed.pDoBuildFromLocalPart(varargout{2}, ...
            codistrX);
    end
end
end

function sz = iGetFirstOutputGlobalSize(szX, edges, dim)
sz = ones(1, max(dim,numel(szX)));
sz(1:numel(szX)) = szX;
sz(dim) = numel(edges);
end

