function [N,BIN] = histc(X,edges,dim)
%MATLAB Code Generation Library Function

%   Copyright 2002-2016 The MathWorks, Inc.
%#codegen

coder.internal.allowEnumInputs;
eml_invariant(nargin>1, ...
    'MATLAB:minrhs');
eml_invariant(isreal(X), ...
    'Coder:toolbox:histc_2', ...
    'IfNotConst','Fail');
eml_invariant(coder.internal.isBuiltInNumeric(X) || ischar(X), ...
    'Coder:toolbox:histc_3', ...
    'IfNotConst','Fail');
eml_invariant(coder.internal.isBuiltInNumeric(edges) || ischar(edges), ...
    'Coder:toolbox:histc_4', ...
    'IfNotConst','Fail');
% Check dims argument (if given) for validity.
if nargin < 3
    dim = coder.internal.constNonSingletonDim(X);
    eml_invariant(eml_is_const(size(X,dim)) || ...
        isscalar(X) || ...
        size(X,dim) ~= 1, ...
        'Coder:toolbox:autoDimIncompatibility');
else
    eml_invariant(~isenum(edges), ...
        'Coder:toolbox:histc_6', ...
        'IfNotConst','Fail');
    eml_invariant(eml_is_const(dim), ...
        'Coder:toolbox:dimNotConst');
    coder.internal.assertValidDim(dim);
end
% Allocate output matrices.
nbins = numel(edges);
% getresultsize: calculate size of result.
if eml_is_const(isvector(X)) && isvector(X) && ...
        dim == 1 && eml_is_const(size(X,2)) && size(X,2) == 1
    outsize = size(X);
    outsize(1) = nbins;
    outsize(2) = 1;
elseif eml_is_const(isvector(X)) && isvector(X) && ...
        dim == 2 && eml_is_const(size(X,1)) && size(X,1) == 1
    outsize = size(X);
    outsize(1) = 1;
    outsize(2) = nbins;
elseif dim > coder.internal.ndims(X)
    outsize = [size(X),ones(1,dim-coder.internal.ndims(X))];
outsize(dim) = nbins;
else
    outsize = size(X);
    outsize(dim) = nbins;
end
N = zeros(outsize);
BIN = zeros(size(X));
ZERO = zeros(coder.internal.indexIntClass);
ONE = ones(coder.internal.indexIntClass);
% Check edges input.
eok = edges_nondecreasing(edges);
eml_invariant(eok,'Coder:MATLAB:histc_InvalidInput3');
if ~eok
    N(:) = coder.internal.nan;
    BIN(:) = coder.internal.nan;
    return
end
% stride length
stride = coder.internal.prodsize(X,'below',dim);
% size of X along active dimension
mx = size(X,dim);
% Pick direction for speed.
if stride == ONE
    n1 = stride;
    n2 = coder.internal.prodsize(X,'above',dim); % eml_rdivide(numel(X),mx);
    xoffset = ZERO;
    yoffset = ZERO;
else
    n1 = coder.internal.prodsize(X,'above',dim); % eml_rdivide(eml_rdivide(numel(X),mx),stride);
    n2 = stride;
    xoffset = coder.internal.indexMinus(coder.internal.indexTimes(stride,mx),1); % stride*mx-1
    yoffset = coder.internal.indexMinus(coder.internal.indexTimes(stride,nbins),1); % stride*nbins-1
end
xind = ONE;
yind = ONE;
ystep = coder.internal.indexMinus(coder.internal.indexTimes(stride,nbins),yoffset); % stride*nbins-yoffset
xpage = coder.internal.indexMinus(coder.internal.indexPlus(xoffset,1),stride); % xoffset-stride+1
ypage = coder.internal.indexMinus(coder.internal.indexPlus(yoffset,1),stride); % yoffset-stride+1
% main loop
for i = 1:n1 % page loop
    for j = 1:n2
        for k = 1:mx
            bin = findbin(X(xind),edges);
            if bin > 0
                % binind = yind + stride*(bin-1);
                binind = coder.internal.indexPlus(yind, ...
                    coder.internal.indexTimes(stride, ...
                    coder.internal.indexMinus(bin,ONE)));
                N(binind) = N(binind) + 1;
            end
            BIN(xind) = bin;
            xind = coder.internal.indexPlus(xind,stride);
        end
        xind = coder.internal.indexMinus(xind,xoffset);
        yind = coder.internal.indexPlus(yind,ystep);
    end
    % go to next page of input and output arrays
    xind = coder.internal.indexPlus(xind,xpage);
    yind = coder.internal.indexPlus(yind,ypage);
end

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

function k = findbin(x,bin_edges)
% Return index of bin the x.  x is in bin k if  bin_edges(k) <= x <
% bin_edges(k+1). Special case: x is in the last bin if x == bin_edges(nbins).
if isenum(x)
    k = findbin(int32(x),bin_edges);
    return
end
k = zeros(coder.internal.indexIntClass);
if ~isempty(bin_edges) && ~isnan(x)
    % Use a binary search
    if x >= bin_edges(1) && x < bin_edges(end)
        k = coder.internal.bsearch(bin_edges,x);
    end
    % Check for special case
    if x == bin_edges(end)
        k = cast(numel(bin_edges),coder.internal.indexIntClass);
    end
end

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

function p = edges_nondecreasing(edges)
nbins = cast(numel(edges),coder.internal.indexIntClass);
if nbins > 1
    for m = 2:nbins
        if ~(edges(m) >= edges(m-1))
            p = false;
            return
        end
    end
end
p = true;

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