%MISSING Create a missing value
%   M = MISSING returns a missing value which can be assigned into an array
%   to represent missing data. Datatypes that support missing values will
%   convert M to their native missing value.
%
%   Examples:
%
%     % Denote an element of a double array as missing
%       a = 1:5
%       a(2) = missing
%
%     % Denote several elements of a datetime array as missing
%       d = datetime:(datetime+4)
%       d([1,3,4]) = missing
%
%   See also ISMISSING, FILLMISSING, ISNAN

%   Copyright 2016-2023 The MathWorks, Inc.

classdef (Sealed) missing < matlab.mixin.internal.MatrixDisplay & matlab.mixin.CustomCompactDisplayProvider
    methods (Hidden)
        function disp(m)
            disp(string(m)); % converts missing to the missing string, not to "<missing>"
        end
        function rep = compactRepresentationForColumn(obj,displayConfiguration,~)
            rep = fullDataRepresentation(obj,displayConfiguration);
        end
        function rep = compactRepresentationForSingleLine(obj,displayConfiguration,~)
            rep = fullDataRepresentation(obj,displayConfiguration);
        end
        
        function b = ismissing(m, indicators)
            if nargin > 1
                try
                    if isa(m, 'missing')
                        if ~isa(indicators, 'missing')
                            error(message('MATLAB:invalidConversion', 'missing', class(indicators)));
                        end
                    else
                        b = deferCall('ismissing', m, indicators);
                        return;
                    end
                catch ME
                    throwAsCaller(ME);
                end
            end
            b = true(size(m));
        end
        
        function d = double(m)
            d = nan(size(m));
        end
        
        function d = single(m)
            d = nan(size(m), 'single');
        end
        
        function s = string(m)
            s = string(nan(size(m)));
        end
        
        function s = struct(varargin)
            if nargin == 1
                error(message('MATLAB:invalidConversion', 'struct', 'missing'));
            else
                try
                    s = builtin('struct', varargin{:});
                catch e
                    throwAsCaller(e);
                end
            end
        end
        
        function o = horzcat(varargin)
            o = deferCall('horzcat', varargin{:});
        end
        
        function o = vertcat(varargin)
            o = deferCall('vertcat', varargin{:});
        end
        
        function o = cat(dim, varargin)
            if ismissing(dim)
                dim = nan;
            end
            varargin = convertCell(varargin);
            o = redispatch('cat', dim, varargin{:});
        end
        
        function b = isequal(varargin)
            b = false;
        end
        
        function b = isequaln(varargin)
            try
                b = deferCall('isequaln', varargin{:});
            catch
                b = false;
            end
        end
        
        function b = lt(left, right)
            b = compare(left,right,'lt');
        end
        
        function b = le(left, right)
            b = compare(left,right,'le');
        end
        
        function b = gt(left, right)
            b = compare(left,right,'gt');
        end
        
        function b = ge(left, right)
            b = compare(left,right,'ge');
        end
        
        function b = eq(left, right)
            b = falseExpand(left, right);
        end
        
        function b = ne(left, right)
            b = ~falseExpand(left, right);
        end
    end
    
    methods (Hidden, Access=protected)
        function displayImpl(m, ~, ~)
            disp(m);
            if isscalar(m) && (matlab.internal.display.formatSpacing == "loose")
                fprintf(newline);
            end
        end
    end
end

function arg = convertCell(arg)
    missings = false(size(arg));
    for i = 1:numel(arg)
        missings(i) = isa(arg{i},"missing");
    end
    % Convert all the missings in the input arg list to the class of the first
    % non-missing.
    found = find(~missings, 1);
    if ~isempty(found)
        for i = find(missings)
            arg{i} = matlab.internal.datatypes.missingLike(arg{i},arg{found});
        end
    end
end

function b = falseExpand(left, right)
    try
        convertCell({left, right});
        b = false(size(left)) | false(size(right));
    catch e
        e.throwAsCaller;
    end
end

function o = redispatch(fcn, varargin)
    try
        if isa(varargin{end}, 'missing')
            o = builtin(fcn, varargin{:});
        else
            o = feval(fcn, varargin{:});
        end
    catch e
        e.throwAsCaller;
    end
end

function o = deferCall(fcn, varargin)
    try
        varargin = convertCell(varargin);
        o = redispatch(fcn, varargin{:});
    catch e
        e.throwAsCaller;
    end
end

function b = compare(left, right, comparison)
    try
        if isa(left, "missing") && isa(right, "missing")
            b = falseExpand(left, right);
        else
            % Redispatch to the class so it can error if it does not support
            % relational operators.
            b = deferCall(comparison, left, right);
        end
    catch e
        e.throwAsCaller;
    end
end