function IA = ismissing(A,indicators)
%ISMISSING Find missing entries
%   I = ISMISSING(T)
%   I = ISMISSING(T,INDICATORS)
%   
%   See also ISMISSING, GPUARRAY.
%   


%   Copyright 2016-2020 The MathWorks, Inc.

% If A lives on the host, gather indicators and launch core MATLAB version
if ~isa(A, 'gpuArray')
    IA = ismissing(A, gather(indicators));
    return
end

if nargin <= 1
    % ismissing(A)
    IA = iCheckForDefaultValue(A);
else
    % ismissing(A,indicators)
    % standardizeMissing(A,indicators)
    % gather second input if remote
    if isa(indicators, 'gpuArray')
        indicators = gather(indicators);
    end
    if isa(indicators, 'missing')
        indicators = feval(underlyingType(A), indicators);
    end
    indstruct = iParseIndicators(indicators);
    IA = iCheckForSpecifiedValue(A,indstruct);
end
end
%--------------------------------------------------------------------------
function IA = iCheckForDefaultValue(A)
% Used for 1-input ismissing for arrays
if isfloat(A)
    IA = isnan(A);
elseif (isinteger(A) || islogical(A))
    IA = gpuArray.false(size(A));
else
    error(message('MATLAB:ismissing:FirstInputInvalid'));
end
end
%--------------------------------------------------------------------------
function IA = iCheckForSpecifiedValue(A,ind)
underlyingClassA = underlyingType(A);
% Indicators-based ismissing and standardizeMissing for gpuArrays
if strcmp(underlyingClassA, 'double')
    IA = iIsmissingApply(A,ind,@iIsmissingNumeric,'Double');
elseif strcmp(underlyingClassA,'single')
    IA = iIsmissingApply(A,ind,@iIsmissingNumeric,'Single');
elseif (isinteger(A) || islogical(A))
    % Integer arrays match to double indicators or to integer indicators of
    % exactly the same type of integer -- same behavior as ISMEMBER.
    indTmp = {};
    if isfield(ind,'IntegerLogical')
        indTmp = ind.('IntegerLogical');
        indTmp = indTmp(cellfun(@(x) isa(x,'double') || isa(x,underlyingClassA),indTmp));
    end
    IA = iIsmissingNumeric(A,indTmp);
    if isempty(indTmp)
        error(message('MATLAB:ismissing:IndicatorsIntegerLogical',underlyingClassA));
    end
else
    error(message('MATLAB:ismissing:FirstInputInvalid'));
end
end
%--------------------------------------------------------------------------
function IA = iIsmissingApply(A,ind,ismissingFun,typeid)
% Apply ismissing with indicators for arrays or table variables
if isfield(ind,typeid)
    % If A is an array (must be true for GPU), a cell of indicators is not supported
    if ind.IndIsCell
        error(message(['MATLAB:ismissing:Indicators',typeid]));
    end
    % Indicators of compatible type with A were provided
    IA = ismissingFun(A,ind.(typeid));
else
    error(message(['MATLAB:ismissing:Indicators',typeid]));
end
end
%--------------------------------------------------------------------------
function indstruct = iParseIndicators(ind)
% Parse indicators. They can be an array or a cell at this point.
indstruct = struct;
if ~iscell(ind)
    indstruct.IndIsCell = false;
    indstruct.IndIsCellStr = false;
    indstruct = iConstructIndicatorsStruct(indstruct,ind);%,AisTable,stdize);
else
    indstruct.IndIsCell = true;
    indstruct.IndIsCellStr = iscellstr(ind);
    if isempty(ind)
        % {} is still a cellstr, we don't want to error for it
        indstruct = iAddToField(indstruct,ind,'Cellstr');
        indstruct = iAddToField(indstruct,ind,'Char');
        indstruct = iAddToField(indstruct,ind,'Categorical');
    end
    for ii = 1:numel(ind)
        indstruct = iConstructIndicatorsStruct(indstruct,ind{ii});
    end
end
end
%--------------------------------------------------------------------------
function indstruct = iConstructIndicatorsStruct(indstruct,ind)
% Separate indicators according to the array type they are compatible with
if (isnumeric(ind) || islogical(ind))
    ind = reshape(ind,[],1);
    if isa(ind,'double')
        % Double indicators supported for all numeric and logical arrays
        indstruct = iAddToField(indstruct,{ind},'Double');
        indstruct = iAddToField(indstruct,{ind},'Single');
        indstruct = iAddToField(indstruct,{ind},'IntegerLogical');
    elseif isa(ind,'single')
        % Single indicators supported only for double and single arrays
        indstruct = iAddToField(indstruct,{ind},'Double');
        indstruct = iAddToField(indstruct,{ind},'Single');
    else
        % Integer indicators supported only for double and integer arrays
        indstruct = iAddToField(indstruct,{ind},'Double');
        indstruct = iAddToField(indstruct,{ind},'IntegerLogical');
    end
else
    % categorical indicators are not supported
    % cellstr indicators within another cell are also not supported
    error(message('MATLAB:ismissing:IndicatorsInvalidType',class(ind)));
end
end
%--------------------------------------------------------------------------
function indstruct = iAddToField(indstruct,ind,typeid)
% Grow the array of indicators of a certain type
if isfield(indstruct,typeid)
    indstruct.(typeid) = [indstruct.(typeid); ind];
else
    indstruct.(typeid) = ind;
end
end
%--------------------------------------------------------------------------
function IA = iIsmissingNumeric(A,ind)
% A is numeric or logical, ind is numeric or logical
IA = false(size(A));
for ii = 1:numel(ind)
    indii = ind{ii};
    if issparse(indii)
        indii = full(indii);
    end
    mi = isnan(indii);
    if any(mi)
        IA = IA | ismember(A,indii(~mi)) | isnan(A);
    else
        IA = IA | ismember(A,indii);
    end
end
end
