function y = cat(dim,varargin)
%MATLAB Code Generation Library Function
%
% Limitations:
%
%   1. Does not support concatenating cell arrays

%   Copyright 1984-2019 The MathWorks, Inc.
%#codegen

coder.internal.allowHalfInputs;
coder.internal.allowEnumInputs;
coder.internal.assert(nargin > 0,'MATLAB:minrhs');
coder.internal.prefer_const(dim);
coder.internal.assertValidDim(dim);
% Deal with a couple of trivial cases.
if nargin == 1
    y = [];
    return
elseif nargin == 2
    coder.internal.assert(~iscell(varargin{1}), ...
        'Coder:toolbox:CellArraysNotSupported', mfilename);
    y = varargin{1};
    return
end
% Handle the ordinary cases.
ONE = ones(coder.internal.indexIntClass);
ZERO = zeros(coder.internal.indexIntClass);
coder.internal.assert(eml_is_const(dim), ...
    'Coder:toolbox:dimNotConst');
dim = coder.const(coder.internal.indexInt(dim));
ND = maxndims(dim,varargin{:});
APPENDING = coder.const((dim == ND) && ~coder.isRowMajor);
isexc = is_exception_case(dim,varargin{:});
% Determine output size.
ysize = ones(1,ND,coder.internal.indexIntClass);
ne = first_ordinary_arg(varargin{:});
ysize_dim = coder.internal.indexInt(size(varargin{ne},dim));
for k = coder.unroll(1:nargin-1)
    if k ~= ne && (isexc || ~is_static_empty(varargin{k}))
        ysize_dim = ysize_dim + coder.internal.indexInt(size(varargin{k},dim));
    end
end
for j = coder.unroll(1:coder.internal.indexInt(numel(ysize)))
    if j == dim
        ysize(j) = ysize_dim;
    else
        ysize(j) = coder.internal.indexInt(size(varargin{ne},j));
    end
end
if eml_ambiguous_types
    y = zeros(ysize);
    return
end
% Preallocate the output with the right type and size.
y = coder.nullcopy(eml_expand(cattype(varargin{:}),ysize));
% Determine whether we can reallocate the first input and then copy
% directly into it.
x1 = varargin{1};
REALLOCFIRST = coder.const( ...
    APPENDING && ...
    ~coder.internal.isConst(size(x1,dim)) && ...
    ND == coder.internal.ndims(x1) && ... % solely workaround for g2056281
    isTypeMatch(x1,y));
if REALLOCFIRST
    % Abandon preallocation of y and replace it with a reallocation of x1.
    y = coder.internal.reallocDynamicMatrix(x1,ysize);
end
% Final consistency checks.
for k = coder.unroll(1:nargin-1)
    coder.internal.assert(isconsistent(dim,y,varargin{k}), ...
        'Coder:MATLAB:catenate_dimensionMismatch');
end
% If the concatenation is not interleaved and if array flattening is OK,
% just write it using linear indexing.
if APPENDING && ~coder.isRowMajor && ~coder.internal.avoidArrayFlattening
    % Data is not interleaved.
    if REALLOCFIRST
        kstart = coder.internal.indexInt(2);
        iy = coder.internal.indexInt(numel(varargin{1}));
    else
        kstart = ONE;
        iy = ZERO;
    end
    coder.unroll;
    for k = kstart:nargin-1
        for j = ONE:numel(varargin{k})
            iy = iy + 1;
            y(iy) = varargin{k}(j);
        end
    end
    return
end
% General case.
if coder.isRowMajor
    startDim = ONE;
else
    startDim = coder.internal.indexInt(eml_ndims(y));
end
y = looper(y,varargin,dim,startDim,ONE,ONE);

%--------------------------------------------------------------------------

