function layer = concatenationLayer(varargin)
% 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 The MathWorks, Inc.


% Parse the input arguments.
varargin = nnet.internal.cnn.layer.util.gatherParametersToCPU(varargin);
inputArguments = iParseInputArguments(varargin{:});

internalLayer = nnet.internal.cnn.layer.Concatenation(inputArguments.Name, inputArguments.dim, inputArguments.NumInputs);

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

end

function inputArguments = iParseInputArguments(varargin)
p = inputParser;
addRequired(p, 'dim', @iAssertValiddim);
addRequired(p, 'NumInputs', @iAssertValidNumInputs);
addParameter(p, 'Name', '', @nnet.internal.cnn.layer.paramvalidation.validateLayerName);
p.parse(varargin{:});
inputArguments = struct;
inputArguments.Name = convertStringsToChars(p.Results.Name); % make sure strings get converted to char vectors
inputArguments.dim = p.Results.dim;
inputArguments.NumInputs = p.Results.NumInputs;
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