function [dX, dh0, dW, dR, db] = internal_gruBackward(dZ, dH, X, h0, W, R, b, Z, H, ~, stateActivation, gateActivation)
% This function is undocumented and will change in a future release.

%   [dZ,dh0,dW,dR,db] = internal_gruBackward(dZ,dH,X,h0,W,R,b,Z,H,stateActivation,gateActivation) 
%   computes the backward propagation of the Gated Recurrent Unit operation
%   using outputs derivative dZ and DH, input data X, initial hidden units
%   h0, input weights W, recurrent weights R, bias term b, output data Z,
%   hidden state H, and activation functions stateActivation and
%   gateActivation.
%
%   Definitions:
%   nF := Number of features of the input data
%   N := Number of input observations (mini-batch size)
%   nS := Sequence length
%   nH := Hidden units size
%
%   Inputs:
%   X - Input data                          (nF)x(N)x(nS) array
%
%   W - Input weights                       (3*nH)x(nF) matrix
%   R - Recurrent weights                   (3*nH)x(nH) matrix
%   b - Bias                                (3*nH)x(1) vector
%
%   h0 - Initial hidden units               (nH)x(N) matrix
%
%   stateActivation                         'tanh' or 'softsign'
%   gateActivation                          'sigmoid' or 'hard-sigmoid'
%
%   Z - Forward output                      (nH)x(N)x(nS) array
%   H - Hidden state                        (nH)x(N)x(nS) array
%
%   dZ - Next layer derivative              (nH)x(N)x(nS) array
%   dH - Hidden state derivative            (nH)x(N) array
%
%   Outputs:
%   dX - Input data derivative              (nF)x(N)x(nS) array
%   dW - Input weights derivative           (3*nH)x(nF) array
%   dR - Recurrent weights derivative       (3*nH)x(nH) array
%   db - Bias derivative                    (3*nH)x(1) vector
%   dh0 - Initial hidden state derivative   (nH)x(N) array

%   Copyright 2019-2023 The MathWorks, Inc.

if ~isreal(dZ) || ~isreal(dH)
     error(message('deep:dlarray:ComplexGradientOutput', 'gru'));
end

% Prepare the inputs for cnnhost implementation
learnable.W = W;
learnable.R = R;
learnable.b = b;
state.h0    = h0;
outputs.Z   = Z;
outputs.H   = H;
derivatives.dZ = dZ;
derivatives.dH = dH;

options.ReturnLast = false; % Always return the full output
options.GateActivationFunction   = gateActivation;
options.StateActivationFunction  = stateActivation;

% Call cnnhost implementation
[dX, dW, dR, db, dh0] = nnet.internal.cnnhost.gruBackwardGeneral(X,learnable,state,...
                                                             outputs,derivatives,options);
end
