function dZ = internal_avgpoolBackward(dZ, X, Z, poolSize, padSize, strideSize, includePadding)
% This function is undocumented and will change in a future release.

%   Copyright 2019-2021 The MathWorks, Inc.

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

% Ensure all inputs have the same data type
[dZ, X, Z] = nnet.internal.cnngpu.castToSuperiorFloat(dZ, X, Z);

padStart = padSize(1:2:end);
padEnd = padSize(2:2:end);
sizeX = size(X,1:length(poolSize));
inputSizeLessThanPoolSize = iIsInputSizeLessThanPoolSize(sizeX, poolSize);
symmetric = all(padStart == padEnd); %symmetric PADDING
manualPadding = (~symmetric || inputSizeLessThanPoolSize) && includePadding;

if manualPadding
    % CUDNN fails when including padding if the padding is not symmetric
    % or if the pooling size is larger than the input. In that case, we pad
    % the input manually and run the cuDNN with zero padding.
    X = nnet.internal.cnnhost.padding.padArray(X, padSize);
    padStart(:) = 0;
    padEnd(:) = 0;
    dZ = nnet.internal.cnngpu.poolingAverageBackwardND(Z, dZ, X, poolSize, padStart, padEnd, strideSize, includePadding);
    dZ = nnet.internal.cnnhost.padding.unpadArray(dZ, padSize);
else
    % CUDNN fails when excluding padding and padding is asymmetric if:
    % 1. padStart is zero and poolSize(i) > size(X,i)
    % 2. padStart < padEnd and poolSize == padStart + padEnd + size(X)
    % TODO(g2210344): In these cases, call the CPU builtin.
    mustCallCPUBuiltin = ~includePadding && ~symmetric && ...
        (inputSizeLessThanPoolSize || any(poolSize == padStart + padEnd + sizeX));
    if mustCallCPUBuiltin
        dZ = nnet.internal.cnnhost.poolingAverageBackwardND(gather(Z), gather(dZ), gather(X), poolSize, padStart, padEnd, strideSize, includePadding);
        dZ = gpuArray(dZ);
    else
        dZ = nnet.internal.cnngpu.poolingAverageBackwardND(Z, dZ, X, poolSize, padStart, padEnd, strideSize, includePadding);
    end
end

end

function tf = iIsInputSizeLessThanPoolSize(sizeX, poolSize)
tf = any(sizeX < poolSize);
end

