function result = cat(dim, varargin)
%CAT Concatenate arrays.
%   C = CAT(DIM,A,B,...)
%   
%   The data format of all inputs A, B, ... must match, or all inputs must
%   be unformatted. Mixed formatted and unformatted inputs are supported.
%   If any of the inputs is formatted, output C is formatted with the same
%   dimension labels.
%
%   See also CAT, VERTCAT, HORZCAT, DLARRAY.

%   Copyright 2019-2023 The MathWorks, Inc.

if isdlarray(dim)
    % If only dim is a dlarray, we want to dispatch to a different cat
    % method.
    [dim,varargin] = iExtractDimension(dim,varargin);
    result = cat(dim, varargin{:});
    return
end

if isscalar(varargin)
    result = varargin{1};
    return
end

% First dlarray input defines the required format
dimDescResult = '';
for ii=1:length(varargin)
    x = varargin{ii};
    if isa(x, 'dlarray') && hasdims(x)
        dimDescResult = dims(x);
        break;
    end
end

dataArgs = cell(1, length(varargin));
for ii=1:length(varargin)
    x = varargin{ii};
    if isa(x, 'dlarray')
        if hasdims(x) && ~iMatchingFormat(dims(x), dimDescResult)
            error(message('deep:cat:DimsMismatch'));
        end
        x = permuteExplicitly(x);
        [~, xdata] = extractData(x.FormattedData);
        dataArgs{ii} = xdata;
    else
        deep.internal.dlarray.validateNonDlarray(x);
        dataArgs{ii} = x;
    end
end

resultdata = cat(dim, dataArgs{:});

result = dlarray;
result.FormattedData = insertData(result.FormattedData, resultdata);
if ~isempty(dimDescResult)
    if length(dimDescResult) < ndims(resultdata)
        dimDescResult(end+1:ndims(resultdata)) = 'U';
    end
    
    result.FormattedData.DimensionMetadata = dimDescResult;
end
end


function isSame = iMatchingFormat(dim1, dim2)
% Two data formats match if the leading dimension labels are equal, and the
% trailing dimension labels of the larger of the two are all 'U'.

len1 = length(dim1);
len2 = length(dim2);

if len1 == len2
    isSame = isequal(dim1, dim2);
elseif len1 < len2
    isSame = all(dim1 == dim2(1:len1)) && all(dim2(len1+1:end) == 'U');
else
    isSame = all(dim1(1:len2) == dim2) && all(dim1(len2+1:end) == 'U');
end
    
end

function [dim, args] = iExtractDimension(dim, args)
% Extract the underlying data for dim and record assertion if there is a
% dlarray in args.
for index = 1:length(args)
    if isdlarray(args{index})
        [dim, args{index}] = extractUnderlyingConstant(dim, args{index}, {'deep:dlarray:AcceleratedDimensionNotSupported', 'cat'});
        return;
    end
end
% There were no dlarrays in the data arguments, so simply extract the
% underlying dim directly.
dim = extractdata(dim);
end
