function layer = sequenceFoldingLayer( nameValueArgs )
% sequenceFoldingLayer   Sequence folding layer
%
%   layer = sequenceFoldingLayer creates a layer that combines together the
%   observation and sequence dimensions of its input.
%
%   layer = sequenceFoldingLayer('Name',name) creates a sequence folding
%   layer and specifies the layer name:
%
%       'Name'                    - A name for the layer. The default is
%                                   ''.
%
%   A sequence folding layer combines the observation dimension (N) and the
%   sequence dimension (S) of the input. The layer does not affect inputs
%   with no sequence dimension or sequence dimension 1. For example:
%
%   Description         | Size                        | Folded size
%   ----------------------------------------------------------------------
%   Vector sequences    | C x N x S                   | C x (N*S)
%   Image sequences     | H x W x C x N x S           | H x W x C x (N*S)
%   Images              | H x W x C x N               | H x W x C x N      (No effect)
%   3-D images          | H x W x D x C x N           | H x W x D x C x N  (No effect)
%
%   A sequence folding layer has two outputs. The outputs can be connected
%   to other layers using connectLayers.
%
%   sequenceFoldingLayer outputs:
%       'out'           - Output feature map corresponding to reshaped
%                         input.
%       'miniBatchSize' - Size of the mini-batch passed into the layer.
%                         This output must be connected to the
%                         'miniBatchSize' input of a sequence unfolding
%                         layer. 'miniBatchSize' corresponds to (N) in the
%                         description table above.
%
%   Example:
%       % Apply 2D convolution to sequences of images.
%
%       % Create layer array
%       layers = [ ...
%           sequenceInputLayer([28 28 1], 'Name', 'seqinput')
%           sequenceFoldingLayer('Name', 'seqfold')
%           convolution2dLayer(5, 20, 'Name', 'conv')
%           reluLayer('Name', 'relu')
%           maxPooling2dLayer(2, 'Stride', 2, 'Name', 'maxpool')
%           sequenceUnfoldingLayer('Name', 'sequnfold')
%           flattenLayer('Name', 'flatten')
%           lstmLayer(128, 'OutputMode', 'last', 'Name', 'lstm')
%           fullyConnectedLayer(2, 'Name', 'fc')
%           softmaxLayer('Name', 'softmax')
%           classificationLayer('Name', 'classout') ];
%
%       % Convert layers to a layer graph. 
%       lgraph = layerGraph(layers)
% 
%       % Connect sequence folding and unfolding layers.
%       lgraph = connectLayers(lgraph, 'seqfold/miniBatchSize', 'sequnfold/miniBatchSize');
%
%   See also nnet.cnn.layer.SequenceFoldingLayer, sequenceUnfoldingLayer.
%
%   <a href="matlab:helpview('deeplearning','list_of_layers')">List of Deep Learning Layers</a>

%   Copyright 2018-2021 The MathWorks, Inc.

arguments
    nameValueArgs.Name {iAssertValidLayerName} = '';
end
nameValueArgs.Name = convertStringsToChars(nameValueArgs.Name);

% Create an internal representation of a folding layer.
internalLayer = nnet.internal.cnn.layer.SequenceFolding( ...
    nameValueArgs.Name, [] );

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

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