function layer = flattenLayer(varargin)
% flattenLayer   flatten the spatial dimensions of a tensor into the channel dimension
%
% The flatten layer flattens the spatial dimensions of the input into a
% single channel. The layer preserves the observation dimension (N) and the
% sequence dimension (S) after flattening. For example:
%
%   Description         | Size                        | Flattened size
%   ----------------------------------------------------------------------
%   Vector sequences    | C x N x S                   | C x N x S
%   Image sequences     | H x W x C x N x S           | (H*W*C) x N x S
%   3-D image sequences | H x W x D x C x N x S       | (H*W*D*C) x N x S
%
%   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.FlattenLayer, sequenceFoldingLayer,
%   sequenceUnfoldingLayer, lstmLayer.
%
%   <a href="matlab:helpview('deeplearning','list_of_layers')">List of Deep Learning Layers</a>

%   Copyright 2018-2019 The MathWorks, Inc.

% Parse the input arguments.
inputArguments = iParseInputArguments(varargin{:});

% Create an internal representation of a flatten layer.
internalLayer = nnet.internal.cnn.layer.Flatten(inputArguments.Name, []);

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

end

function inputArguments = iParseInputArguments(varargin)
parser = iCreateParser();
parser.parse(varargin{:});
% Convert to canonical form
inputArguments.Name = convertStringsToChars(parser.Results.Name);
end

function p = iCreateParser()
p = inputParser;

defaultName = '';

addParameter(p, 'Name', defaultName, @iAssertValidLayerName);
end

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