function varargout = internal_dlforBackward(bodyInfo,varargin)
% This function is undocumented and will change in a future release.

% Backward method of a for loop operation. All inputs and outputs are numeric.
%
% [drange1,...,drangeM,dx1,...,dxN] = internal_dlforBackward(bodyInfo,dz1,...,dzN,dyCell,range1,...,rangeM,yCell)
%
% Inputs:
%   bodyInfo          = A struct containing information about the body
%                       function.
%   dz1,...,dzN       = Adjoints for outputs from the final iteration of
%                       the for loop.
%   dyCell            = Adjoints for saved variables needed for backward.
%   range1,...,rangeM = Ranges of the for loop.
%   yCell             = Variables needed for backward calculations.
%
% Outputs:
%   drange1,...,drangeM = An empty double [].
%   dx1,...,dxN         = Adjoints for inputs to the first iteration of the
%                         for loop.

%   Copyright 2022 The MathWorks, Inc.

% 1. Number of ranges and outputs from the for loop op.
numRanges = bodyInfo.RangeInfo.NumRanges;
numOpOutputs = bodyInfo.NumBodyOutputs;

% 2. Expand output adjoints (if necessary).
accop = bodyInfo.BodyAccOp;
adjointSizes = accop.AdjointSizes;
varargin(numOpOutputs+1) = []; % intermediate adjoint dyCell
adjointSizes(numOpOutputs+1:end) = [];
varargin(1:numOpOutputs) = deep.internal.recording.expandPlaceholderGradients(varargin(1:numOpOutputs), adjointSizes);

% 3. For loop op output adjoints, ranges and variables needed for backward.
dX = varargin(1:numOpOutputs);
ranges = varargin(numOpOutputs+1:numOpOutputs+numRanges);
opSavedVarsForBackward = varargin{numOpOutputs+numRanges+1};
varargin = []; %#ok<NASGU>

% 4. Body backward function.
bodyBackwardFcnWithConstantsInput = bodyInfo.BodyBackwardFcnWithConstantsInput;
backwardConstants = bodyInfo.BackwardConstants;

% 5. Call the body backward function in reverse order.
prodRangeSizes = iProdRangeSizes(ranges);
emptyAdjoints = cell(1,numRanges);
for k = prodRangeSizes:-1:1
    bodySavedVarsForBackward = opSavedVarsForBackward{k};
    opSavedVarsForBackward{k} = [];
    [emptyAdjoints{:},dX{:}] = bodyBackwardFcnWithConstantsInput(dX{:},bodySavedVarsForBackward{:},backwardConstants);
end

% 6. Return adjoint as [] for ranges along with body input adjoints.
varargout = [emptyAdjoints,dX];
end

function prodRangeSizes = iProdRangeSizes(ranges)
prodRangeSizes = prod(cellfun(@(x) numel(x), ranges));
end