function dX = internal_parenReferenceBackward(dZ,ShapeX,varargin)
% This function is undocumented and will change in a future release.

% Backward method for ParenReferenceOp.
%
% Z = parenReference of X using subscripts in Index.
%
% Inputs:
%   dZ     = gradient of loss w.r.t Z
%   ShapeX = size of X
%   Index  = subscripts used to index into X
%
% Outputs:
%   dX = gradient of loss w.r.t X

%   Copyright 2019 The MathWorks, Inc.

Index = varargin;
subscripts = iReplaceColonAndRemoveLogical(Index,dZ);
isLinearIndex = isscalar(Index);
if isLinearIndex
    numX = prod(ShapeX);
    dX = accumarray(subscripts{1}(:),dZ(:),[numX 1]);
    dX = reshape(dX,ShapeX);
else % length(Index) > 1, empty case does not get here.
    linSubscripts = cell(1,numel(subscripts));
    [linSubscripts{:}] = ndgrid(subscripts{:});
    for k = 1:numel(linSubscripts)
        linSubscripts{k} = linSubscripts{k}(:);
    end
    shapeX = iAdjustSizeVector(ShapeX,numel(linSubscripts));
    dX = accumarray(linSubscripts,dZ(:),shapeX);
    dX = reshape(dX, ShapeX);
end
end

function subs = iReplaceColonAndRemoveLogical(subs,dZ)
numSubs = numel(subs);
sizedZ = size(dZ);
sizedZ = iAdjustSizeVector(sizedZ,numSubs);
tf = strcmp(':',subs);
for i = 1:numSubs
    if tf(i)
        subs{i} = 1:sizedZ(i);
    else
        sub = subs{i};
        if isobject(sub)
            % subsindex returns a numeric, 0-based array, never a logical.
            sub = subsindex(sub)+1;
        end
        if islogical(sub)
            sub = find(sub);
        end
        subs{i} = sub;
    end
end
end

function sizeVector = iAdjustSizeVector(sizeVector,outputLength)
% Returns new sizeVector of length outputLength.
N = numel(sizeVector);
if outputLength > N
    % Pad with trailing singleton dimensions
    sizeVector(end+1:outputLength) = 1;
elseif outputLength < N
    % Implicitly reshape the matrix so that the last dimension contains all remaining sizes
    sizeVector = [sizeVector(1:outputLength-1) prod(sizeVector(outputLength:end))];
end
end