function y = looper(y,catArgs,catDim,loopDim,catArgsIdx,argIdx,varargin)
coder.internal.allowEnumInputs;
coder.inline('always');
coder.internal.prefer_const(catDim,loopDim);
ND = coder.internal.indexInt(eml_ndims(y));
ONE = coder.internal.indexInt(1);
ZERO = coder.internal.indexInt(0);
if loopDim == catDim
    % k is the subscript for the catDim-th dimension of y.
    k = ZERO;
    for iarg = coder.unroll(1:length(catArgs))
        if ~is_static_empty(catArgs{iarg})
            % j is the subscript for catDim-th dimension of catArgs{iarg}.
            for j = ONE:size(catArgs{iarg},catDim)
                k = k + 1;
                if coder.isRowMajor
                    y = looper(y,catArgs,catDim,loopDim + 1,iarg,j,varargin{:},k);
                else
                    y = looper(y,catArgs,catDim,loopDim - 1,iarg,j,k,varargin{:});
                end
            end
        end
    end
elseif loopDim >= ONE && loopDim <= ND
    for k = ONE:size(y,loopDim)
        if coder.isRowMajor
            y = looper(y,catArgs,catDim,loopDim + 1,catArgsIdx,argIdx,varargin{:},k);
        else
            y = looper(y,catArgs,catDim,loopDim - 1,catArgsIdx,argIdx,k,varargin{:});
        end
    end
else
    arg = catArgs{catArgsIdx};
    subsarg = varargin;
    subsarg{catDim} = argIdx;
    y(varargin{:}) = arg(subsarg{:});
end

%--------------------------------------------------------------------------

function p = is_exception_case(dim,varargin)
% Returns true if this is the exception case.
coder.internal.allowEnumInputs;
p = false;
if dim < 3
    return
end
for k = coder.unroll(1:nargin-1)
    if size(varargin{k},1) ~= 0 || size(varargin{k},2) ~= 0 || ...
            nonexception_util(size(varargin{k}),dim)
        return
    end
end
p = true;

function p = nonexception_util(sz,dim)
% Return TRUE if a certain condition for being an exception case is
% satisfied.  Returns FALSE otherwise.
coder.internal.allowEnumInputs;
for j = 3:numel(sz)
    if j ~= dim && sz(j) ~= 1
        p = true;
        return
    end
end
p = false;

%--------------------------------------------------------------------------

function ne = first_ordinary_arg(varargin)
% Find the index NE of the first argument such that
% ~ISEQUAL(VARARGIN{NE},[]).  If there is no such argument, the return
% value is 1.
coder.internal.allowEnumInputs;
ne = 1;
for k = coder.unroll(1:nargin)
    if ~is_static_empty(varargin{k})
        ne = k;
        return
    end
end

%--------------------------------------------------------------------------

function nd = maxndims(ndmin,varargin)
% ND = max{NDMIN,ndims(VARARGIN{1}),...,ndims(VARARGIN{end})}
coder.internal.allowEnumInputs;
nd = ndmin;
for k = coder.unroll(1:nargin-1)
    if eml_ndims(varargin{k}) > nd
        nd(1) = eml_ndims(varargin{k});
    end
end

%--------------------------------------------------------------------------

function p = isconsistent(dim,y,x)
coder.internal.allowEnumInputs;
% Check whether Y and X have the same size vectors except in
% dimension DIM.  It is assumed that ndims(y) >= ndims(x).
if is_static_empty(x)
    p = true;
    return
end
for j = 1:eml_ndims(x)
    if j ~= dim && size(y,j) ~= size(x,j)
        p = false;
        return
    end
end
for j = eml_ndims(x)+1:eml_ndims(y)
    if j ~= dim && size(y,j) ~= 1
        p = false;
        return
    end
end
p = true;

%--------------------------------------------------------------------------
% Type inference functions.
%--------------------------------------------------------------------------

function x = get_scalar_enum(varargin)
% Return the first element of the first non-empty enumeration argument.
coder.internal.allowEnumInputs;
for k = coder.unroll(1:nargin)
    if ~isempty(varargin{k})
        x = varargin{k}(1);
        return
    end
end
coder.internal.assert(false, ...
    'Coder:toolbox:cat_UnsupportedEmptyEnumArray');

%--------------------------------------------------------------------------

