function [n,edges,bin] = histcounts(x,varargin)
%MATLAB Code Generation Library Function

%   Limitations:
%   1. For code generation, HISTCOUNTS does not permit contradictory
%      options. If the same option is supplied more than once, the last
%      option value chosen is used, but contradictory options will result
%      in errors.
%   2. Unless edges are supplied, HISTCOUNTS usually requires
%      variable-sizing, often dynamic memory allocation. This may affect
%      use in a MATLAB Function Block in Simulink.
%   3. Sparse inputs are not supported. Partial coding for sparse inputs
%      has been done in some places. Note that there are limitations with
%      the current approach:
%      * The BIN output is will not be supported with sparse x.
%        Use [...] = histcounts(full(x),...).
%      * The bin method FD will not be supported with sparse input until
%        coder.internal.quickselect works with sparse input.

%   Copyright 1984-2019 The MathWorks,Inc.
%#codegen

coder.internal.prefer_const(varargin);
validateattributes(x,{'numeric','logical'},{'real'},mfilename,'x',1)
coder.internal.assert(~issparse(x), ...
    'Coder:toolbox:SparseNotSupported',mfilename);
[ ...
    BinEdges,BinEdgesSupplied, ...
    BinLimits,BinLimitsSupplied, ...
    BinMethod,~, ...
    BinWidth,BinWidthSupplied, ...
    Normalization,NormalizationSupplied, ...
    NumBins,NumBinsSupplied] = parseinput(varargin{:}); %#ok<ASGLU>
% Parse again for a better chance at constant-folding the indicator
% variables.
[ ...
    BinEdgesSupplied, ...
    BinLimitsSupplied, ...
    BinWidthSupplied, ...
    NormalizationSupplied, ...
    NumBinsSupplied] = optionsSupplied(varargin{:});
if nargout == 3
    [ni,edges,bini] = HistCounts(x, ...
        NumBins,NumBinsSupplied, ...
        BinEdges,BinEdgesSupplied, ...
        BinLimits,BinLimitsSupplied, ...
        BinWidth,BinWidthSupplied, ...
        BinMethod);
    bin = double(bini);
else
    [ni,edges] = HistCounts(x, ...
        NumBins,NumBinsSupplied, ...
        BinEdges,BinEdgesSupplied, ...
        BinLimits,BinLimitsSupplied, ...
        BinWidth,BinWidthSupplied, ...
        BinMethod);
end
n = double(ni);
if NormalizationSupplied
    n = normalize(Normalization,n,edges,numel(x));
end

%--------------------------------------------------------------------------

function [ ...
    BinEdges,BinEdgesSupplied, ...
    BinLimits,BinLimitsSupplied, ...
    BinMethod,BinMethodSupplied, ...
    BinWidth,BinWidthSupplied, ...
    Normalization,NormalizationSupplied, ...
    NumBins,NumBinsSupplied] = parseinput(varargin)
% If the first value is NumBins or BinEdges, pull it off before parsing PV
% pairs.
coder.internal.prefer_const(varargin);
NumBins = coder.internal.indexInt(0);
NumBinsSupplied = false;
BinEdgesSupplied = false;
if nargin >= 1 && (isnumeric(varargin{1}) || islogical(varargin{1}))
    if coder.internal.isConstTrue(isscalar(varargin{1}))
        validateattributes(varargin{1},{'numeric','logical'}, ...
            {'integer','positive'},mfilename,'m',2);
        NumBins = coder.internal.indexInt(varargin{1});
        NumBinsSupplied = true;
    else
        coder.internal.assert(length(varargin{1}) >= 2, ...
            'MATLAB:histcounts:EmptyOrScalarBinEdges');
        validateattributes(varargin{1},{'numeric','logical'}, ...
            {'vector','real','nondecreasing'}, ...
            mfilename,'edges',2)
        BinEdges = varargin{1};
        BinEdgesSupplied = true;
    end
    firstPVPair = 2;
else
    firstPVPair = 1;
end
% Parse PV Pairs.
parms = struct( ...
    'BinEdges',uint32(0), ...
    'BinLimits',uint32(0), ...
    'BinMethod',uint32(0), ...
    'BinWidth',uint32(0), ...
    'Normalization',uint32(0), ...
    'NumBins',uint32(0) ...
    );
% Select any non-default parsing options.
poptions = struct( ...
    'PartialMatching','unique',...
    'StructExpand',false,...
    'IgnoreNulls',true,...
    'CaseSensitivity',false);
% Parse the inputs.
pstruct = coder.internal.parseParameterInputs(parms,poptions, ...
    varargin{firstPVPair:end});
