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

%   Copyright 2019-2022 The MathWorks, Inc.

if ~isreal(X)
    error(message('deep:dlarray:ComplexNotSupported'))
end

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;

% 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 matrix manually and run the CUDNN with zero padding.
if manualPadding
    X = nnet.internal.cnnhost.padding.padArray(X, padSize);
    padStart(:) = 0;
    padEnd(:) = 0;
end

% CUDNN fails when excluding padding and padding is not symmetric if:
% 1. padStart is zero and poolSize(i) > size(X,i)
% 2. padStart(i) < padEnd(i) and poolSize(i) == padStart(i) + padEnd(i) + size(X,i)
% TODO(g2210344): In these cases, call the CPU builtin.
mustCallCPUBuiltin = ~includePadding && ~symmetric && ...
        (inputSizeLessThanPoolSize || any(poolSize == padStart + padEnd + sizeX));
if mustCallCPUBuiltin
    X = nnet.internal.cnnhost.poolingAverageForwardND(gather(X), poolSize, padStart, padEnd, strideSize, includePadding);
    X = gpuArray(X);
else
    X = nnet.internal.cnngpu.poolingAverageForwardND(X, poolSize, padStart, padEnd, strideSize, includePadding);
end

end

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

