function varargout = hGlobalIndicesImpl(dist, dim, workerIdx)
%hGlobalIndicesImpl The implementation of global indices without the error checking.
%
%   See also codistributor2dbc/isComplete, codistributed/globalIndices.

%   Copyright 2009-2023 The MathWorks, Inc.

if dim > 2
    first = 1;
    last = 1;
    [varargout{1:nargout}] = convertToReturnArgs(first, last);
    return;
end

gsize = dist.Cached.GlobalSize;
blksize = dist.BlockSize;
grid = dist.WorkerGrid;

% Calculate the (processRow, processCol) of the worker in the ScaLAPACK process
% grid.  
processRow = dist.pSpmdIndexToProcessorRow(workerIdx);
processCol = dist.pSpmdIndexToProcessorCol(workerIdx);

if dim == 1
    [first, last] = allGlobalIndices(processRow, grid(1), blksize, gsize(1));
else
    [first, last] = allGlobalIndices(processCol, grid(2), blksize, gsize(2));
end
[varargout{1:nargout}] = convertToReturnArgs(first,  last);

end % End of hGlobalIndicesImpl.


%------------
function [first, last] = allGlobalIndices(procID, numProcs, blksize, len)
%[first, last] = allGlobalIndices(process, numProcs, blksize, len) 
%       Calculate the global indices for process number procID out of numProcs.
%       The block size is blksize and the length in the dimension is len.
%       The global indices are returned as the start and the end indices.

% The number of blocks needed to cover all of this dimension.
numBlocks = ceil(len/blksize);

% Which worker row and column own last block.
procWithLastBlock = mod(numBlocks - 1, numProcs) + 1;

% Find the global indices for the worker's local part.
% The worker has the union of the indices first(i):last(i).
blocksOnWorker = (procID:numProcs:numBlocks);
if isempty(blocksOnWorker)
    first = 1;
    last = 0;
else
    first = blksize*(blocksOnWorker - 1) + 1;
    last = first + (blksize - 1);
end

if procWithLastBlock == procID
    last(end) = len;
end

% If we have multiple blocks then check for consecutive indices that can be
% merged so that we return the minimum number of ranges spanning the data.
% e.g. if first = [1 5 11 15]
%         last  = [4 9 14 19]
% Then we can merge ranges 1,2 and 3,4 to give:
%         first = [1 11]
%         last  = [9 19]

% We can merge when last(ii) == first(ii+1)+1, so we we take the difference
% between all such first and last indices and see if that difference is 1.
% If it is NOT 1 then we must keep those first and last, otherwise we can
% discard.
diffIdx = first(2:end)-last(1:end-1);
% First index is always kept.
first = first([true (diffIdx ~= 1)]);
% Last index is always kept.
last = last([(diffIdx ~= 1) true]);

end % End of allGlobalIndices.

%------------
function varargout = convertToReturnArgs(first, last)
%Convert the input arguments into the correct output format for
%globalIndices.

% Simple case - return the starts and ends directly
if nargout == 2
    varargout{1} = first;
    varargout{2} = last;
    return;
end

% Single output case - return all the indices obtained by concatenating
% first(i):last(i). The total number of such indices is stored in len.
len = sum(diff([first; last]) + 1 );
ind = zeros(1, len);
offset = 1;
for i = 1:length(first)
    thisLen = last(i) - first(i) + 1;
    ind(offset: offset + thisLen - 1) = first(i):last(i);
    offset = offset + thisLen;
end
varargout{1} = ind;

end % End of convertToReturnArgs.
