function [b,ndx,pos] = unique(a,varargin)
%MATLAB Code Generation Library Function

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

coder.internal.allowEnumInputs;
coder.inline('always');
narginchk(1,4);
coder.internal.assert(coder.internal.isBuiltInNumeric(a) || islogical(a) || ischar(a) ...
    || iscell(a),'Coder:toolbox:unique_3', ...
    'IfNotConst','Fail');
coder.internal.assert(iscell(a) || isa(a,'float') || isreal(a), ...
    'Coder:toolbox:unique_4', ...
    'IfNotConst','Fail');
[byrow,lastp,legacyp,stablep] = parse_flags(varargin{:});
if iscell(a)
    if nargout == 3
        [b,indx,ipos] = unique_cell(a,byrow,lastp,legacyp,stablep);
        ndx = double(indx);
        pos = double(ipos);
    elseif nargout == 2
        [b,indx] = unique_cell(a,byrow,lastp,legacyp,stablep);
        ndx = double(indx);
    else
        b = unique_cell(a,byrow,lastp,legacyp,stablep);
    end
elseif byrow
    if nargout == 3
        [b,indx,ipos] = unique_rows(a,lastp,legacyp,stablep);
        ndx = double(indx);
        pos = double(ipos);
    elseif nargout == 2
        [b,indx] = unique_rows(a,lastp,legacyp,stablep);
        ndx = double(indx);
    else
        b = unique_rows(a,lastp,legacyp,stablep);
    end
else
    if nargout == 3
        [b,indx,ipos] = unique_vector(a,lastp,legacyp,stablep);
        ndx = double(indx);
        pos = double(ipos);
    elseif nargout == 2
        [b,indx] = unique_vector(a,lastp,legacyp,stablep);
        ndx = double(indx);
    else
        b = unique_vector(a,lastp,legacyp,stablep);
    end
end

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

function p = legacy_mode_default_value
tmp = coder.const(feval('union',[],[]));
p = coder.const(size(tmp,2)==0);

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

function [byrow,lastp,legacyp,stablep] = parse_flags(varargin)
coder.internal.prefer_const(varargin);
byrow = false;
legacyp = false;
r2012ap = false;
stablep = false;
sortedp = false;
firstp = false;
lastp = false;
for k = coder.unroll(nargin:-1:1)
    coder.internal.assert(coder.internal.isConst(varargin{k}) && coder.internal.isCharOrScalarString(varargin{k}), ...
        'Coder:toolbox:unique_5', ...
        'IfNotConst','Fail');
    lenvk = coder.const(strlength(varargin{k}));
    len1 = coder.const(eml_max(1,lenvk));
    len2 = coder.const(eml_max(2,lenvk));
    if strncmpi(varargin{k},'first',len1)
        firstp = true;
    elseif strncmpi(varargin{k},'last',len2)
        lastp = true;
    elseif strncmpi(varargin{k},'legacy',len2)
        legacyp = true;
        coder.internal.assert(k == nargin, ...
            'MATLAB:UNIQUE:LegacyTrailing', ...
            'IfNotConst','Fail');
    elseif strncmpi(varargin{k},'r2012a',len2)
        r2012ap = true;
        coder.internal.assert(k == nargin, ...
            'MATLAB:UNIQUE:R2012aTrailing', ...
            'IfNotConst','Fail');
    elseif strncmpi(varargin{k},'rows',len2)
        byrow = true;
    elseif strncmpi(varargin{k},'sorted',len2)
        sortedp = true;
    elseif strncmpi(varargin{k},'stable',len2)
        stablep = true;
    else
        coder.internal.assert(false, ...
            'MATLAB:UNIQUE:UnknownFlag', varargin{k}, ...
            'IfNotConst','Fail');
    end
