function z = pagemtimes(x, transpX, y, transpY)
%PAGEMTIMES   Page-wise matrix multiply.
%   Z = PAGEMTIMES(X,Y)
%   Z = PAGEMTIMES(X,transpX,Y,transpY)
%
%   The inputs A and B must not be formatted unless one of A or B
%   is unformatted and has scalar pages.
%
%   See also PAGEMTIMES, DLARRAY/MTIMES, DLARRAY.

%   Copyright 2020-2022 The MathWorks, Inc.

if nargin == 2
    y = transpX;
    transpX = 'n';
    transpY = 'n';
elseif nargin == 3
    error(message('MATLAB:pagemtimes:threeInputNotSupported'))
else
    transpX = iProcessTranspFlag(transpX);
    transpY = iProcessTranspFlag(transpY);
end

isXdlarray = isa(x, 'dlarray');
hasDimsX = isXdlarray && ~isempty(x.FormattedData.DimensionMetadata);

isYdlarray = isa(y, 'dlarray');
hasDimsY = isYdlarray && ~isempty(y.FormattedData.DimensionMetadata);

iErrorForLabeledUnlessScalarPages(x, hasDimsX, y, hasDimsY);

scalarPagesX = iScalarPages(x);
scalarPagesY = iScalarPages(y);

if scalarPagesX || scalarPagesY
    
    if transpX ~= 'n' && ~scalarPagesX
        x = permute(x, [2 1 3:ndims(x)]);
    end
    if transpY ~= 'n' && ~scalarPagesY
        y = permute(y, [2 1 3:ndims(y)]);
    end
    % Note: no special treatment for case 'ctranspose' vs. 'transpose',
    % because complex numbers are not supported for dlarray.
    
    z = x.*y;
    return
end

% z will be assigned either x or y in the following
z = [];

% Get data for X and Y, permute explicitly if necessary.
[xdata, z] = iProcessInput(x, z, isXdlarray);
[ydata, z] = iProcessInput(y, z, isYdlarray);

% Apply batch matrix multiplication
ydata = pagemtimes(xdata, transpX, ydata, transpY);

% Insert ydata into y:
z.FormattedData.Data = ydata;

% The new output has no internal permutation.
z.ViewToDataMap = [];
end


function transpFlag = iProcessTranspFlag(transpFlag)
if ~(isstring(transpFlag) && isscalar(transpFlag)) && ~(ischar(transpFlag) && isrow(transpFlag))
    error(message('MATLAB:pagemtimes:invalidOption'))
end

slen = strlength(transpFlag);
if strncmpi(transpFlag, 'transpose', slen)
    transpFlag = 't';
elseif strncmpi(transpFlag, 'ctranspose', slen)
    transpFlag = 'c';
elseif strncmpi(transpFlag, 'none', slen)
    transpFlag = 'n';
else
    error(message('MATLAB:pagemtimes:invalidOption'))
end

end


function [xdata, z] = iProcessInput(x, z, isXdlarray)
if isXdlarray
    x = permuteExplicitly(x);
    xdata = x.FormattedData.Data;
    z = x;
else
    deep.internal.dlarray.validateNonDlarray(x);
    xdata = x;
end
end


function iErrorForLabeledUnlessScalarPages(x, hasDimsX, y, hasDimsY)

if hasDimsX && ~(iScalarPages(y) && ~hasDimsY)
    % x has labels, but y is not an unformatted scalar
    error(message('deep:dlarray:MtimesLabeled'));
end

if hasDimsY && ~(iScalarPages(x) && ~hasDimsX)
    % y has labels, but x is not an unformatted scalar
    error(message('deep:dlarray:MtimesLabeled'));
end
end


function tf = iScalarPages(x)
tf = size(x, 1) == 1 && size(x, 2) == 1;
end