% Retrieve parameter values.

% BinEdges
if pstruct.BinEdges > 0
    BinEdgesSupplied = true;
    BinEdgesPV = coder.internal.getParameterValue( ...
        pstruct.BinEdges,zeros(1,2),varargin{firstPVPair:end});
    validateattributes(BinEdgesPV,{'numeric','logical'}, ...
        {'vector','nonempty','real','nondecreasing'}, ...
        mfilename,'BinEdges');
    coder.internal.assert(length(BinEdgesPV) >= 2, ...
        'MATLAB:histcounts:EmptyOrScalarBinEdges');
    BinEdges = BinEdgesPV;
end

% BinLimits
BinLimits = coder.internal.getParameterValue( ...
    pstruct.BinLimits,[0,0],varargin{firstPVPair:end});
BinLimitsSupplied = pstruct.BinLimits > 0;
if BinLimitsSupplied
    validateattributes(BinLimits, {'numeric','logical'}, ...
        {'numel',2,'vector','real','nondecreasing','finite'}, ...
        mfilename,'BinLimits');
end

% BinMethod
BinMethodInput = coder.internal.getParameterValue( ...
    pstruct.BinMethod,'auto',varargin{firstPVPair:end});
BinMethodSupplied = pstruct.BinMethod > 0;
BinMethod = parseBinMethod(BinMethodInput);

% BinWidth
BinWidth = coder.internal.getParameterValue( ...
    pstruct.BinWidth,0,varargin{firstPVPair:end});
BinWidthSupplied = pstruct.BinWidth > 0;
if BinWidthSupplied
    validateattributes(BinWidth,{'numeric','logical'}, ...
        {'scalar','real','positive','finite'}, ...
        mfilename,'BinWidth');
end

% Normalization
NormalizationInput = coder.internal.getParameterValue( ...
    pstruct.Normalization,'count',varargin{firstPVPair:end});
NormalizationSupplied = pstruct.Normalization > 0;
Normalization = parseNormalization(NormalizationInput);

% NumBins
if pstruct.NumBins > 0
    NumBinsSupplied = true;
    NumBinsPV = coder.internal.getParameterValue( ...
        pstruct.NumBins,NumBins,varargin{firstPVPair:end});
    validateattributes(NumBinsPV,{'numeric','logical'}, ...
        {'scalar','integer','positive'},mfilename,'NumBins');
    NumBins = coder.internal.indexInt(NumBinsPV);
end

% Check for incompatible combinations.
if BinEdgesSupplied
    % BinEdges is incompatible with all other options.
    coder.internal.assert(~( ...
        NumBinsSupplied || ...
        BinWidthSupplied || ...
        BinLimitsSupplied || ...
        BinMethodSupplied), ...
        'Coder:toolbox:InvalidMixedOptions');
else
    BinEdges = zeros(1,2);
end
% Can't have more than one of NumBins, BinWidth, and BinMethod.
coder.internal.assert( ...
    NumBinsSupplied + ...
    BinWidthSupplied + ...
    BinMethodSupplied <= 1, ...
    'Coder:toolbox:InvalidMixedOptions');

%--------------------------------------------------------------------------

function [ ...
    BinEdgesSupplied, ...
    BinLimitsSupplied, ...
    BinWidthSupplied, ...
    NormalizationSupplied, ...
    NumBinsSupplied] = optionsSupplied(varargin)
% Determine which options have been supplied. This code is pared down from
% parseInputs to facilitate constant-folding, since in some cases the
% presence of non-constant options prevents at least one of the outputs of
% this function from constant-folding.
coder.internal.prefer_const(varargin);
NumBinsSupplied = false;
BinEdgesSupplied = false;
if nargin >= 1 && (isnumeric(varargin{1}) || islogical(varargin{1}))
    if coder.internal.isConstTrue(isscalar(varargin{1}))
        NumBinsSupplied = true;
    else
        BinEdgesSupplied = true;
    end
    firstPVPair = 2;
else
    firstPVPair = 1;
end
% Parse PV Pairs.
parms = struct( ...
    'BinEdges',uint32(0), ...
    'BinLimits',uint32(0), ...
    'BinMethod',uint32(0), ...
    'BinWidth',uint32(0), ...
    'Normalization',uint32(0), ...
    'NumBins',uint32(0) ...
    );
% Select any non-default parsing options.
poptions = struct( ...
    'PartialMatching','unique',...
    'StructExpand',false,...
    'IgnoreNulls',true,...
    'CaseSensitivity',false);