end
if legacyp || r2012ap
    % Either the legacy or the R2012a flag was supplied, so 'sorted' and
    % 'stable' flags are not permitted.
    coder.internal.assert(~(sortedp || stablep), ...
        'MATLAB:UNIQUE:SetOrderBehavior', ...
        'IfNotConst','Fail');
    legacyp = ~r2012ap;
else
    coder.internal.assert(~(sortedp && stablep), ...
        'MATLAB:UNIQUE:SetOrderConflict', ...
        'IfNotConst','Fail');
    % Use the MATLAB default.
    if sortedp || stablep
        legacyp = false;
    else
        legacyp = legacy_mode_default_value;
    end
end
if ~(firstp || lastp)
    % Neither 'first' nor 'last' was selected. The default is 'last' in
    % legacy mode, otherwise 'first'.
    lastp = legacyp;
else
    coder.internal.assert(~(firstp && lastp), ...
        'MATLAB:UNIQUE:OccurrenceConflict', ...
        'IfNotConst','Fail');
    coder.internal.assert(~(sortedp || stablep), ...
        'MATLAB:UNIQUE:SetOrderOccurrence', ...
        'IfNotConst','Fail');
end

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

function [b,ndx,pos] = unique_vector(a,lastp,legacyp,stablep)
% Handle empty: no elements.
coder.internal.prefer_const(lastp,legacyp,stablep);
if coder.internal.isConstTrue(isscalar(a))
    b = a;
    ndx = coder.internal.indexInt(1);
    pos = coder.internal.indexInt(1);
    return
end
na = cast(numel(a),coder.internal.indexIntClass);
isrowndx = false;
if coder.internal.isConstTrue(isrow(a))
    coder.varsize('b',[],[0,1]);
    if legacyp
        isrowndx = true;
        pos = coder.nullcopy(zeros(size(a),coder.internal.indexIntClass));
    else
        pos = coder.nullcopy(zeros(size(a,2),1,coder.internal.indexIntClass));
    end
    maxnb = size(a,2);
elseif legacyp
    coder.internal.assert(false,'Coder:toolbox:unique_8');
elseif coder.internal.isConstTrue(isvector(a))
    coder.varsize('b',[],[1,0]);
    pos = coder.nullcopy(zeros(size(a,1),1,coder.internal.indexIntClass));
    maxnb = size(a,1);
else
    coder.internal.assert(false,'Coder:toolbox:unique_InputMustBeVector');
end
idx = coder.internal.sortIdx(a,'a');
b = coder.nullcopy(eml_expand(coder.internal.scalarEg(a),size(a)));
for k = 1:na
    b(k) = a(idx(k));
end
if coder.internal.isConstTrue(isempty(a)) %g1875250
    ZERO = coder.internal.indexInt(0);
    nMInf = ZERO;
    nFinite = ZERO;
    nInf = ZERO;
    nNaN = ZERO;
else
    [nMInf,nFinite,nInf,nNaN] = count_nonfinites(b,na);
end
nb = zeros(coder.internal.indexIntClass);
% Process -Inf entries.
if nMInf > 0
    nb = ones(coder.internal.indexIntClass);
    b(nb) = b(1);
    for j = 1:nMInf
        pos(idx(j)) = 1;
    end
    if lastp
        idx(nb) = idx(nMInf);
    else
        idx(nb) = idx(1);
    end
end
% Process finite entries.
khi = coder.internal.indexPlus(nMInf,nFinite);
k = coder.internal.indexPlus(nMInf,1);
while k <= khi
    x = b(k);
    k0 = k;
    % Seek forward for a different entry.
    while true
        k = coder.internal.indexPlus(k,1);
        % Since x and b(k) are being read from the same array without
        % modification, we should not need safeEq.
        if k > khi || b(k) ~= x
            break
        end
    end
    % Record x.
    nb = coder.internal.indexPlus(nb,1);
    b(nb) = x;
    for j = k0:coder.internal.indexMinus(k,1)
        pos(idx(j)) = nb;
    end
    if lastp
        idx(nb) = idx(coder.internal.indexMinus(k,1));
    else
        idx(nb) = idx(k0);
    end