function cls = dominant_enum_class(varargin)
% Return the class of the first enumeration argument.  Returns an empty
% string '' if there are no enumeration arguments.
coder.internal.allowEnumInputs;
for k = coder.unroll(1:nargin)
    if coder.isenum(varargin{k})
        cls = class(varargin{k});
        return
    end
end
cls = '';

%--------------------------------------------------------------------------

function cls = dominant_integer_class(varargin)
% Return the class of the first integer argument.  Returns an empty
% string '' if there are no integer arguments.
coder.internal.allowEnumInputs;
for k = coder.unroll(1:nargin)
    if isinteger(varargin{k})
        cls = class(varargin{k});
        return
    end
end
cls = '';

%--------------------------------------------------------------------------

function p = all_real(varargin)
% Return true if all inputs are real, false otherwise.
coder.internal.allowEnumInputs;
for k = coder.unroll(1:nargin)
    if ~isreal(varargin{k})
        p = false;
        return
    end
end
p = true;

%--------------------------------------------------------------------------

function [any_char,any_logical,all_logical,any_enum,all_enum,any_integer,dicls] = ...
    cattype_helper(varargin)
% Return information about the inputs. Output dicls is the dominant integer
% class, if any.
coder.internal.allowEnumInputs;
any_char = false;
any_enum = false;
all_enum = true;
any_logical = false;
all_logical = true;
any_integer = false;
decls = dominant_enum_class(varargin{:});
dicls = dominant_integer_class(varargin{:});
for k = coder.unroll(1:nargin)
    coder.internal.assert(~iscell(varargin{k}), ...
        'Coder:toolbox:CellArraysNotSupported',mfilename);
    if ischar(varargin{k})
        any_char = true;
        all_enum = false;
        all_logical = false;
    elseif coder.isenum(varargin{k})
        coder.internal.assert(isa(varargin{k},decls), ...
            'Coder:toolbox:cat_6', ...
            'IfNotConst','Fail');
        any_enum = true;
        all_logical = false;
    elseif islogical(varargin{k})
        any_logical = true;
        all_enum = false;
    elseif isinteger(varargin{k})
        any_integer = true;
        all_enum = false;
        all_logical = false;
    elseif is_static_empty(varargin{k})
        % Skip it.
    else
        all_enum = false;
        all_logical = false;
    end
end

%--------------------------------------------------------------------------

function y = cattype(varargin)
coder.internal.allowEnumInputs;
[any_char,any_logical,all_logical,any_enum,all_enum,any_integer,dicls] = ...
    cattype_helper(varargin{:});
if coder.const(any_char)
    coder.internal.assert(~any_logical, ...
        'Coder:toolbox:cat_8', ...
        'IfNotConst','Fail');
    coder.internal.assert(~any_enum, ...
        'Coder:toolbox:cat_9', ...
        'IfNotConst','Fail');
    y = 'a';
elseif coder.const(any_logical && all_logical)
    y = false;
elseif coder.const(any_enum)
    coder.internal.assert(all_enum, ...
        'Coder:toolbox:cat_10', ...
        'IfNotConst','Fail');
    y = get_scalar_enum(varargin{:});
elseif coder.const(any_integer)
    if coder.const(all_real(varargin{:}))
        y = cast(0,dicls);
    else
        y = complex(cast(0,dicls));
    end
else
    y = coder.internal.scalarEg(varargin{:});
end

%--------------------------------------------------------------------------

function y = is_static_empty(m)
coder.internal.allowEnumInputs;
y = eml_is_const(size(m)) && isequal(m,[]);

%--------------------------------------------------------------------------

function p = isTypeMatch(x,y)
% Determines whether x and y have the same types (except for size).
% Currently this only returns true with built-in numeric types, char,
% logical, and half. Anything else and it returns false even if it is
% really a match.
xOK = coder.internal.isBuiltInNumeric(x) || ischar(x) || ...
    islogical(x) || isa(x,'half');
yOK = coder.internal.isBuiltInNumeric(y) || ischar(y) || ...
    islogical(y) || isa(x,'half');
p = coder.const(xOK && yOK && isa(x,class(y)) && isreal(x) == isreal(y));

%--------------------------------------------------------------------------
