function y = mtimes(x, y)
%*   Matrix multiply.
%   C = A * B
%   C = MTIMES(A,B)
%
%   In most cases, neither A nor B is formatted. One input argument can be
%   formatted only when the other input is an unformatted scalar. For
%   example, if A is formatted, then B must be an unformatted scalar, and C
%   has the same data format as A.
%
%   Multiplying a dlarray with a non-dlarray sparse matrix is supported
%   only when both inputs are non-scalar.
%
%   See also MTIMES, DLARRAY.

%   Copyright 2019-2022 The MathWorks, Inc.

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

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

errorForLabeledUnlessScalar(x, hasDimsX, y, hasDimsY);

% Get data for X (still need to extract / delete internal copy later on)
if isXdlarray
    xdata = x.FormattedData.Data;
else
    allowSparse = true;
    deep.internal.dlarray.validateNonDlarray(x, allowSparse);
    xdata = x;
end

% Get data for Y (still need to extract / delete internal copy later on)
if isYdlarray
    ydata = y.FormattedData.Data;
else
    allowSparse = true;
    deep.internal.dlarray.validateNonDlarray(y, allowSparse);
    ydata = y;
end

if isscalar(xdata) || isscalar(ydata)
    % Scalar multiplication
    
    % Make x the (unformatted) scalar
    if ~isscalar(x) || hasDimsX
        [x, y] = iSwap(x, y);
        [isXdlarray, isYdlarray] = iSwap(isXdlarray, isYdlarray);  %#ok<ASGLU> % (isXdlarray not used, swap just for clarity)
        [xdata, ydata] = iSwap(xdata, ydata);
    end
    
    % Unformatted scalar x is already extracted into xdata
    
    % Extract y. If y is formatted, keep the labels as they are (no need to
    % change them, since the internal permutation of y doesn't change).
    if isYdlarray
        fdy = extractData(y.FormattedData);
        y.FormattedData = [];
    else
        % If y is not a dlarray, x is a dlarray. Reuse this object to write
        % into, but don't use any of its properties.
        y = x;
        y.ViewToDataMap = [];
        fdy = y.FormattedData;
    end
    
    ydata = xdata * ydata;

    % Output of mtimes with one input a scalar and one input sparse results in
    % a sparse output, which is not supported to store in a dlarray.
    if issparse(ydata)
        error(message('deep:dlarray:ConstructorInvalidSparseInput'));
    end

    y.FormattedData = insertData(fdy, ydata);
    
else
    % Matrix multiplication
    
    % Avoid permutations for better performance: Check if x or y is implicitly
    % transposed.
    isXtransposed = false;
    if isXdlarray
        % Make sure the underlying data is a matrix (contains no
        % singleton dimensions which are trailing in the object x itself)
        [xdata, viewToData] = squeezedata(xdata, x.ViewToDataMap);
        
        if ~ismatrix(xdata) || length(viewToData) > 2
            error(message('MATLAB:mtimes:inputsMustBe2D'));
        end
        
        % Permutation is either empty or the vector [1 2] or [2 1] now
        isXtransposed = ~isempty(viewToData) && viewToData(1) == 2;
    end
    
    isYtransposed = false;
    if isYdlarray
        % Make sure the underlying data is also a matrix (contains no
        % singleton dimensions which are trailing in the object y itself)
        [ydata, viewToData] = squeezedata(ydata, y.ViewToDataMap);
        
        if ~ismatrix(ydata) || length(viewToData) > 2
            error(message('MATLAB:mtimes:inputsMustBe2D'));
        end
        
        % Permutation is either empty or the vector [1 2] or [2 1] now
        isYtransposed = ~isempty(viewToData) && viewToData(1) == 2;
    else
        % Make sure y (used as output) is a dlarray
        y = x;
    end
    
    % Call mtimes and transposed together, which will be optimized to avoid
    % explicitly computing the transpose. This may result in different outputs
    % on the round-off level based on the internal permutation vector.
    if isXtransposed
        if isYtransposed
            ydata = xdata.' * ydata.';
        else
            ydata = xdata.' * ydata;
        end
    else
        if isYtransposed
            ydata = xdata * ydata.';
        else
            ydata = xdata * ydata;
        end
    end
    
    % Insert ydata into y:
    y.FormattedData.Data = ydata;
    
    % The new output has no internal permutation.
    y.ViewToDataMap = [];
end
end


function [y, x] = iSwap(x, y)
end