end
% Process Inf entries.
if nInf > 0
    nb = coder.internal.indexPlus(nb,1);
    b(nb) = b(coder.internal.indexPlus(khi,1));
    for j = 1:nInf
        pos(idx(coder.internal.indexPlus(khi,j))) = nb;
    end
    if lastp
        idx(nb) = idx(coder.internal.indexPlus(khi,nInf));
    else
        idx(nb) = idx(coder.internal.indexPlus(khi,1));
    end
end
% Process NaN entries.
k = coder.internal.indexPlus(khi,nInf);
for j = 1:nNaN
    nb = coder.internal.indexPlus(nb,1);
    b(nb) = b(coder.internal.indexPlus(k,j));
    pos(idx(coder.internal.indexPlus(k,j))) = nb;
    idx(nb) = idx(coder.internal.indexPlus(k,j));
end
% Trim output vectors.
assert(nb <= maxnb); %<HINT>
b = b(1:nb);
if isrowndx
    ndx = coder.nullcopy(zeros(1,nb,coder.internal.indexIntClass));
else
    ndx = coder.nullcopy(zeros(nb,1,coder.internal.indexIntClass));
end
for k = 1:nb
    ndx(k) = idx(k);
end
if stablep
    % Restore original order.
    r = coder.internal.sortIdx(ndx,'a');
    b = b(r);
    invr = coder.nullcopy(r);
    for k = 1:nb
        ndx(k) = idx(r(k));
        invr(r(k)) = k;
    end
    pos(:) = invr(pos(:));
end

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

function [nMInf,nFinite,nPInf,nNaN] = count_nonfinites(b,nb)
k = ones(coder.internal.indexIntClass);
while k <= nb && isinf(b(k)) && b(k) < 0
    k = coder.internal.indexPlus(k,1);
end
nMInf = coder.internal.indexMinus(k,1);
k = nb;
while k >= 1 && isnan(b(k))
    k = coder.internal.indexMinus(k,1);
end
nNaN = coder.internal.indexMinus(nb,k);
while k >= 1 && isinf(b(k)) && b(k) > 0
    k = coder.internal.indexMinus(k,1);
end
nPInf = coder.internal.indexMinus(coder.internal.indexMinus(nb,k),nNaN);
nFinite = coder.internal.indexMinus(k,nMInf);

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

function [b,ndx,pos] = unique_rows(a,lastp,legacyp,stablep)
% Unique rows.
coder.internal.prefer_const(lastp,legacyp,stablep);
coder.varsize('b',[],[1,~coder.internal.isConst(size(a,2))]);
coder.internal.assert(ismatrix(a), ...
    'Coder:toolbox:unique_9', ...
    'IfNotConst','Fail');
pos = coder.nullcopy(zeros(size(a,1),1,coder.internal.indexIntClass));
if size(a,1) == 0
    b = a;
    ndx = zeros(0,1,coder.internal.indexIntClass);
    return
end
if coder.internal.isConstTrue(isrow(a))
    b = a;
    ndx = coder.internal.indexInt(1);
    pos = coder.internal.indexInt(1);
    return
end
[b,idx] = sortrows(a);
nb = zeros(coder.internal.indexIntClass);
khi = cast(size(a,1),coder.internal.indexIntClass);
k = ones(coder.internal.indexIntClass);
while k <= khi
    k0 = k;
    % Seek forward for a different row.
    while true
        k = coder.internal.indexPlus(k,1);
        if k > khi || rows_differ(b,k0,k)
            break
        end
    end
    % Record row k0.
    nb = coder.internal.indexPlus(nb,1);
    for j = ones(coder.internal.indexIntClass):size(b,2)
        b(nb,j) = b(k0,j);
    end
    for j = k0:coder.internal.indexMinus(k,1)
        pos(idx(j)) = nb;
    end
    if lastp
        idx(nb) = idx(coder.internal.indexMinus(k,1));
    else
        idx(nb) = idx(k0);
    end
