function [X, H, Ws] = internal_gru(X, h0, W, R, b, stateActivation, gateActivation, isTraining)
% This function is undocumented and will change in a future release.

% The help for this method is similar to the CPU method internal_gru.
%
%   Copyright 2019-2023 The MathWorks, Inc.

if ~isreal(X) || ~isreal(h0) || ~isreal(W) || ~isreal(R) || ~isreal(b)
    error(message('deep:dlarray:ComplexNotSupported'))
end

if nargin < 8
    isTraining = false;
end

if iHasDefaultActivations(stateActivation, gateActivation)
    % Ensure all inputs have the same data type
    [X, h0, W, R, b] = nnet.internal.cnngpu.castToSuperiorFloat(X, h0, W, R, b);

    Wc = nnet.internal.cnngpu.util.GRUWeightsConverter.toCudnn(W, R, b);

    if isTraining
        [X, H, Ws] = nnet.internal.cnngpu.gruForwardTrain(X, h0, Wc);
    else
        [X, H] = nnet.internal.cnngpu.gruForwardPredict(X, h0, Wc);
        Ws = [];
    end

else
    learnable.W = W;
    learnable.R = R;
    learnable.b = b;
    state.h0 = h0;
    options.ReturnLast = false;
    options.StateActivationFunction = stateActivation;
    options.GateActivationFunction = gateActivation;
    [X, H] = nnet.internal.cnngpu.gruForwardGeneral(X, learnable, state, options);
    Ws = [];
end
end

function tf = iHasDefaultActivations(stateActivation, gateActivation)
tf = (stateActivation == "tanh") && (gateActivation == "sigmoid");
end