% Parse the inputs.
pstruct = coder.internal.parseParameterInputs(parms,poptions, ...
    varargin{firstPVPair:end});
BinWidthSupplied = pstruct.BinWidth > 0;
BinLimitsSupplied = pstruct.BinLimits > 0;
if pstruct.BinEdges > 0
    BinEdgesSupplied = true;
end
if pstruct.NumBins > 0
    NumBinsSupplied = true;
end
NormalizationSupplied = pstruct.Normalization > 0;

%--------------------------------------------------------------------------

function [n,edges,bin] = HistCounts(x, ...
    NumBins,NumBinsSupplied, ...
    BinEdges,BinEdgesSupplied, ...
    BinLimits,BinLimitsSupplied, ...
    BinWidth,BinWidthSupplied, ...
    BinMethod)
coder.internal.prefer_const( ...
    NumBins,NumBinsSupplied, ...
    BinEdgesSupplied, ...
    BinLimitsSupplied, ...
    BinWidthSupplied, ...
    BinMethod);

edges = findEdges(x,BinLimitsSupplied,BinLimits,BinWidthSupplied,...
    BinWidth,BinEdgesSupplied,BinEdges,NumBinsSupplied,NumBins,MAXNUMBINS,...
    BinMethod);

if nargout <= 2
    n = mapElementsToBins(x,edges);
else
    [n,bin] = mapElementsToBins(x,edges);
end

%--------------------------------------------------------------------------

function n = normalize(Normalization,n,edges,nx)
% Normalize n, if requested.
coder.internal.prefer_const(Normalization,nx);
nBins = coder.internal.indexInt(numel(n));
switch Normalization
    case COUNTDENSITY
        % n = n./double(diff(edges))
        for k = 1:nBins
            n(k) = n(k)/double(edges(k+1) - edges(k));
        end
    case CUMCOUNT
        % n = cumsum(n)
        for k = 2:nBins
            n(k) = n(k) + n(k - 1);
        end
    case PROBABILITY
        n = n/nx;
    case CDF
        % n = cumsum(n/numel(x))
        n = n/nx;
        for k = 2:nBins
            n(k) = n(k) + n(k - 1);
        end
    case PDF
        % n = n/numel(x)./double(diff(edges));
        n = n/nx;
        for k = 1:nBins
            n(k) = n(k)/double(edges(k+1) - edges(k));
        end
end

%--------------------------------------------------------------------------

function mb = MAXNUMBINS
coder.inline('always');
mb = 65536;  % 2^16

%--------------------------------------------------------------------------

function Normalization = parseNormalization(NormalizationInput)
% Normalization = validatestring(value, ...
%     {'count','countdensity','cumcount','probability','pdf','cdf'}, ...
%     funcname,'Normalization',argumentPosition);
NormalizationMap = struct('count',COUNT,'countdensity',COUNTDENSITY, ...
    'cumcount',CUMCOUNT,'probability',PROBABILITY,'pdf',PDF,'cdf',CDF);
Normalization = coder.internal.partialParamParser( ...
    deblank(NormalizationInput),NormalizationInput,NormalizationMap, ...
    NormalizationMap,COUNT);

function n = COUNT
coder.inline('always');
n = int8(0);

function n = COUNTDENSITY
coder.inline('always');
n = int8(1);

function n = CUMCOUNT
coder.inline('always');
n = int8(2);

function n = PROBABILITY
coder.inline('always');
n = int8(3);

function n = PDF
coder.inline('always');
n = int8(4);

function n = CDF
coder.inline('always');
n = int8(5);

%--------------------------------------------------------------------------

function BinMethod = parseBinMethod(BinMethodInput)
% BinMethodStr = validatestring(value, ...
%     {'auto','scott','fd','integers','sturges','sqrt'},funcname, ...
%     'BinMethod',argumentPosition);
methodMap = struct('auto',AUTO,'scott',SCOTT,'fd',FD, ...
    'integers',INTEGERS,'sturges',STURGES,'sqrt',SQRT);
BinMethod = coder.internal.partialParamParser(deblank(BinMethodInput), ...
    BinMethodInput,methodMap,methodMap,AUTO);

function n = AUTO
coder.inline('always');
n = int8(0);

function n = SCOTT
coder.inline('always');
n = int8(1);

function n = FD
coder.inline('always');
n = int8(2);

function n = INTEGERS
coder.inline('always');
n = int8(3);

function n = STURGES
coder.inline('always');
n = int8(4);

function n = SQRT
coder.inline('always');
n = int8(5);

%--------------------------------------------------------------------------
