function [M,F,C] = mode(x,dim)
%MATLAB Code Generation Library Function

%   Limitations:
%       The third output argument is not supported.

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

coder.inline('always');
eml_invariant(nargin > 0, ...
    'MATLAB:minrhs');
eml_invariant(nargout < 3, ...
    'Coder:toolbox:mode_2', ...
    'IfNotConst','Fail'); C = 0; % To calm MLINT.
eml_invariant(coder.internal.isBuiltInNumeric(x) || islogical(x), ...
    'Coder:toolbox:unsupportedClass','mode',class(x));
xzero = coder.internal.scalarEg(x);
if nargin < 2
    if eml_is_const(size(x)) && isequal(x,[])
        if isfloat(x)
            M = coder.internal.nan(class(x)) + xzero;
        else
            M = xzero;
        end
        F = 0;
        % C = {zeros(0,1,class(x))};
        return
    end
    eml_invariant(eml_is_const(size(x)) || ~isequal(x,[]), ...
        'Coder:toolbox:mode_specialEmpty');
    dim = coder.internal.constNonSingletonDim(x);
    eml_invariant(eml_is_const(size(x,dim)) || ...
        isscalar(x) || ...
        size(x,dim) ~= 1, ...
        'Coder:toolbox:autoDimIncompatibility');
else
    coder.internal.prefer_const(dim);
    eml_invariant(eml_is_const(dim), ...
        'Coder:toolbox:dimNotConst');
    if coder.internal.isVectorDim(dim)
        fx = @(x)arraymode(x,coder.internal.indexInt(1));
        if nargout == 2
            [M,f] = coder.internal.applyToMultipleDims(fx,x,dim);
            F = double(f);
        else
            M = coder.internal.applyToMultipleDims(fx,x,dim);
        end
        return
    end
    coder.internal.assertValidDim(dim);
end
if nargout == 2
    [M,f] = arraymode(x,dim);
    F = double(f);
else
    M = arraymode(x,dim);
end

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

function [M,F] = arraymode(x,dim)
% Calculate the mode along each vector of an array.
coder.internal.prefer_const(dim);
if islogical(x)
    N = coder.internal.indexInt(size(x,dim));
    mid = coder.internal.indexDivide(N,2);
    F = coder.internal.indexInt(sum(x,dim));
    M = F > mid;
    TrueCountToModeCount = @(c)eml_max(c,N-c);
    F = coder.internal.applyScalarFunctionInPlace('',TrueCountToModeCount,F);
    return
end
if eml_is_const(size(x,dim)) && size(x,dim) == 1 % Covers dim > ndims(x) case.
    M = x;
    F = ones(size(x),coder.internal.indexIntClass);
    return
end
ONE = coder.internal.indexInt(1);
xzero = coder.internal.scalarEg(x);
sz = size(x);
sz(dim) = 1;
M = coder.nullcopy(eml_expand(xzero,sz));
F = coder.nullcopy(zeros(sz,coder.internal.indexIntClass));
if isempty(x) || eml_ambiguous_types
    if isfloat(x)
        M(:) = coder.internal.nan(class(x));
    else
        M(:) = 0;
    end
    F(:) = 0;
elseif isscalar(M)
    [M,F] = vectormode(x);
else
    npages  = coder.internal.prodsize(x,'above',dim);
    if dim == 1
        if coder.internal.useParforConst(mfilename,npages)
            parfor i = 1:npages
                [mtmp,ftmp] = vectormode(x(:,i));
                M(i) = mtmp;
                F(i) = ftmp;
            end
        else
            for i = 1:npages
                [mtmp,ftmp] = vectormode(x(:,i));
                M(i) = mtmp;
                F(i) = ftmp;
            end
        end
    elseif nargout == 1 && coder.internal.avoidArrayFlattening
        % applyVectorFunction only supports 1 output. Otherwise, we could
        % use it for everything here.
        M = coder.internal.applyVectorFunction(@vectormode,x,dim,ONE,xzero);
    else
        vlen = size(x,dim);
        vwork = coder.nullcopy(eml_expand(xzero,[vlen,1]));
        vstride = coder.internal.prodsize(x,'below',dim);
        vspread = coder.internal.indexTimes(coder.internal.indexMinus(vlen,1),vstride);
        i2 = zeros(coder.internal.indexIntClass);
        iy = zeros(coder.internal.indexIntClass);
        for i = 1:npages
            i1 = i2;
            i2 = coder.internal.indexPlus(i2,vspread);
            for j = 1:vstride
                i1 = coder.internal.indexPlus(i1,1);
                i2 = coder.internal.indexPlus(i2,1);
                % Copy x(i1:vstride:i2) to vwork.
                ix = i1;
                for k = 1:vlen
                    vwork(k) = x(ix);
                    ix = coder.internal.indexPlus(ix,vstride);
                end
                % Calculate median and store in y.
                [mtmp,ftmp] = vectormode(vwork);
                iy = coder.internal.indexPlus(iy,1);
                M(iy) = mtmp;
                F(iy) = ftmp;
            end
        end
    end
end

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

function [m,f] = vectormode(v)
% Mode of a vector with corresponding frequency.
ONE = ones(coder.internal.indexIntClass);
v = sort(v);
m = v(1);
f = ONE;
mtmp = m;
ftmp = ONE;
for k = 2:numel(v)
    if v(k) == mtmp
        ftmp = coder.internal.indexPlus(ftmp,ONE);
    else
        if ftmp > f
            m = mtmp;
            f = ftmp;
        end
        mtmp = v(k);
        ftmp = ONE;
    end
end
if ftmp > f
    m = mtmp;
    f = ftmp;
end

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