function layer = concatenationLayer(dim,numInputs,nameValueArgs)
% concatenationLayer   Concatenation layer
%
%   layer = concatenationLayer(dim,numInputs) creates a concatenation
%   layer. This layer takes multiple inputs that have same sizes in all
%   dimensions except the concatenation dimension dim. The number of inputs
%   for the layer is determined by numInputs.
%
%   layer = concatenationLayer(dim,numInputs,'PARAM1', VAL1) specifies
%   optional parameter name/value pairs for creating the layer:
%
%       'Name'                    - A name for the layer. The default is
%                                   ''.
%
%   Concatenation layer has the following inputs:
%       'in1','in2',...,'inN'   - Inputs to be concatenated. Note that all
%                                 of the inputs must have the same
%                                 dimensions except along the concatenation
%                                 axis dimension specified by dim. See the
%                                 example below for usage.
%
%   Example:
%       % Create a concatenation layer with two inputs that concatenates 5D
%       % array output from two ReLU layers along the 4th dimension
%       % (channel).
%
%       concat_1 = concatenationLayer(4,2,'Name','concat_1');
%       relu_1 = reluLayer('Name','relu_1');
%       relu_2 = reluLayer('Name','relu_2');
%
%       lgraph = layerGraph();
%       lgraph = addLayers(lgraph, relu_1);
%       lgraph = addLayers(lgraph, relu_2);
%       lgraph = addLayers(lgraph, concat_1);
%
%       lgraph = connectLayers(lgraph, 'relu_1', 'concat_1/in1');
%       lgraph = connectLayers(lgraph, 'relu_2', 'concat_1/in2');
%
%       plot(lgraph);
%
%   See also nnet.cnn.layer.ConcatenationLayer, convolution3dLayer,
%   reluLayer.
%
%   <a href="matlab:helpview('deeplearning','list_of_layers')">List of Deep Learning Layers</a>

%   Copyright 2018-2023 The MathWorks, Inc.

arguments
    dim                  {iAssertValiddim}
    numInputs            {iAssertValidNumInputs}
    nameValueArgs.Name   {iAssertValidLayerName} = ""
end

% Gather inputs to CPU.
dim = nnet.internal.cnn.layer.util.gatherParametersToCPU(dim);
numInputs = nnet.internal.cnn.layer.util.gatherParametersToCPU(numInputs);
nameValueArgs = nnet.internal.cnn.layer.util.gatherParametersToCPU(nameValueArgs);

% Canonicalization.
numInputs = iConvertToDouble(numInputs);
dim = iConvertToDouble(dim);
nameValueArgs.Name = convertStringsToChars(nameValueArgs.Name);

% Create an internal representation of a concatenation layer.
internalLayer = nnet.internal.cnn.layer.Concatenation(nameValueArgs.Name, dim, numInputs);

% Pass the internal layer to a function to construct a user visible
% concatenation layer.
layer = nnet.cnn.layer.ConcatenationLayer(internalLayer);

end

function iAssertValiddim(value)
validateattributes(value, {'numeric'}, ...
    {'positive', 'real', 'integer', 'nonempty', 'scalar'});
end

function iAssertValidNumInputs(value)
validateattributes(value, {'numeric'}, ...
    {'positive', 'real', 'integer', 'nonempty', 'scalar','>',1});
end

function iAssertValidLayerName(name)
nnet.internal.cnn.layer.paramvalidation.validateLayerName(name)
end

function value = iConvertToDouble(value)
value = nnet.internal.cnn.layer.util.convertToDouble(value);
end