end
% Trim output matrices.
assert(nb <= size(a,1)); %<HINT>
b = b(1:nb,:);
ndx = coder.nullcopy(zeros(nb,1,coder.internal.indexIntClass));
for k = 1:nb
    ndx(k) = idx(k);
end
if stablep
    % Restore original order.
    r = coder.internal.sortIdx(ndx,'a');
    b = b(r,:);
    invr = coder.nullcopy(r);
    for k = 1:nb
        ndx(k) = idx(r(k));
        invr(r(k)) = k;
    end
    pos(:) = invr(pos(:));
end

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

function p = rows_differ(b,k0,k)
% Returns ~isequal(b(k0,:),b(k,:)).
p = false;
for j = 1:size(b,2)
    % Since b(k0,j) and b(k,j) are being read from the same array without
    % modification, we should not need safeEq.
    if b(k0,j) ~= b(k,j)
        p = true;
        break
    end
end

%--------------------------------------------------------------------------
function [b,ndx,pos] = unique_cell(a,byrow,lastp,legacyp,stablep)
coder.internal.prefer_const(byrow,lastp,legacyp,stablep);
coder.internal.assert(iscellstr(a),'MATLAB:UNIQUE:InputClass'); %#ok
coder.internal.assert(coder.internal.isConstTrue(isvector(a)),'Coder:toolbox:unique_cellInputMustBeVector');
isRow = coder.internal.isConstTrue(isrow(a));
coder.internal.assert(~legacyp || isRow,'Coder:toolbox:unique_cellInputMustBeRowForLegacyOption');
if coder.internal.isConstTrue(isscalar(a))
    b = a;
    ndx = coder.internal.indexInt(1);
    pos = coder.internal.indexInt(1);
    return
end
n = coder.internal.indexInt(numel(a));
isvalidType = @(x)ismatrix(x) && size(x,1) <= 1;
coder.unroll(~coder.internal.isHomogeneousCell(a));
for i = 1:n
    coder.internal.assert(isvalidType(a{i}),'MATLAB:UNIQUE:NotARowVector');
end
if byrow && ~coder.setwarningflag
    coder.internal.warning('MATLAB:UNIQUE:RowsFlagIgnored');
end
    
[cs,idx] = sort(a);
if legacyp % input is already validated to be a row vector
    pos = coder.nullcopy(zeros(1,n,coder.internal.indexIntClass));
else
    pos = coder.nullcopy(zeros(n,1,coder.internal.indexIntClass));
end

k = coder.internal.indexInt(1);
nunique = coder.internal.indexInt(0);
while k <= n
    k0 = k;
    c0 = cs{k0};
    while true
        k = k + 1;
        if k > n || ~isequal(c0,cs{k})
            break
        end
    end
    nunique = nunique + 1;
    cs{nunique} = c0;
    for j = k0:k-1
        pos(idx(j)) = nunique;
    end

    if lastp
        idx(nunique) = idx(k-1);
    else
        idx(nunique) = idx(k0);
    end
end

assert(nunique <= n); %<HINT>
if legacyp
    ndx = coder.nullcopy(zeros(1,nunique,coder.internal.indexIntClass));
else
    ndx = coder.nullcopy(zeros(nunique,1,coder.internal.indexIntClass));
end

for i = 1:nunique
    ndx(i) = idx(i);
end

if isRow
    b = cell(1,nunique);
else
    b = cell(nunique,1);
end

if stablep
    r = coder.internal.sortIdx(ndx,'a');
    invr = coder.nullcopy(r);
    for k = 1:nunique
        ndx(k) = idx(r(k));
        invr(r(k)) = k;
    end
    pos(:) = invr(pos(:));
    for i = 1:nunique
        b{i} = cs{r(i)};
    end
else
    for i = 1:nunique
        b{i} = cs{i};
    end
end
