function layer = classificationLayer( varargin )
% classificationLayer   Classification output layer for a neural network
%
%   layer = classificationLayer() creates a classification output layer for
%   a neural network. The classification output layer holds the name of the
%   loss function that is used for training the network, the size of the
%   output, and the class labels.
%
%   layer = classificationLayer('PARAM1', VAL1, 'PARAM2', VAL2, ...)
%   specifies optional parameter name/value pairs for creating the layer:
%
%       'Name'            - A name for the layer. The default is ''.
%
%       'Classes'         - Categories of the output layer, specified as a
%                           string vector, a categorical vector, a cell
%                           array of character vectors, or 'auto'. The
%                           function sets the Classes property of the
%                           output layer. For string or cell array input Y,
%                           the function sets the classes in the order that
%                           they appear in Y. The classes are assumed to be
%                           nonordinal. For categorical input Y, the
%                           function sets the classes in the order
%                           categories(Y). If Y is ordinal, then the
%                           Classes property of the output layer is
%                           ordinal. Otherwise, it is nonordinal. If the
%                           value is 'auto',  the classes are automatically
%                           set during training. Default: 'auto'.
%
%   Example:
%       % Create a classification output layer.
%
%       layer = classificationLayer();
%
%   See also nnet.cnn.layer.ClassificationOutputLayer, softmaxLayer.
%
%   <a href="matlab:helpview('deeplearning','list_of_layers')">List of Deep Learning Layers</a>

%   Copyright 2015-2018 The MathWorks, Inc.

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

% Create an internal representation of a cross entropy layer
internalLayer = nnet.internal.cnn.layer.CrossEntropy( ...
    args.Name, ...
    args.OutputSize);

% Pass the internal layer to a function to construct
layer = nnet.cnn.layer.ClassificationOutputLayer(internalLayer);

% Use class set method to convert to canonical form and set OutputSize too
layer.Classes = args.Classes;

end

function inputArguments = iParseInputArguments(varargin)
parser = iCreateParser();
parser.parse(varargin{:});
inputArguments = iConvertToCanonicalForm(parser);
end

function p = iCreateParser()
p = inputParser;
defaultName = '';
defaultClasses = 'auto';
addParameter(p, 'Name', defaultName, @iAssertValidLayerName);
addParameter(p, 'Classes', defaultClasses, @iAssertValidClasses);
end

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

function iAssertValidClasses(value)
nnet.internal.cnn.layer.paramvalidation.validateClasses(value);
end

function inputArguments = iConvertToCanonicalForm(p)
inputArguments = struct;
inputArguments.OutputSize = [];
inputArguments.Classes = p.Results.Classes;
% make sure strings get converted to char vectors
inputArguments.Name = convertStringsToChars(p.Results.Name); 
end