## Copyright (C) 2026 Andreas Bertsatos <abertsatos@biol.uoa.gr>
##
## This file is part of the datatypes package for GNU Octave.
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 3 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, see <http://www.gnu.org/licenses/>.

################################################################################
##                ** Create and convert 'categorical' type **                 ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'categorical'      'dispstrings'      'cellstr'          'char'            ##
## 'double'           'single'           'int64'            'int32'           ##
## 'int16'            'int8'             'uint64'           'uint32'          ##
## 'uint16'           'uint8'                                                 ##
##                                                                            ##
################################################################################

%!assert_equal (isscalar (categorical ()), false)
%!assert_equal (isempty (categorical ()), true)
%!assert_equal (class (categorical), 'categorical')
%!assert_equal (dispstrings (categorical ()), {})
%!assert_equal (size (categorical), [0, 0])
%!assert_equal (size (categorical (NaN (2, 3, 4))), [2, 3, 4])
%!assert_equal (categories (categorical), {})
%!assert_equal (categories (categorical (NaN)), {})
%!assert_equal (categories (categorical (NaN (3))), {})
%!assert_equal (double (categorical), [])
%!assert_equal (double (categorical (NaN)), NaN)
%!assert_equal (double (categorical (NaN (3))), NaN (3))
%!test
%! stations = categorical (string ({'S1', 'S2', 'S1', 'S3', 'S2'}));
%! assert_equal (stations, categorical ({'S1', 'S2', 'S1', 'S3', 'S2'}));
%! assert_equal (dispstrings (stations), {'S1', 'S2', 'S1', 'S3', 'S2'});
%! assert_equal (categories (stations), {'S1'; 'S2'; 'S3'});
%!test
%! stations = categorical ({'S1', 'S2', 'S1'; 'S2', 'S3', 'S2'});
%! assert_equal (categories (stations), {'S1'; 'S2'; 'S3'});
%! assert_equal (double (stations), [1, 2, 1; 2, 3, 2]);
%! assert_equal (size (stations), [2, 3]);
%!test
%! stations = categorical ({'S1', 'S2', ''; 'S2', 'S3', 'S2'});
%! assert_equal (categories (stations), {'S1'; 'S2'; 'S3'});
%! assert_equal (dispstrings (stations), {'S1', 'S2', '<undefined>'; 'S2', 'S3', 'S2'});
%! assert_equal (ismissing (stations), isundefined (stations));
%! assert_equal (double (stations), [1, 2, NaN; 2, 3, 2]);
%!test
%! stations = categorical ({'S1', 'S2', 'S1'; 'S2', 'S3', 'S2'}, 'Ordinal', true);
%! assert_equal (categories (stations), {'S1'; 'S2'; 'S3'});
%! assert_equal (double (stations), [1, 2, 1; 2, 3, 2]);
%!test
%! stations = categorical ({'S3', 'S2', 'S1'; 'S2', 'S3', 'S2'}, 'Ordinal', true);
%! assert_equal (categories (stations), {'S1'; 'S2'; 'S3'});
%! assert_equal (double (stations), [3, 2, 1; 2, 3, 2]);
%!test
%! C = categorical ([3, 2, 3; 2, 1, 2]);
%! assert_equal (dispstrings (C), {'3', '2', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (double (C), [3, 2, 3; 2, 1, 2]);
%!test
%! C = categorical ([1, 2, 3; 2, 1, 2]);
%! assert_equal (dispstrings (C), {'1', '2', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (double (C), [1, 2, 3; 2, 1, 2]);
%!test
%! C = categorical ([3, 2, 3; 2, 1, 2], 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3', '2', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (double (C), [3, 2, 3; 2, 1, 2]);
%!test
%! C = categorical ([1, 2, 3; 2, 1, 2], 'Ordinal', true);
%! assert_equal (dispstrings (C), {'1', '2', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (double (C), [1, 2, 3; 2, 1, 2]);
%!test
%! C = categorical ([3, NaN, 3; 2, 1, 2]);
%! assert_equal (dispstrings (C), {'3', '<undefined>', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (ismissing (C), isundefined (C));
%! assert_equal (double (C), [3, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical ([1, NaN, 3; 5, 1, 2]);
%! assert_equal (dispstrings (C), {'1', '<undefined>', '3'; '5', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'; '5'});
%! assert_equal (ismissing (C), isundefined (C));
%! assert_equal (double (C), [1, NaN, 3; 4, 1, 2]);
%!test
%! C = categorical ([3, NaN, 3; 2, 1, 2], 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3', '<undefined>', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (ismissing (C), isundefined (C));
%! assert_equal (double (C), [3, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical ([1, NaN, 3; 2, 1, 2], 'Ordinal', true);
%! assert_equal (dispstrings (C), {'1', '<undefined>', '3'; '2', '1', '2'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (ismissing (C), isundefined (C));
%! assert_equal (double (C), [1, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical ({'medium', 'small', 'large'}, 'Ordinal', true);
%! assert_equal (dispstrings (C), {'medium', 'small', 'large'});
%! assert_equal (categories (C), {'large'; 'medium'; 'small'});
%! assert_equal (double (C), [2, 3, 1]);
%!test
%! S = {'medium', 'small', 'large'; 'large', 'medium', 'small'};
%! C = categorical (S, 'Ordinal', true);
%! assert_equal (dispstrings (C), S);
%! assert_equal (categories (C), {'large'; 'medium'; 'small'});
%! assert_equal (double (C), [2, 3, 1; 1, 2, 3]);
%!test
%! allsizes = {'medium', 'large', 'small', 'small', 'medium', 'large', 'medium', 'small'};
%! valueset = {'small', 'medium', 'large'};
%! C = categorical (allsizes, valueset, 'Ordinal', true);
%! assert_equal (categories (C), {'small'; 'medium'; 'large'});
%! assert_equal (double (C), [2, 3, 1, 1, 2, 3, 2, 1]);
%!test
%! allsizes = {'medium', 'large', 'small', 'small', 'medium', 'large', 'medium', 'small'};
%! valueset = string ({'small', 'medium', 'large'});
%! C = categorical (allsizes, valueset, 'Ordinal', true);
%! assert_equal (categories (C), {'small'; 'medium'; 'large'});
%! assert_equal (double (C), [2, 3, 1, 1, 2, 3, 2, 1]);
%!test
%! allsizes = string ({'medium', 'large', 'small', 'small', ...
%!                     'medium', 'large', 'medium', 'small'});
%! valueset = string ({'small', 'medium', 'large'});
%! C = categorical (allsizes, valueset, 'Ordinal', true);
%! assert_equal (categories (C), {'small'; 'medium'; 'large'});
%! assert_equal (double (C), [2, 3, 1, 1, 2, 3, 2, 1]);
%!test
%! allsizes = string ({'medium', 'large', 'small', 'small', ...
%!                     'medium', 'large', 'medium', 'small'});
%! valueset = {'small', 'medium', 'large'};
%! C = categorical (allsizes, valueset, 'Ordinal', true);
%! assert_equal (categories (C), {'small'; 'medium'; 'large'});
%! assert_equal (double (C), [2, 3, 1, 1, 2, 3, 2, 1]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]));
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'1 day'; '2 days'; '3 days'});
%! assert_equal (double (C), [3, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]), 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'1 day'; '2 days'; '3 days'});
%! assert_equal (double (C), [3, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]), days (1:3), 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'1 day'; '2 days'; '3 days'});
%! assert_equal (double (C), [3, NaN, 3; 2, 1, 2]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]), days ([3:-1:1]), 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'3 days'; '2 days'; '1 day'});
%! assert_equal (double (C), [1, NaN, 1; 2, 3, 2]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]), days ([3, 1, 2]), 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'3 days'; '1 day'; '2 days'});
%! assert_equal (double (C), [1, NaN, 1; 3, 2, 3]);
%!test
%! C = categorical (days ([3, NaN, 3; 2, 1, 2]), days ([2, 3, 1]), 'Ordinal', true);
%! assert_equal (dispstrings (C), {'3 days', '<undefined>', '3 days'; '2 days', '1 day', '2 days'});
%! assert_equal (categories (C), {'2 days'; '3 days'; '1 day'});
%! assert_equal (double (C), [2, NaN, 2; 1, 3, 1]);
%!test
%! C = categorical ([1:5], [1:5], {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical ([1:5], [5:-1:1], {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical ([1:5], [1:5], {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical ([1:5], [5:-1:1], {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical (categorical (1:5), categorical (1:5), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical (categorical (1:5), categorical ([5:-1:1]), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical (categorical (1:5), categorical (1:5), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical (categorical (1:5), categorical ([5:-1:1]), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical (categorical (1:5), categorical (2:4), {'B', 'C', 'D'});
%! assert_equal (dispstrings (C), {'<undefined>', 'B', 'C', 'D', '<undefined>'});
%! assert_equal (categories (C), {'B'; 'C'; 'D'});
%! assert_equal (double (C), [NaN, 1, 2, 3, NaN]);
%!test
%! C = categorical (categorical ([5:-1:1]), categorical (1:5), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical (categorical ([5:-1:1]), categorical ([5:-1:1]), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'A'; 'B'; 'C'; 'D'; 'E'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical (categorical ([5:-1:1]), categorical (1:5), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'A', 'B', 'C', 'D', 'E'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), [5, 4, 3, 2, 1]);
%!test
%! C = categorical (categorical ([5:-1:1]), categorical ([5:-1:1]), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (dispstrings (C), {'E', 'D', 'C', 'B', 'A'});
%! assert_equal (categories (C), {'E'; 'D'; 'C'; 'B'; 'A'});
%! assert_equal (double (C), 1:5);
%!test
%! C = categorical ([1, 2, 1, NaN, 3, 2, 3]);
%! assert_equal (dispstrings (C), ({'1', '2', '1', '<undefined>', '3', '2', '3'}));
%! C = categorical ([1, 2, 1, NaN, 3, 2, 3], [1, 2, 3, NaN], {'a', 's', 'd', 'f'});
%! assert_equal (dispstrings (C), {'a', 's', 'a', 'f', 'd', 's', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'; 'f'});
%! assert_equal (double (C), [1, 2, 1, 4, 3, 2, 3]);
%!test
%! C = categorical (categorical ([1, 2, 1, NaN, 3, 2, 3]));
%! assert_equal (dispstrings (C), ({'1', '2', '1', '<undefined>', '3', '2', '3'}));
%! C = categorical (categorical ([1, 2, 1, NaN, 3, 2, 3]), ...
%!                  categorical ([1, 2, 3, NaN]), {'a', 's', 'd', 'f'});
%! assert_equal (dispstrings (C), {'a', 's', 'a', 'f', 'd', 's', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'; 'f'});
%! assert_equal (double (C), [1, 2, 1, 4, 3, 2, 3]);
%!test
%! C = categorical (1:5, [1, 2, 3], {'a', 'b', 'b'});
%! assert_equal (dispstrings (C), {'a', 'b', 'b', '<undefined>', '<undefined>'});
%! assert_equal (categories (C), {'a'; 'b'});
%! assert_equal (double (C), [1, 2, 2, NaN, NaN]);
%!test
%! C = categorical (categorical (1:5), categorical ([1, 2, 3]), {'a', 'b', 'b'});
%! assert_equal (dispstrings (C), {'a', 'b', 'b', '<undefined>', '<undefined>'});
%! assert_equal (categories (C), {'a'; 'b'});
%! assert_equal (double (C), [1, 2, 2, NaN, NaN]);
%!test
%! C = categorical ({'1', '2', '1', '2'}, {' ', '1', '2'}, {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 's', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 2, 3]);
%!test
%! C = categorical ({'1', '2', '', '2'}, {' ', '1', '2'}, {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 'a', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 1, 3]);
%!test
%! C = categorical ({'1', '2', ' ', '2'}, {' ', '1', '2'}, {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 'a', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 1, 3]);
%!test
%! C = categorical (string ({'1', '2', '1', '2'}), string ({' ', '1', '2'}), {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 's', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 2, 3]);
%!test
%! C = categorical (string ({'1', '2', '', '2'}), string ({' ', '1', '2'}), {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 'a', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 1, 3]);
%!test
%! C = categorical (string ({'1', '2', ' ', '2'}), string ({' ', '1', '2'}), {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 'a', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 1, 3]);
%!test
%! C = categorical (string ({'1', '2', ' ', '2'}), string ({missing, '1', '2'}), {'a','s','d'});
%! assert_equal (dispstrings (C), {'s', 'd', 'a', 'd'});
%! assert_equal (categories (C), {'a'; 's'; 'd'});
%! assert_equal (double (C), [2, 3, 1, 3]);
%!test
%! C = categorical (days ([1, 2, 1, 3, NaN]));
%! assert_equal (dispstrings (C), {'1 day', '2 days', '1 day', '3 days', '<undefined>'});
%! assert_equal (categories (C), {'1 day'; '2 days'; '3 days'});
%! assert_equal (double (C), [1, 2, 1, 3, NaN]);
%!test
%! C = categorical (days ([1, 2, 1, 3, NaN]), days ([1, 2, 3, NaN]), {'a', 'b', 'c', 'd'});
%! assert_equal (dispstrings (C), {'a', 'b', 'a', 'c', 'd'});
%! assert_equal (categories (C), {'a'; 'b'; 'c'; 'd'});
%! assert_equal (double (C), [1, 2, 1, 3, 4]);
%!test
%! C = categorical (days ([1, 2, 1, 3, NaN]), days ([1, 2, 3, NaN]), {'a', 'c', 'c', 'd'});
%! assert_equal (dispstrings (C), {'a', 'c', 'a', 'c', 'd'});
%! assert_equal (categories (C), {'a'; 'c'; 'd'});
%! assert_equal (double (C), [1, 2, 1, 2, 3]);
%!test
%! DT = [datetime('yesterday'), datetime('today'), datetime('tomorrow')];
%! C = categorical (DT);
%! assert_equal (dispstrings (C), dispstrings (DT));
%! assert_equal (categories (C), dispstrings (DT'));
%! assert_equal (double (C), [1, 2, 3]);
%!test
%! DT = [datetime('yesterday'), datetime('today'), datetime('tomorrow')];
%! C = categorical (DT, DT, {'a', 'b', 'c'});
%! assert_equal (dispstrings (C), {'a', 'b', 'c'});
%! assert_equal (categories (C), {'a'; 'b'; 'c'});
%! assert_equal (double (C), [1, 2, 3]);
%!test
%! DT = [datetime('yesterday'), datetime('today'), NaT, datetime('tomorrow')];
%! C = categorical (DT, DT, {'a', 'b', 'c', 'd'});
%! assert_equal (dispstrings (C), {'a', 'b', 'c', 'd'});
%! assert_equal (categories (C), {'a'; 'b'; 'c'; 'd'});
%! assert_equal (double (C), [1, 2, 3, 4]);
%!test
%! DT = [datetime('yesterday'), datetime('today'), NaT, datetime('tomorrow')];
%! C = categorical (DT, DT, {'a', 'b', 'c', 'c'});
%! assert_equal (dispstrings (C), {'a', 'b', 'c', 'c'});
%! assert_equal (categories (C), {'a'; 'b'; 'c'});
%! assert_equal (double (C), [1, 2, 3, 3]);
%!test
%! C = categorical ([false, true, true, false, true]);
%! assert_equal (dispstrings (C), {'false', 'true', 'true', 'false', 'true'});
%! assert_equal (categories (C), {'false'; 'true'});
%! assert_equal (double (C), [1, 2, 2, 1, 2]);
%!error<categorical: 'Ordinal' variable indicator must be either false \(0\) or true \(1\).> ...
%! categorical (1, 'Ordinal', 3)
%!error<categorical: 'Ordinal' variable indicator must be either false \(0\) or true \(1\).> ...
%! categorical (1, 'Ordinal', 'true')
%!error<categorical: 'Protected' categories indicator must be either false \(0\) or true \(1\).> ...
%! categorical (1, 'Protected', 3)
%!error<categorical: 'Protected' categories indicator must be either false \(0\) or true \(1\).> ...
%! categorical (1, 'Protected', 'true')
%!error<categorical: VALUESET contains multiple missing values.> ...
%! categorical (categorical (1:5), categorical ([1, NaN, NaN]))
%!error<categorical: VALUESET contains non-unique values.> ...
%! categorical (categorical (1:5), categorical ([1, 1, 1]))
%!error<categorical: VALUESET with missing value requires CATNAMES.> ...
%! categorical (categorical (1:5), categorical ([1, 2, NaN]))
%!error<categorical: invalid type of CATNAMES.> ...
%! categorical (categorical (1:5), categorical ([1, 2, NaN]), [1, 2])
%!error<categorical: CATNAMES and VALUESET lengths do not match.> ...
%! categorical (categorical (1:5), categorical ([1, 2, NaN]), {'1', '2'})
%!error<categorical: CATNAMES contain empty or missing strings.> ...
%! categorical (categorical (1:5), categorical ([1, 2, NaN]), {'1', '2', ''})
%!error<categorical: invalid type of CATNAMES.> ...
%! categorical (categorical (1:5), categorical ([1, 2, 3]), [1, 2])
%!error<categorical: CATNAMES and VALUESET lengths do not match.> ...
%! categorical (categorical (1:5), categorical ([1, 2, 3]), {'1', '2'})
%!error<categorical: CATNAMES contain empty or missing strings.> ...
%! categorical (categorical (1:5), categorical ([1, 2, 3]), {'1', '2', ''})
%!error<categorical: incompatible types of VALUESET and X.> ...
%! categorical (categorical (1:5), [1, 2, 3])
%!error<categorical: invalid input type for X.> categorical ({1})
%!error<categorical: incompatible types of VALUESET and X.> ...
%! categorical ({'1', '2', '3'}, [1, 2, 3])
%!error<categorical: incompatible types of VALUESET and X.> ...
%! categorical (days ([1, 2, 3]), [1, 2, 3])
%!error<categorical: VALUESET with missing value requires CATNAMES.> ...
%! categorical (1:5, [1, 2, NaN])
%!error<categorical: VALUESET contains multiple missing values.> ...
%! categorical (1:5, [1, NaN, NaN], {'a', 'b', 'c'})
%!error<categorical: VALUESET contains non-unique values.> ...
%! categorical (1:5, [1, 2, 2])
%!error<categorical: VALUESET contains non-unique values.> ...
%! categorical (1:5, [2, 2, NaN], {'a', 'b', 'c'})
%!error<categorical: invalid type of CATNAMES.> ...
%! categorical (1:5, [1, 2, NaN], ['a'; 'b'; 'c'])
%!error<categorical: invalid type of CATNAMES.> ...
%! categorical (1:5, [1, 2, NaN], days ([1, 2, 3]))
%!error<categorical: CATNAMES and VALUESET lengths do not match.> ...
%! categorical (1:5, [1, 2, NaN], {'a', 'b'})
%!error<categorical: CATNAMES contain empty or missing strings.> ...
%! categorical (1:5, [1, 2, NaN], {'a', 'b', ''})

%!test
%! C = categorical ([1, 2, 1, NaN, 3, 2, 3]);
%! assert_equal (cellstr (C), dispstrings (C));

%!test
%! C = categorical ([1, 2, 1, NaN, 3, 2, 3]);
%! assert_equal (char (C), char (cellstr (C)));
%! assert_equal (size (char (C)), [7, 11]);
%! assert_equal (class (char (C)), 'char');

%!test
%! C = categorical ([1, 2, 1, NaN, 3, 2, 3]);
%! assert_equal (double (C), [1, 2, 1, NaN, 3, 2, 3]);
%! assert_equal (single (C), single ([1, 2, 1, NaN, 3, 2, 3]));
%! assert_equal (int64 (C), int64 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (int32 (C), int32 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (int16 (C), int16 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (int8 (C), int8 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (uint64 (C), uint64 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (uint32 (C), uint32 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (uint16 (C), uint16 ([1, 2, 1, 0, 3, 2, 3]));
%! assert_equal (uint8 (C), uint8 ([1, 2, 1, 0, 3, 2, 3]));

%!test
%! C = categorical (1:256);
%! assert_equal (int8 (C), int8 ([1:126, 127*ones(1, 130)]));
%! assert_equal (uint8 (C), uint8 ([1:255, 255]));

################################################################################
##                         ** Summary Information **                          ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'summary'          'categories'       'countcats'        'length'          ##
## 'size'             'ndims'            'numel'            'keyHash'         ##
##                                                                            ##
################################################################################

%!test
%! C = categorical (repmat ([1, 3; 4, 2], 3, 3, 2));
%! s = summary (C, 1);
%! assert_equal (s.Size, [6, 6, 2]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'1'; '2'; '3'; '4'});
%! assert_equal (s.Counts, countcats (C, 1));
%! assert_equal (size (s.Counts), [4, 6, 2]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 1));
%! assert_equal (size (s.NumMissing), [1, 6, 2]);
%!test
%! C = categorical (repmat ([1, 3; 4, 2], 3, 3, 2));
%! s = summary (C, 2);
%! assert_equal (s.Size, [6, 6, 2]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'1'; '2'; '3'; '4'});
%! assert_equal (s.Counts, countcats (C, 2));
%! assert_equal (size (s.Counts), [6, 4, 2]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 2));
%! assert_equal (size (s.NumMissing), [6, 1, 2]);
%!test
%! C = categorical (repmat ([1, 3; 4, 2], 3, 3, 2));
%! s = summary (C, 3);
%! assert_equal (s.Size, [6, 6, 2]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'1'; '2'; '3'; '4'});
%! assert_equal (s.Counts, countcats (C, 3));
%! assert_equal (size (s.Counts), [6, 6, 4]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 3));
%! assert_equal (size (s.NumMissing), [6, 6]);
%!test
%! C = categorical (repmat ([1, 3; 4, 2], 3, 3, 2));
%! s = summary (C, 4);
%! assert_equal (s.Size, [6, 6, 2]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'1'; '2'; '3'; '4'});
%! assert_equal (s.Counts, countcats (C, 4));
%! assert_equal (size (s.Counts), [6, 6, 2, 4]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 4));
%! assert_equal (size (s.NumMissing), [6, 6, 2]);
%!test
%! C = categorical (string ({'1', '2', ' ', '2'}), string ({' ', '1', '2'}), ...
%!                  {'a', 's', 'd'});
%! s = summary (C);
%! assert_equal (s.Size, [1, 4]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'a'; 's'; 'd'});
%! assert_equal (s.Counts, [1, 1, 2]);
%! assert_equal (s.NumMissing, 0);
%!test
%! C = categorical ({'A'; 'B'; 'C'; ''; 'C'; ''; 'C'; 'A'; ''});
%! C = repmat (C, 1, 4, 3);
%! s = summary (C);
%! assert_equal (s.Size, [9, 4, 3]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'A'; 'B'; 'C'});
%! assert_equal (s.Counts, countcats (C));
%! assert_equal (size (s.Counts), [3, 4, 3]);
%! assert_equal (s.NumMissing, sum (isundefined (C)));
%! assert_equal (size (s.NumMissing), [1, 4, 3]);
%!test
%! C = categorical ({'A'; 'B'; 'C'; ''; 'C'; ''; 'C'; 'A'; ''});
%! C = repmat (C, 1, 4, 3);
%! s = summary (C, 2);
%! assert_equal (s.Size, [9, 4, 3]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'A'; 'B'; 'C'});
%! assert_equal (s.Counts, countcats (C, 2));
%! assert_equal (size (s.Counts), [9, 3, 3]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 2));
%! assert_equal (size (s.NumMissing), [9, 1, 3]);
%!test
%! C = categorical ({'A'; 'B'; 'C'; ''; 'C'; ''; 'C'; 'A'; ''});
%! C = repmat (C, 1, 4, 3);
%! s = summary (C, 3);
%! assert_equal (s.Size, [9, 4, 3]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'A'; 'B'; 'C'});
%! assert_equal (s.Counts, countcats (C, 3));
%! assert_equal (size (s.Counts), [9, 4, 3]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 3));
%! assert_equal (size (s.NumMissing), [9, 4]);
%!test
%! C = categorical ({'A'; 'B'; 'C'; ''; 'C'; ''; 'C'; 'A'; ''});
%! C = repmat (C, 1, 4, 3);
%! s = summary (C, 4);
%! assert_equal (s.Size, [9, 4, 3]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'A'; 'B'; 'C'});
%! assert_equal (s.Counts, countcats (C, 4));
%! assert_equal (size (s.Counts), [9, 4, 3, 3]);
%! assert_equal (s.NumMissing, sum (isundefined (C), 4));
%! assert_equal (size (s.NumMissing), [9, 4, 3]);
%!test
%! s = summary (categorical ([1, 2, 1, 3, 2, 3, 2, 1, 3, 2], [1, 2, 3], ...
%!                           {'l','m','h'}, 'Ordinal', true), 1, ...
%!              'Statistics', {'min', 'nummissing', 'counts', 'max', 'median'});
%! assert_equal (s.Size, [1, 10]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'l'; 'm'; 'h'});
%! assert_equal (size (s.Counts), [3, 10]);
%! C = categorical ({'l','m','l','h','m','h','m','l','h','m'}, ...
%!                  {'l','m','h'}, {'l','m','h'}, 'Ordinal', true);
%! assert_equal (s.Min, C);
%! assert_equal (s.NumMissing, zeros (1, 10));
%! assert_equal (s.Max, C);
%! assert_equal (s.Median, C);
%!test
%! s = summary (categorical ([1, 2, 1, 3, 2, 3, 2, 1, 3, NaN], [1, 2, 3], ...
%!                           {'l','m','h'}, 'Ordinal', true), 1, ...
%!              'Statistics', {'min', 'nummissing', 'counts', 'max', 'median'});
%! assert_equal (s.Size, [1, 10]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'l'; 'm'; 'h'});
%! assert_equal (size (s.Counts), [3, 10]);
%! C = categorical ({'l','m','l','h','m','h','m','l','h',''}, ...
%!                  {'l','m','h'}, {'l','m','h'}, 'Ordinal', true);
%! assert_equal (isequaln (s.Min, C), true);
%! assert_equal (s.NumMissing, [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
%! assert_equal (isequaln (s.Max, C), true);
%! assert_equal (isequaln (s.Median, C), true);
%!test
%! s = summary (categorical ([1, 2, 1, 3, 2, 3, 2, 1, 3, NaN], [1, 2, 3], ...
%!                           {'l','m','h'}, 'Ordinal', true), 1, ...
%!              'Statistics', {'min', 'nummissing', 'max', 'median', 'mode'});
%! assert_equal (s.Size, [1, 10]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'l'; 'm'; 'h'});
%! assert_equal (size (s.Min), [1, 10]);
%! assert_equal (s.NumMissing, [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
%! assert_equal (size (s.Max), [1, 10]);
%! assert_equal (size (s.Median), [1, 10]);
%! assert_equal (size (s.Mode), [1, 10]);
%!test
%! s = summary (categorical ([1, 2, 1, 3, 2, 3, 2, 1, 3, NaN], [1, 2, 3], ...
%!                           {'l','m','h'}, 'Ordinal', true), 1, ...
%!              'Statistics', {'min', 'counts', 'max', 'median', 'mode'});
%! assert_equal (s.Size, [1, 10]);
%! assert_equal (s.Type, 'categorical');
%! assert_equal (s.Categories, {'l'; 'm'; 'h'});
%! assert_equal (size (s.Counts), [3, 10]);
%! assert_equal (size (s.Min), [1, 10]);
%! assert_equal (size (s.Max), [1, 10]);
%! assert_equal (size (s.Median), [1, 10]);
%! assert_equal (size (s.Mode), [1, 10]);
%!error<categorical.summary: unrecognized input argument.> ...
%! summary (categorical ([1:3]), 2, 3)
%!error<categorical.summary: DIM must be a positive integer.> ...
%!  summary (categorical ([1:3]), 0)
%!error<categorical.summary: DIM must be a positive integer.> ...
%!  summary (categorical ([1:3]), [1, 2])
%!error<categorical.summary: DIM must be a positive integer.> ...
%!  summary (categorical ([1:3]), 'a')

%!assert_equal (categories (categorical), {})
%!assert_equal (categories (categorical (NaN (3), [1, 2, 3])), {'1'; '2'; '3'})
%!assert_equal (categories (categorical (1:5)), {'1'; '2'; '3'; '4'; '5'})

%!assert_equal (countcats (categorical), [])
%!assert_equal (countcats (categorical ([NaN, 1;NaN,2;NaN, NaN])), [0, 1; 0, 1])
%!assert_equal (countcats (categorical (NaN (3))), zeros (0, 3))
%!assert_equal (countcats (categorical (NaN (3), [1, 2, 3])), zeros (3))
%!assert_equal (countcats (categorical (NaN (2, 0, 3))), zeros (0, 0, 3))
%!assert_equal (countcats (categorical (NaN (2, 0, 3), [1, 2, 3])), zeros (3, 0, 3))
%!assert_equal (countcats (categorical (NaN (2, 0, 3), [1, 2, 3]), 2), zeros (2, 3, 3))
%!assert_equal (countcats (categorical (ones (3, 2, 0, 2))), ones (0, 2, 0, 2))
%!assert_equal (countcats (categorical (ones (3, 2, 0, 2)), 3), ones (3, 2, 0, 2))
%!error<categorical.countcats: DIM must be a positive integer.> ...
%! countcats (categorical (ones (3, 2, 0, 2)), 0)
%!error<categorical.countcats: DIM must be a positive integer.> ...
%! countcats (categorical (ones (3, 2, 0, 2)), [1, 2])
%!error<categorical.countcats: DIM must be a positive integer.> ...
%! countcats (categorical (ones (3, 2, 0, 2)), 'a')

%!assert_equal (length (categorical), 0)
%!assert_equal (length (categorical (ones (3, 2, 0, 2))), 0)
%!assert_equal (length (categorical (ones (2, 3, 4))), 4)
%!assert_equal (length (categorical (ones (3, 4, 2))), 4)
%!assert_equal (length (categorical (ones (3))), 3)

%!assert_equal (size (categorical), [0, 0])
%!assert_equal (size (categorical ([1:5])), [1, 5])
%!assert_equal (size (categorical (ones (2, 3, 4))), [2, 3, 4])
%!assert_equal (size (categorical (ones (2, 3, 4)), 1), 2)
%!assert_equal (size (categorical (ones (2, 3, 4)), 2), 3)
%!assert_equal (size (categorical (ones (2, 3, 4)), 3), 4)
%!assert_equal (size (categorical (ones (2, 3, 4)), 4), 1)
%!test
%! [d1, d2, d3] = size (categorical (ones (2, 3, 4)));
%! assert_equal (d1, 2);
%! assert_equal (d2, 3);
%! assert_equal (d3, 4);
%!test
%! [d1, d2] = size (categorical (ones (2, 3, 4)), 1, 2);
%! assert_equal (d1, 2);
%! assert_equal (d2, 3);
%!test
%! [d2, d3] = size (categorical (ones (2, 3, 4)), 2, 3);
%! assert_equal (d2, 3);
%! assert_equal (d3, 4);
%!test
%! [d2, d3] = size (categorical (ones (2, 3, 4)), [2, 3]);
%! assert_equal (d2, 3);
%! assert_equal (d3, 4);
%!error<categorical.size: nargout > ...
%! [d2, d3] = size (categorical (ones (2, 3, 4)), [2, 3, 4])

%!assert_equal (ndims (categorical), 2)
%!assert_equal (ndims (categorical (ones (2, 3, 4))), 3)
%!assert_equal (ndims (categorical (ones (2, 3, 0, 4))), 4)

%!assert_equal (numel (categorical), 0)
%!assert_equal (numel (categorical (1:5)), 5)
%!assert_equal (numel (categorical (ones (2, 3, 4))), 24)

%!assert (keyHash (categorical), uint64 (600793793929482348))
%!assert (keyHash (categorical (1:5)), uint64 (6763637898068551630))
%!assert (keyHash (categorical (ones (3))), uint64 (1656458728321551574))
%!assert (keyHash (categorical (ones (2, 3, 4))), uint64 (16817716959342937566))
%!assert (keyHash (categorical (NaN (3))), uint64 (8652545556995177325))
%!test
%! key = keyHash (categorical ([1, 2; 3, 4]));
%! assert (key, uint64 (3893744934052072694));
%!test
%! key = keyHash (categorical ([1, 2; 3, 4], [1, 2, 3, 4]));
%! assert (key, uint64 (3893744934052072694));
%!test
%! key = keyHash (categorical ([1, 2; 3, 4], [1, 2, 3, 4], {'a', 'b', 'c', 'd'}));
%! assert (key, uint64 (7189439461002363766));
%!test
%! key = keyHash (categorical (1:3, [1, 2, 3]));
%! assert (key, uint64 (17803270835922091242));
%!test
%! base_key = uint64 (2342124352342344234);
%! key = keyHash (categorical (1:3, [1, 2, 3]), base_key);
%! assert (key, uint64 (14454225355392922673));
%!error<categorical.keyHash: BASE must be a UINT64 scalar.> ...
%! keyHash (categorical (1:5), uint64 ([1, 2]))
%!error<categorical.keyHash: BASE must be a UINT64 scalar.> ...
%! keyHash (categorical (1:5), 2231107818551636405)

################################################################################
##                          ** Query Operations **                            ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'iscategory'       'iscolumn'         'isempty'          'isequal'         ##
## 'isequaln'         'ismatrix'         'ismember'         'ismissing'       ##
## 'isordinal'        'isprotected'      'isrow'            'isscalar'        ##
## 'issorted'         'issortedrows'     'isundefined'      'isvector'        ##
##                                                                            ##
################################################################################

%!assert_equal (iscategory (categorical, ''), false)
%!assert_equal (iscategory (categorical (1:3), '1'), true)
%!assert_equal (iscategory (categorical (1:3), '4'), false)
%!assert_equal (iscategory (categorical (1:3), string ('1')), true)
%!assert_equal (iscategory (categorical (1:3), string ('4')), false)
%!assert_equal (iscategory (categorical (1:3), {'1', '2'}), [true, true])
%!assert_equal (iscategory (categorical (1:3), {'1'; '2'}), [true; true])
%!assert_equal (iscategory (categorical (1:3), {'1', '4', '3'}), logical ([1, 0, 1]))
%!test
%! C = categorical ([], [1, 2, 3], {'A', 'B', 'C'});
%! assert_equal (iscategory (C, 'A'), true);
%! assert_equal (iscategory (C, '1'), false);
%! assert_equal (iscategory (C, string ('A')), true);
%! assert_equal (iscategory (C, string (1:3)), [false, false, false]);
%!error<categorical.iscategory: CATNAMES input argument is required.> ...
%! iscategory (categorical)

%!assert_equal (iscolumn (categorical), false)
%!assert_equal (iscolumn (categorical (1)), true)
%!assert_equal (iscolumn (categorical (1:2)), false)
%!assert_equal (iscolumn (categorical ([1:2]')), true)
%!assert_equal (iscolumn (categorical (ones (2))), false)

%!assert_equal (isempty (categorical), true)
%!assert_equal (isempty (categorical (1)), false)
%!assert_equal (isempty (categorical (ones (3, 2, 0, 3))), true)
%!assert_equal (isempty (categorical ([], [1, 2, 3], {'A', 'B', 'C'})), true)
%!assert_equal (isempty (categorical (NaN (2, 0), [1, 2, 3], {'A', 'B', 'C'})), true)

%!assert_equal (isequal (categorical ([1, 1, 1]), ...
%!                       categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequal (categorical ([1, NaN, 1]), ...
%!                       categorical ([2, NaN, 2], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal (categorical ([1, 1, NaN]), ...
%!                       categorical ([2, 2, NaN], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal (categorical ([1, 1, 1]), ...
%!                       categorical ([1, 1, 1], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal (categorical ([1, 1, 1], [1, 2]), ...
%!                       categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequal (categorical ([NaN, 1, 1], [1, 2]), ...
%!                       categorical ([NaN, 2, 2], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal (categorical ([1, NaN, 1], [1, 2]), ...
%!                       categorical ([2, NaN, 2], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal (categorical ([1, 1, 1], [1, 2]), ...
%!                       categorical ([1, 1, 1], [1, 2], {'2', '1'})), false)
%!assert_equal (isequal ({'1', '1', '1'}, categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequal (categorical ([1, 1, 1]), {'1', '1', '1'}), true)
%!assert_equal (isequal (string ({'1', '1', '1'}), ...
%!                       categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequal (categorical ([2, 2, 2], [1, 2], {'2', '1'}), ...
%!                       string ({'1', '1', '1'})), true)
%!assert_equal (isequal (missing, categorical), false)
%!assert_equal (isequal (missing, categorical (NaN)), false)
%!assert_equal (isequal (categorical (1), string (1)), true)
%!assert_equal (isequal (string (1), categorical (1)), true)
%!assert_equal (isequal (categorical (NaN), string (missing)), false)
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'});
%! C2 = categorical ([1, 1, 1], [1, 2], {'A', 'B'});
%! assert_equal (isequal (C1, C2), true);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'});
%! C2 = categorical ([2, 2, 2], [1, 2], {'B', 'A'});
%! assert_equal (isequal (C1, C2), true);
%! assert_equal (isequal (C1, C2, {'A', 'A', 'A'}), true);
%! assert_equal (isequal (C1, C2, {'A', '', 'A'}), false);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'});
%! C2 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! assert_equal (isequal (C1, C2), false);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([1, 1, 1], [1, 2], {'A', 'B'});
%! assert_equal (isequal (C1, C2), false);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! assert_equal (isequal (C1, C2), true);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([2, 2, 2], [1, 2], {'B', 'A'}, 'Ordinal', true);
%! assert_equal (isequal (C1, C2), false);


%!assert_equal (isequaln (categorical ([1, 1, 1]), ...
%!                        categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (categorical ([1, NaN, 1]), ...
%!                        categorical ([2, NaN, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (categorical ([1, 1, NaN]), ...
%!                        categorical ([2, 2, NaN], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (categorical ([1, 1, 1], [1, 2]), ...
%!                        categorical ([2, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (categorical ([NaN, 1, 1], [1, 2]), ...
%!                        categorical ([NaN, 2, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (categorical ([1, NaN, 1], [1, 2]), ...
%!                        categorical ([2, NaN, 2], [1, 2], {'2', '1'})), true)
%!assert_equal (isequaln (missing, categorical), false)
%!assert_equal (isequaln (missing, categorical (NaN)), true)
%!assert_equal (isequaln (categorical (NaN), string (missing)), true)
%!assert_equal (isequaln (repmat (missing, 2), categorical (NaN (2))), true)
%!test
%! C1 = categorical ([1, 1, NaN], [1, 2], {'A', 'B'});
%! C2 = categorical ([1, 1, NaN], [1, 2], {'A', 'B'});
%! assert_equal (isequaln (C1, C2), true);
%!test
%! C1 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'});
%! C2 = categorical ([2, NaN, 2], [1, 2], {'B', 'A'});
%! assert_equal (isequaln (C1, C2), true);
%! assert_equal (isequaln (C1, C2, {'A', 'A', 'A'}), false);
%! assert_equal (isequaln (C1, C2, {'A', '', 'A'}), true);
%!test
%! C1 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'});
%! C2 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! assert_equal (isequaln (C1, C2), false);
%!test
%! C1 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'});
%! assert_equal (isequaln (C1, C2), false);
%!test
%! C1 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! assert_equal (isequaln (C1, C2), true);
%!test
%! C1 = categorical ([1, 1, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([2, 2, 2], [1, 2], {'B', 'A'}, 'Ordinal', true);
%! assert_equal (isequaln (C1, C2), false);
%!test
%! C1 = categorical ([1, NaN, 1], [1, 2], {'A', 'B'}, 'Ordinal', true);
%! C2 = categorical ([2, NaN, 2], [1, 2], {'B', 'A'}, 'Ordinal', true);
%! assert_equal (isequaln (C1, C2), false);

%!assert_equal (ismatrix (categorical), true)
%!assert_equal (ismatrix (categorical (NaN)), true)
%!assert_equal (ismatrix (categorical (ones (1, 0))), true)
%!assert_equal (ismatrix (categorical (ones (2, 1))), true)
%!assert_equal (ismatrix (categorical (ones (2, 2, 1))), true)
%!assert_equal (ismatrix (categorical (ones (2, 1, 2))), false)
%!assert_equal (ismatrix (categorical (ones (2, 0, 2))), false)

%!assert_equal (ismember (categorical (1:3), '1'), [true, false, false])
%!assert_equal (ismember (categorical (1:3), string ('1')), [true, false, false])
%!assert_equal (ismember (categorical (1:3), {'1', '2'}), [true, true, false])
%!assert_equal (ismember (categorical (1:3), string ({'1', '2'})), [true, true, false])
%!assert_equal (ismember ('1', categorical (1:3)), true)
%!assert_equal (ismember ({'1', '2'}, categorical (1:3)), [true, true])
%!assert_equal (ismember ({'1', '2'}, categorical ([1:3]')), [true, true])
%!assert_equal (ismember ({'1'; '2'}, categorical (1:3)), [true; true])
%!assert_equal (ismember ({'1', '2'}, categorical ([1, 2, 1, 3, 2, 3, 2, 1])), ...
%!              [true, true])
%!assert_equal (ismember (categorical ([1, 2, 1, 3, 2, 3, 2, 1]), {'1','2'}), ...
%!              logical ([1, 1, 1, 0, 1, 0, 1, 1]))
%!assert_equal (ismember (categorical ([1, 2, 1, 3, 2, 3, 2, 1]), categorical (1:3)), ...
%!              true (1, 8))
%!assert_equal (ismember (categorical (1:3), categorical([1, 2, 1, 3, 2, 3, 2, 1])), ...
%!              true (1, 3))
%!assert_equal (ismember (categorical ([1, 2, 1, 3, 2, 3, 2, 1]), categorical (1:2)), ...
%!              logical ([1, 1, 1, 0, 1, 0, 1, 1]))
%!assert_equal (ismember (categorical ([1; 2]), categorical ([1, 2, 1, 3, 2, 3, 2, 1])), ...
%!              [true; true])
%!test
%! C1 = categorical ([1, 2, 1, 3, 2, 3, 2, NaN]);
%! C2 = categorical ([1, NaN, 3]);
%! assert_equal (ismember (C1, C2), logical ([1, 0, 1, 1, 0, 1, 0, 0]));
%! assert_equal (ismember (C2, C1), [true, false, true]);
%! assert_equal (ismember (C1', C2), logical ([1; 0; 1; 1; 0; 1; 0; 0]));
%! assert_equal (ismember (C2', C1), [true; false; true]);
%!test
%! C1 = categorical ([1, 2, 1, 3, 2, 3, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! C2 = categorical ([1, NaN, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (ismember (C1, C2), logical ([1, 0, 1, 1, 0, 1, 0, 0]));
%! assert_equal (ismember (C2, C1), [true, false, true]);
%! assert_equal (ismember (C1', C2), logical ([1; 0; 1; 1; 0; 1; 0; 0]));
%! assert_equal (ismember (C2', C1), [true; false; true]);
%!test
%! C1 = categorical ([1, 2, 1, 3, 2, 3, 2, NaN], [3, 2, 1], 'Ordinal', true);
%! C2 = categorical ([1, NaN, 3], [3, 2, 1], 'Ordinal', true);
%! assert_equal (ismember (C1, C2), logical ([1, 0, 1, 1, 0, 1, 0, 0]));
%! assert_equal (ismember (C2, C1), [true, false, true]);
%! assert_equal (ismember (C1', C2), logical ([1; 0; 1; 1; 0; 1; 0; 0]));
%! assert_equal (ismember (C2', C1), [true; false; true]);
%!test
%! C1 = categorical ([1, 2, 1, 3, 2, 3, 2, NaN], [1, 2, 3], {'a', 'b', 'c'}, 'Ordinal', true);
%! C2 = categorical ([1, NaN, 3], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (ismember (C1, C2), logical ([1, 0, 1, 1, 0, 1, 0, 0]));
%! assert_equal (ismember (C2, C1), [true, false, true]);
%! assert_equal (ismember (C1', C2), logical ([1; 0; 1; 1; 0; 1; 0; 0]));
%! assert_equal (ismember (C2', C1), [true; false; true]);
%!assert_equal (ismember ({'1', '2', '2'}, categorical (1:3), 'rows'), false)
%!assert_equal (ismember ({'1', '2', '2'; '1', '2', '3'}, ...
%!                        categorical (1:3), 'rows'), [false; true])
%!assert_equal (ismember ({'1', '2', '3'}, categorical (1:3), 'rows'), true)
%!assert_equal (ismember ({'1', '2', '3'}, ...
%!                        categorical ([1, 2, 3; 2, 2, 3]), 'rows'), true)
%!assert_equal (ismember (string ({'1', '2', '2'}), categorical (1:3), 'rows'), ...
%!              false)
%!assert_equal (ismember (string ({'1', '2', '2'; '1', '2', '3'}), ...
%!                        categorical (1:3), 'rows'), [false; true])
%!assert_equal (ismember (string ({'1', '2', '3'}), ...
%!                        categorical (1:3), 'rows'), true)
%!assert_equal (ismember (string ({'1', '2', '3'}), ...
%!                        categorical ([1, 2, 3; 2, 2, 3]), 'rows'), true)
%!assert_equal (ismember (categorical (1:3), {'1', '2', '2'}, 'rows'), false)
%!assert_equal (ismember (categorical (1:3), {'1', '2', '2'; '1', '2', '3'}, 'rows'), true)
%!assert_equal (ismember (categorical (1:3), {'1', '2', '3'}, 'rows'), true)
%!assert_equal (ismember (categorical ([1, 2, 3; 2, 2, 3]), ...
%!                        {'1', '2', '3'}, 'rows'), [true; false])
%!error<categorical.ismember: A and B must be categorical arrays, or one of them> ...
%! ismember (1, categorical)
%!error<categorical.ismember: A and B must be categorical arrays, or one of them> ...
%! ismember (categorical, {1})
%!error<categorical.ismember: all options must be character vectors.> ...
%! ismember (categorical, categorical, 1)
%!error<categorical.ismember: only 'rows' and 'legacy' are valid options.> ...
%! ismember (categorical, categorical, 'option')
%!error<categorical.ismember: 'rows' applies only to 2-D matrices.> ...
%! ismember (categorical (ones (2, 3, 4)), categorical, 'rows')
%!error<categorical.ismember: 'rows' requires same number of columns.> ...
%! ismember ({'1', '2'}, categorical (1:3), 'rows')
%!error<categorical.ismember: 'rows' requires same number of columns.> ...
%! ismember (categorical (1:3), {'1', '2'}, 'rows')
%!error<categorical.ismember: ordinal categorical arrays must have the same ordered set of categories.> ...
%! ismember (categorical (1, 'Ordinal', true), categorical (2, 'Ordinal', true))
%!error<categorical.ismember: both categorical arrays must be ordinal.> ...
%! ismember (categorical (1, 'Ordinal', true), categorical (1))

%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), '2'), logical ([0, 1, 0, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), {'2'}), logical ([0, 1, 0, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), string ('2')), ...
%!              logical ([0, 1, 0, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), {'2', '4'}), logical ([0, 1, 0, 1]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), string ({'2', '4'})), ...
%!              logical ([0, 1, 0, 1]))
%!assert_equal (ismissing (categorical ([1; 2; NaN; 4]), {'2', '4'}), logical ([0; 1; 0; 1]))
%!assert_equal (ismissing (categorical ([1, 2; NaN, 4]), string ({'2'; '4'})), ...
%!              logical ([0, 1; 0, 1]))
%!assert_equal (ismissing (categorical ([1, 2; NaN, 3], [1:3], {'A', 'B', 'C'}), {'A', 'B'}), ...
%!              logical ([1, 1; 0, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 3], [1:3], {'A', 'B', 'C'}), {'A', 'C'}), ...
%!              logical ([1, 0, 0, 1]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 3], [1:3], {'A', 'B', 'C'}), {'A', '<undefined>'}), ...
%!              logical ([1, 0, 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), categorical (NaN)), ...
%!              logical ([0, 0, 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2, NaN, 4]), categorical ([NaN, 5, 2])), ...
%!              logical ([0, 1, 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2, 5, 4]), categorical ([NaN, 5, 2])), ...
%!              logical ([0, 1, 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2; 5, 4]), categorical ([NaN, 5, 2])), ...
%!              logical ([0, 1; 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2, 5, 4]), categorical ([NaN; 5; 2])), ...
%!              logical ([0, 1, 1, 0]))
%!assert_equal (ismissing (categorical ([1, 2; 5, 4]), categorical ([NaN; 5; 2])), ...
%!              logical ([0, 1; 1, 0]))
%!error<categorical.ismissing: too many input arguments.> ...
%! ismissing (categorical ([1, 2, 5, 4]), 1, 2)
%!error<categorical.ismissing: INDICATOR specified as a character vector must be a row vector.> ...
%! ismissing (categorical ([1, 2, 5, 4]), ['1';'2'])
%!error<categorical.ismissing: INDICATOR must be a text representation of categories or categorical.> ...
%! ismissing (categorical ([1, 2, 5, 4]), 1)
%!error<categorical.ismissing: INDICATOR must be a vector.> ...
%! ismissing (categorical ([1, 2, 5, 4]), {'a', 'f'; 'e', 'r'})

%!assert_equal (isordinal (categorical (1)), false)
%!assert_equal (isordinal (categorical (1, 'Ordinal', true)), true)
%!assert_equal (isordinal (categorical (1, 'Protected', true)), false)
%!assert_equal (isordinal (categorical (1:3, 1:3, {'a', 'b', 'c'})), false)
%!assert_equal (isordinal (categorical (1:3, 1:3, {'a', 'b', 'c'}, 'Ordinal', true)), true)
%!assert_equal (isordinal (categorical (1:3, 1:3, {'a', 'b', 'c'}, 'Protected', true)), false)

%!assert_equal (isprotected (categorical (1)), false)
%!assert_equal (isprotected (categorical (1, 'Ordinal', true)), true)
%!assert_equal (isprotected (categorical (1, 'Protected', true)), true)
%!assert_equal (isprotected (categorical (1:3, 1:3, {'a', 'b', 'c'})), false)
%!assert_equal (isprotected (categorical (1:3, 1:3, {'a', 'b', 'c'}, 'Ordinal', true)), true)
%!assert_equal (isprotected (categorical (1:3, 1:3, {'a', 'b', 'c'}, 'Protected', true)), true)

%!assert_equal (isrow (categorical), false)
%!assert_equal (isrow (categorical (NaN)), true)
%!assert_equal (isrow (categorical (1)), true)
%!assert_equal (isrow (categorical (1:4)), true)
%!assert_equal (isrow (categorical (ones (2))), false)
%!assert_equal (isrow (categorical (ones (4, 1))), false)
%!assert_equal (isrow (categorical (ones (0, 1))), false)
%!assert_equal (isrow (categorical (ones (1, 0))), true)

%!assert_equal (isscalar (categorical), false)
%!assert_equal (isscalar (categorical (NaN)), true)
%!assert_equal (isscalar (categorical (1)), true)
%!assert_equal (isscalar (categorical (1:4)), false)
%!assert_equal (isscalar (categorical (ones (2))), false)
%!assert_equal (isscalar (categorical (ones (4, 1))), false)
%!assert_equal (isscalar (categorical (ones (0, 1))), false)
%!assert_equal (isscalar (categorical (ones (1, 0))), false)

%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C)), true);
%! assert (issorted (sort (C), 'ascend'), true);
%! assert (issorted (sort (C), 'descend'), false);
%! assert (issorted (sort (C), 'monotonic'), true);
%! assert (issorted (sort (C), 'strictascend'), false);
%! assert (issorted (sort (C), 'strictdescend'), false);
%! assert (issorted (sort (C), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C, 2), 2), true);
%! assert (issorted (sort (C, 2), 2, 'ascend'), true);
%! assert (issorted (sort (C, 2), 2, 'descend'), false);
%! assert (issorted (sort (C, 2), 2, 'monotonic'), true);
%! assert (issorted (sort (C, 2), 2, 'strictascend'), true);
%! assert (issorted (sort (C, 2), 2, 'strictdescend'), false);
%! assert (issorted (sort (C, 2), 2, 'strictmonotonic'), true);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C, 2), 'ascend'), false);
%! assert (issorted (sort (C, 2), 'descend'), false);
%! assert (issorted (sort (C, 2), 'monotonic'), false);
%! assert (issorted (sort (C, 2), 'strictascend'), false);
%! assert (issorted (sort (C, 2), 'strictdescend'), false);
%! assert (issorted (sort (C, 2), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C, 'descend'), 'ascend'), false);
%! assert (issorted (sort (C, 'descend'), 'descend'), true);
%! assert (issorted (sort (C, 'descend'), 'monotonic'), true);
%! assert (issorted (sort (C, 'descend'), 'strictascend'), false);
%! assert (issorted (sort (C, 'descend'), 'strictdescend'), false);
%! assert (issorted (sort (C, 'descend'), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'ascend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'descend'), true);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'monotonic'), true);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'strictascend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'strictdescend'), true);
%! assert (issorted (sort (C, 2, 'descend'), 2, 'strictmonotonic'), true);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issorted (sort (C, 2, 'descend'), 'ascend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 'descend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 'monotonic'), false);
%! assert (issorted (sort (C, 2, 'descend'), 'strictascend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 'strictdescend'), false);
%! assert (issorted (sort (C, 2, 'descend'), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, 5]);
%! assert (issorted (sort (C), 'ascend'), true);
%! assert (issorted (sort (C), 'descend'), false);
%! assert (issorted (sort (C), 'monotonic'), true);
%! assert (issorted (sort (C), 'strictascend'), true);
%! assert (issorted (sort (C), 'strictdescend'), false);
%! assert (issorted (sort (C), 'strictmonotonic'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, 5]);
%! assert (issorted (sort (C, 'descend'), 'ascend'), false);
%! assert (issorted (sort (C, 'descend'), 'descend'), true);
%! assert (issorted (sort (C, 'descend'), 'monotonic'), true);
%! assert (issorted (sort (C, 'descend'), 'strictascend'), false);
%! assert (issorted (sort (C, 'descend'), 'strictdescend'), true);
%! assert (issorted (sort (C, 'descend'), 'strictmonotonic'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C)), true);
%! assert (issorted (sort (C), 'ascend'), true);
%! assert (issorted (sort (C), 'descend'), false);
%! assert (issorted (sort (C), 'monotonic'), true);
%! assert (issorted (sort (C), 'strictascend'), false);
%! assert (issorted (sort (C), 'strictdescend'), false);
%! assert (issorted (sort (C), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 'descend'), 'ascend'), false);
%! assert (issorted (sort (C, 'descend'), 'descend'), true);
%! assert (issorted (sort (C, 'descend'), 'monotonic'), true);
%! assert (issorted (sort (C, 'descend'), 'strictascend'), false);
%! assert (issorted (sort (C, 'descend'), 'strictdescend'), false);
%! assert (issorted (sort (C, 'descend'), 'strictmonotonic'), false);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 'MissingPlacement', 'auto')), true);
%! assert (issorted (sort (C, 'MissingPlacement', 'first')), false);
%! assert (issorted (sort (C, 'MissingPlacement', 'first'), ...
%!                   'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 'descend', 'MissingPlacement', 'auto'), 'descend'), true);
%! assert (issorted (sort (C, 'descend', 'MissingPlacement', 'last'), 'descend'), false);
%! assert (issorted (sort (C, 'descend', 'MissingPlacement', 'first'), ...
%!                   'descend', 'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 2, 'MissingPlacement', 'auto'), 2), true);
%! assert (issorted (sort (C, 2, 'MissingPlacement', 'first'), 2), false);
%! assert (issorted (sort (C, 2, 'MissingPlacement', 'first'), 2, ...
%!                   'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 2, 'descend', 'MissingPlacement', 'auto'), 2, 'descend'), true);
%! assert (issorted (sort (C, 2, 'descend', 'MissingPlacement', 'last'), 2, 'descend'), false);
%! assert (issorted (sort (C, 2, 'descend', 'MissingPlacement', 'first'), 2, ...
%!                   'descend', 'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 2, string ('ascend'), 'MissingPlacement', 'auto'), 2), true);
%! assert (issorted (sort (C, 2, string ('ascend'), 'MissingPlacement', 'first'), 2), false);
%! assert (issorted (sort (C, 2, string ('ascend'), 'MissingPlacement', 'first'), 2, ...
%!                   'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2; 2, 3; 3, 4; 4, NaN]);
%! assert (issorted (sort (C, 2, string ('descend'), 'MissingPlacement', 'auto'), 2, 'descend'), true);
%! assert (issorted (sort (C, 2, string ('descend'), 'MissingPlacement', 'last'), 2, 'descend'), false);
%! assert (issorted (sort (C, 2, string ('descend'), 'MissingPlacement', 'first'), 2, ...
%!                   'descend', 'MissingPlacement', 'first'), true);
%!test  ## strictmonotonic honours an explicit MissingPlacement argument
%! assert (issorted (categorical ([3, 2, 1]), 'strictmonotonic', ...
%!                   'MissingPlacement', 'last'), true);
%! assert (issorted (categorical ([3, 2, 1]), 'strictmonotonic', ...
%!                   'MissingPlacement', 'first'), true);
%! assert (issorted (categorical ([1, 1, 2]), 'strictmonotonic', ...
%!                   'MissingPlacement', 'last'), false);
%!error<categorical.issorted: invalid value for 'MissingPlacement'.> ...
%! issorted (sort (categorical), 'MissingPlacement', 'some')
%!error<categorical.issorted: invalid DIRECTION value.> ...
%! issorted (sort (categorical), 'asd')

%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! assert (issortedrows (sortrows (C)), true);
%! assert (issortedrows (sortrows (C, 1)), true);
%! assert (issortedrows (sortrows (C, 1), -1), false);
%! assert (issortedrows (sortrows (C, 1), 1), true);
%! assert (issortedrows (sortrows (C, 2)), false);
%! assert (issortedrows (sortrows (C, 2), 2), true);
%! assert (issortedrows (sortrows (C, 2), -2), false);
%! assert (issortedrows (sortrows (C, -2), 2), false);
%! assert (issortedrows (sortrows (C, -2), -2), true);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C)), true);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, -2, 'MissingPlacement', 'first')), false);
%! assert (issortedrows (sortrows (C, -2, 'MissingPlacement', 'first'), -2), true);
%! assert (issortedrows (sortrows (C, -2, 'MissingPlacement', 'last'), -2), false);
%! assert (issortedrows (sortrows (C, -2, 'MissingPlacement', 'last'), -2, ...
%!                       'MissingPlacement', 'last'), true);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, 2, 'MissingPlacement', 'last')), false);
%! assert (issortedrows (sortrows (C, 2, 'MissingPlacement', 'last'), 2), true);
%! assert (issortedrows (sortrows (C, 2, 'MissingPlacement', 'first'), 2), false);
%! assert (issortedrows (sortrows (C, 2, 'MissingPlacement', 'first'), 2, ...
%!                       'MissingPlacement', 'first'), true);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, [1, -2, 3]), [1, -2, 3]), true);
%! assert (issortedrows (sortrows (C, [1, 2, -3]), [1, -2, 3]), false);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, string ({'ascend', 'descend', 'ascend'})), ...
%!                       string ({'ascend', 'descend', 'ascend'})), true);
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, string ({'ascend', 'descend', 'ascend'})), ...
%!                       string ({'descend', 'ascend', 'ascend'})), false);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, [2, 3], string ({'descend', 'ascend'})), ...
%!                       [2, 3], string ({'descend', 'ascend'})), true);
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, [2, 3], string ({'ascend', 'descend'})), ...
%!                       [2, -3]), true);
%! assert (issortedrows (sortrows (C, [2, 3], string ({'descend', 'descend'})), ...
%!                       [2, -3], string ({'descend', 'ascend'})), false);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! assert (issortedrows (sortrows (C, [1, -2, 3], 'MissingPlacement', 'first'), ...
%!         [1, -2, 3], 'MissingPlacement', 'first'), true);
%! assert (issortedrows (sortrows (C, [-1, -2, -3], 'MissingPlacement', 'first'), ...
%!         [-1, -2, -3]), true);
%! assert (issortedrows (sortrows (C, [-1, -2, -3], 'MissingPlacement', 'first'), ...
%!         [1, -2,-3], string ({'descend', 'descend', 'descend'})), true);
%!test  ## strict issortedrows semantics, verified against MATLAB R2026a
%! assert (issortedrows (categorical ([1, 3; 1, 4]), 'strictascend'), false);
%! assert (issortedrows (categorical ([1, 3; 1, 3]), 'strictascend'), false);
%! assert (issortedrows (categorical ([1, 3; 1, 4]), ...
%!                       {'strictascend', 'ascend'}), false);
%! assert (issortedrows (categorical ([1, 3; 2, 3]), ...
%!                       {'ascend', 'strictascend'}), true);
%! assert (issortedrows (categorical ([2, 4; 1, 3]), 'monotonic'), true);
%! assert (issortedrows (categorical ([2, 4; 1, 3]), 'descend'), true);
%! assert (issortedrows (categorical ([1, 4; 2, 3]), 2, 'strictascend'), false);
%! assert (issortedrows (categorical ([2, 3; 1, 4]), 2, 'strictascend'), true);
%!test  ## strict issortedrows with missing values (vs MATLAB R2026a)
%! assert (issortedrows (categorical ([1, 3; NaN, 4]), ...
%!                       {'strictascend', 'ascend'}), false);
%! assert (issortedrows (categorical ([1, 3; NaN, 4]), ...
%!                       {'ascend', 'strictascend'}), true);
%! assert (issortedrows (categorical ([NaN, 3; 2, 4]), 2, 'strictascend'), true);
%! assert (issortedrows (categorical ([1, NaN; 2, 4]), 'strictascend'), true);

%!assert (isundefined (categorical ([1:10])), false (1, 10))
%!assert (isundefined (categorical ([1, 2, NaN, 4, NaN])), logical ([0, 0, 1, 0, 1]))
%!assert (isundefined (categorical (NaN (3, 4, 5))), true (3, 4, 5))
%!assert (isundefined (categorical (zeros (3, 4, 5))), false (3, 4, 5))

%!assert (isvector (categorical ([1, 2, 3, 4])), true)
%!assert (isvector (categorical ([1; 2; 3; 4])), true)
%!assert (isvector (categorical ([1, 2; 3, 4])), false)
%!assert (isvector (categorical ([])), false)
%!assert (isvector (categorical (ones (1, 0))), true)
%!assert (isvector (categorical (ones (0, 1))), true)
%!assert (isvector (categorical (1)), true)
%!assert (isvector (categorical (ones (2, 3, 4))), false)

################################################################################
##                        ** Category Operations **                           ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'addcats'          'mergecats'        'removecats'       'renamecats'      ##
## 'reordercats'      'setcats'          'times'                              ##
##                                                                            ##
################################################################################

%!test
%! C = categorical ({'red', 'blue', 'red', 'blue', 'red', 'blue'});
%! B = addcats (C, {'green', 'black'});
%! assert_equal (C, B);
%! assert_equal (categories (B), {'blue'; 'red'; 'green'; 'black'});
%! assert_equal (categories (C), {'blue'; 'red'});
%!test
%! C = categorical ({'red', 'blue', 'red', 'blue', 'red', 'blue'});
%! B = addcats (C, string ({'green', 'black'}));
%! assert_equal (C, B);
%! assert_equal (categories (B), {'blue'; 'red'; 'green'; 'black'});
%! assert_equal (categories (C), {'blue'; 'red'});
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, 'xsmall', 'Before', 'small');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'small'; 'medium'; 'large'; 'xlarge'});
%! assert_equal (double (B), [3, 4, 2, 5, 4, 3]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, 'xsmall', 'Before', 'medium');
%! cats = categories (B);
%! assert_equal (cats, {'small'; 'xsmall'; 'medium'; 'large'; 'xlarge'});
%! assert_equal (double (B), [3, 4, 1, 5, 4, 3]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, 'xsmall', 'Before', 'large');
%! cats = categories (B);
%! assert_equal (cats, {'small'; 'medium'; 'xsmall'; 'large'; 'xlarge'});
%! assert_equal (double (B), [2, 4, 1, 5, 4, 2]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, 'xsmall', 'Before', 'xlarge');
%! cats = categories (B);
%! assert_equal (cats, {'small'; 'medium'; 'large'; 'xsmall'; 'xlarge'});
%! assert_equal (double (B), [2, 3, 1, 5, 3, 2]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, {'xxsmall', 'xsmall'}, 'Before', 'small');
%! cats = categories (B);
%! assert_equal (cats, {'xxsmall'; 'xsmall'; 'small'; 'medium'; 'large'; 'xlarge'});
%! assert_equal (double (B), [4, 5, 3, 6, 5, 4]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xlarge', 'large', 'medium'};
%! C = categorical (S, {'small', 'medium', 'large', 'xlarge'}, 'Ordinal', true);
%! B = addcats (C, {'xxsmall', 'xsmall'}, 'Before', 'xlarge');
%! cats = categories (B);
%! assert_equal (cats, {'small'; 'medium'; 'large'; 'xxsmall'; 'xsmall'; 'xlarge'});
%! assert_equal (double (B), [2, 3, 1, 6, 3, 2]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, 'xlarge', 'After', 'large');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'small'; 'medium'; 'large'; 'xlarge'});
%! assert_equal (double (B), [3, 4, 2, 1, 4, 3]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, 'xlarge', 'After', 'medium');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'small'; 'medium'; 'xlarge'; 'large'});
%! assert_equal (double (B), [3, 5, 2, 1, 5, 3]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, 'xlarge', 'After', 'small');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'small'; 'xlarge'; 'medium'; 'large'});
%! assert_equal (double (B), [4, 5, 2, 1, 5, 4]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, 'xlarge', 'After', 'xsmall');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'xlarge'; 'small'; 'medium'; 'large'});
%! assert_equal (double (B), [4, 5, 3, 1, 5, 4]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, {'xlarge', 'xxlarge'}, 'After', 'large');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'small'; 'medium'; 'large'; 'xlarge'; 'xxlarge'});
%! assert_equal (double (B), [3, 4, 2, 1, 4, 3]);
%! assert_equal (cats (double (B)), S(:));
%!test
%! S = {'medium', 'large', 'small', 'xsmall', 'large', 'medium'};
%! C = categorical (S, {'xsmall', 'small', 'medium', 'large'}, 'Ordinal', true);
%! B = addcats (C, {'xlarge', 'xxlarge'}, 'After', 'xsmall');
%! cats = categories (B);
%! assert_equal (cats, {'xsmall'; 'xlarge'; 'xxlarge'; 'small'; 'medium'; 'large'});
%! assert_equal (double (B), [5, 6, 4, 1, 6, 5]);
%! assert_equal (cats (double (B)), S(:));
%!error<categorical.addcats: too few input arguments.> addcats (categorical)
%!error<categorical.addcats: NEWCATS cannot be empty.> addcats (categorical, '')
%!error<categorical.addcats: NEWCATS cannot be empty.> addcats (categorical, [])
%!error<categorical.addcats: NEWCATS cannot be empty.> addcats (categorical, {})
%!error<categorical.addcats: NEWCATS cannot be numeric or logical.> ...
%! addcats (categorical, 1)
%!error<categorical.addcats: NEWCATS cannot be numeric or logical.> ...
%! addcats (categorical, false)
%!error<categorical.addcats: NEWCATS cannot be converted to cellstr.> ...
%! addcats (categorical, struct ('a', 2))
%!error<categorical.addcats: duplicate category names in NEWCATS.> ...
%! addcats (categorical, {'a', 'a'})
%!error<categorical.addcats: new category names already present.> ...
%! addcats (categorical ({'a', 'b', 'a'}), {'a', 'c'})
%!error<categorical.addcats: cannot specify both 'After' and 'Before'.> ...
%! addcats (categorical ({'a', 'b', 'a'}), 'c', 'After', 'a', 'Before', 'b')
%!error<categorical.addcats: 'After' indexes a non-existing category.> ...
%! addcats (categorical ({'a', 'b', 'a'}), 'c', 'After', 'd')
%!error<categorical.addcats: 'Before' indexes a non-existing category.> ...
%! addcats (categorical ({'a', 'b', 'a'}), 'c', 'Before', 'c')
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, 1)
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, [])
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, {'w', 'e'})
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, ['w'; 'e'])
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, string ({'w', 'e'}))
%!error<categorical.mergecats: NEWCAT must be either a character vector, a cellstring scalar, or a string scalar.> ...
%! mergecats (categorical, {'a'}, missing)
%!error<categorical.mergecats: NEWCAT cannot be empty.> ...
%! mergecats (categorical, {'a'}, '')
%!error<categorical.mergecats: NEWCAT cannot be empty.> ...
%! mergecats (categorical, {'a'}, {''})
%!error<categorical.mergecats: NEWCAT cannot be empty.> ...
%! mergecats (categorical, {'a'}, string ({''}))
%!error<categorical.mergecats: NEWCAT cannot be empty.> ...
%! mergecats (categorical, {'a'}, string (missing))
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, {'red', 'pink'});
%! assert_equal (B, categorical ({'red', 'blue', 'red', 'red', 'blue', 'red'}));
%! assert_equal (categories (B), {'blue'; 'red'});
%! assert_equal (double (B), [2, 1, 2, 2, 1, 2]);
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, {'pink', 'red'});
%! assert_equal (B, categorical ({'pink', 'blue', 'pink', 'pink', 'blue', 'pink'}));
%! assert_equal (categories (B), {'blue'; 'pink'});
%! assert_equal (double (B), [2, 1, 2, 2, 1, 2]);
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, {'pink', 'blue'});
%! assert_equal (B, categorical ({'pink', 'pink', 'pink', 'red', 'pink', 'red'}));
%! assert_equal (categories (B), {'pink'; 'red'});
%! assert_equal (double (B), [1, 1, 1, 2, 1, 2]);
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, {'red', 'pink'}, 'gold');
%! assert_equal (B, categorical ({'gold', 'blue', 'gold', 'gold', 'blue', 'gold'}));
%! assert_equal (categories (B), {'blue'; 'gold'});
%! assert_equal (double (B), [2, 1, 2, 2, 1, 2]);
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, string ({'pink', 'red'}), 'gold');
%! assert_equal (B, categorical ({'gold', 'blue', 'gold', 'gold', 'blue', 'gold'}));
%! assert_equal (categories (B), {'blue'; 'gold'});
%! assert_equal (double (B), [2, 1, 2, 2, 1, 2]);
%!test
%! A = categorical ({'pink', 'blue', 'pink', 'red', 'blue', 'red'});
%! B = mergecats (A, {'pink', 'blue'}, string ('gold'));
%! assert_equal (B, categorical ({'gold', 'gold', 'gold', 'red', 'gold', 'red'}));
%! assert_equal (categories (B), {'gold'; 'red'});
%! assert_equal (double (B), [1, 1, 1, 2, 1, 2]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! assert_equal (categories (A), {'belt'; 'dress'; 'pants'; 'shirt'; 'shoes'});
%! B = mergecats (A, {'belt', 'shoes'}, 'hat');
%! assert_equal (categories (B), {'hat'; 'dress'; 'pants'; 'shirt'});
%! assert_equal (double (B), [4, 3, 1, 4, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! assert_equal (categories (A), {'belt'; 'dress'; 'pants'; 'shirt'; 'shoes'});
%! B = mergecats (A, {'dress', 'shoes'}, 'hat');
%! assert_equal (categories (B), {'belt'; 'hat'; 'pants'; 'shirt'});
%! assert_equal (double (B), [4, 3, 2, 4, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! assert_equal (categories (A), {'belt'; 'dress'; 'pants'; 'shirt'; 'shoes'});
%! B = mergecats (A, {'dress', 'shirt'}, 'hat');
%! assert_equal (categories (B), {'belt'; 'hat'; 'pants'; 'shoes'});
%! assert_equal (double (B), [2, 3, 4, 2, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! assert_equal (categories (A), {'belt'; 'dress'; 'pants'; 'shirt'; 'shoes'});
%! B = mergecats (A, {'belt', 'shirt', 'shoes'}, 'hat');
%! assert_equal (categories (B), {'hat'; 'dress'; 'pants'});
%! assert_equal (double (B), [1, 3, 1, 1, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! B = mergecats (A, {'dresss', 'shoes'}, 'hat');
%! assert_equal (categories (B), {'belt'; 'dress'; 'pants'; 'shirt'; 'hat'});
%! assert_equal (double (B), [4, 3, 5, 4, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt'});
%! B = mergecats (A, {'dresss', 'shirt'}, 'hat');
%! assert_equal (categories (B), {'belt'; 'dress'; 'pants'; 'hat'; 'shoes'});
%! assert_equal (double (B), [4, 3, 5, 4, 2, 1]);
%!test
%! A = categorical ({'shirt', 'pants', 'shoes', 'shirt', 'dress', 'belt', ''});
%! B = mergecats (A, {'belt', 'shoes'}, 'other');
%! cats = categories (B);
%! assert_equal (cats, {'other'; 'dress'; 'pants'; 'shirt'});
%! codes = double (B);
%! assert_equal (codes, [4, 3, 1, 4, 2, 1, NaN]);
%! S = {'shirt'; 'pants'; 'other'; 'shirt'; 'dress'; 'other'};
%! assert_equal (cats(codes(! isnan (codes))), S);
%!test
%! A = categorical ([1, 2, 3, 2, 1], 1:3, {'poor', 'fair', 'good'}, 'Ordinal', true);
%! B = mergecats (A, {'fair', 'poor'}, "bad");
%! assert_equal (categories (B), {'bad'; 'good'});
%! assert_equal (double (B), [1, 1, 2, 1, 1]);
%!error<categorical.mergecats: too few input arguments.> mergecats (categorical)
%!error<categorical.mergecats: OLDCATS cannot be empty.> mergecats (categorical, '')
%!error<categorical.mergecats: OLDCATS cannot be empty.> mergecats (categorical, [])
%!error<categorical.mergecats: OLDCATS cannot be empty.> mergecats (categorical, {})
%!error<categorical.mergecats: OLDCATS cannot be numeric or logical.> ...
%! mergecats (categorical, 1)
%!error<categorical.mergecats: OLDCATS cannot be numeric or logical.> ...
%! mergecats (categorical, false)
%!error<categorical.mergecats: OLDCATS cannot be a character vector.> ...
%! mergecats (categorical ({'a'}), 'a')
%!error<categorical.mergecats: OLDCATS cannot be converted to cellstr.> ...
%! mergecats (categorical, struct ('a', 2))

%!test
%! A = categorical ({'red', 'blue', 'blue', 'blue', 'red', 'blue'}, ...
%!                  {'black', 'blue', 'red', 'green'});
%! assert_equal (categories (A), {'black'; 'blue'; 'red'; 'green'});
%! B = removecats (A);
%! assert_equal (categories (B), {'blue'; 'red'});
%! assert_equal (A, B);
%!test
%! A = categorical ({'red', 'blue', 'blue', 'blue', 'red', 'blue'}, ...
%!                  {'black', 'blue', 'red', 'green'});
%! B = removecats (A, cell (0, 1));
%! assert_equal (categories (B), {'black'; 'blue'; 'red'; 'green'});
%! assert_equal (A, B);
%!test
%! A = categorical ({'red', 'blue', 'blue', 'blue', 'red', 'blue'}, ...
%!                  {'black', 'blue', 'red', 'green'});
%! B = removecats (A, {});
%! assert_equal (categories (B), {'black'; 'blue'; 'red'; 'green'});
%! assert_equal (A, B);
%!test
%! A = categorical ({'plane', 'car', 'train', 'car', 'plane', 'car'});
%! B = removecats (A, 'car');
%! assert_equal (categories (B), {'plane'; 'train'});
%! assert_equal (double (B), [1, NaN, 2, NaN, 1, NaN]);
%!test
%! A = categorical ({'plane', 'car', 'train', 'car', 'plane', 'car'});
%! B = removecats (A, string ('plane'));
%! assert_equal (categories (B), {'car'; 'train'});
%! assert_equal (double (B), [NaN, 1, 2, 1, NaN, 1]);
%!test
%! A = categorical ({'plane', 'car', 'train', 'car', 'plane', 'car'});
%! B = removecats (A, {'car', 'train'});
%! assert_equal (categories (B), {'plane'});
%! assert_equal (double (B), [1, NaN, NaN, NaN, 1, NaN]);
%!error<categorical.removecats: OLDCATS cannot be empty.> ...
%! removecats (categorical, [])
%!error<categorical.removecats: OLDCATS cannot be empty.> ...
%! removecats (categorical, '')
%!error<categorical.removecats: OLDCATS cannot be numeric or logical.> ...
%! removecats (categorical, 1)
%!error<categorical.removecats: OLDCATS cannot be numeric or logical.> ...
%! removecats (categorical, true)
%!error<categorical.removecats: OLDCATS cannot be converted to cellstr.> ...
%! removecats (categorical, struct ('a', 1))

%!test
%! A = categorical ({'purple', 'blue', 'purple', 'red', 'red', 'blue'});
%! B = renamecats (A, {'b', 'p', 'r'});
%! cats = categories (B);
%! assert_equal (cats, {'b'; 'p'; 'r'});
%! assert_equal (cats(double (B)), {'p'; 'b'; 'p'; 'r'; 'r'; 'b'});
%!test
%! A = categorical ({'purple', 'blue', 'purple', 'red', 'red', 'blue'});
%! B = renamecats (A, string ({'b', 'p', 'r'}));
%! cats = categories (B);
%! assert_equal (cats, {'b'; 'p'; 'r'});
%! assert_equal (cats(double (B)), {'p'; 'b'; 'p'; 'r'; 'r'; 'b'});
%!test
%! A = categorical ({'purple', 'blue', 'purple', 'red', 'red', 'blue'});
%! B = renamecats (A, 'purple', 'gold');
%! cats = categories (B);
%! assert_equal (cats, {'blue'; 'gold'; 'red'});
%! assert_equal (cats(double (B)), {'gold'; 'blue'; 'gold'; 'red'; 'red'; 'blue'});
%!test
%! A = categorical ();
%! B = renamecats (A, {});
%! assert_equal (A, B);
%!test
%! A = categorical ({'purple', 'blue', 'purple', 'red', 'red', 'blue'});
%! B = renamecats (A, {}, {});
%! assert_equal (A, B);
%!error<categorical.renamecats: too few input arguments.> ...
%! renamecats (categorical)
%!error<categorical.renamecats: NEWNAMES cannot be empty.> ...
%! renamecats (categorical, '')
%!error<categorical.renamecats: NEWNAMES cannot be numeric or logical.> ...
%! renamecats (categorical, 1)
%!error<categorical.renamecats: NEWNAMES cannot be numeric or logical.> ...
%! renamecats (categorical, true)
%!error<categorical.renamecats: NEWNAMES cannot be converted to cellstr.> ...
%! renamecats (categorical, struct ('a', 'b'))
%!error<categorical.renamecats: NEWNAMES must equal the number of existing categories in input array.> ...
%! renamecats (categorical, string (missing))
%!error<categorical.renamecats: OLDNAMES cannot be empty.> ...
%! renamecats (categorical ({'a'}), '', 'a')
%!error<categorical.renamecats: OLDNAMES cannot be numeric or logical.> ...
%! renamecats (categorical ({'a'}), 1, 'a')
%!error<categorical.renamecats: OLDNAMES cannot be numeric or logical.> ...
%! renamecats (categorical ({'a'}), true, 'a')
%!error<categorical.renamecats: OLDNAMES cannot be converted to cellstr.> ...
%! renamecats (categorical ({'a'}), struct ('a', 'a'), 'a')
%!error<categorical.renamecats: NEWNAMES cannot be empty.> ...
%! renamecats (categorical ({'a'}), 'a', '')
%!error<categorical.renamecats: NEWNAMES cannot be numeric or logical.> ...
%! renamecats (categorical ({'a'}), 'a', 1)
%!error<categorical.renamecats: NEWNAMES cannot be numeric or logical.> ...
%! renamecats (categorical ({'a'}), 'a', true)
%!error<categorical.renamecats: NEWNAMES cannot be converted to cellstr.> ...
%! renamecats (categorical ({'a'}), 'a', struct ('a', 'a'))
%!error<categorical.renamecats: OLDNAMES and NEWNAMES must have the same number of elements.> ...
%! renamecats (categorical ({'a'}), 'a', {})
%!error<categorical.renamecats: OLDNAMES and NEWNAMES must have the same number of elements.> ...
%! renamecats (categorical ({'a'}), {}, 'a')
%!error<categorical.renamecats: OLDNAMES must be a subset of existing categories.> ...
%! renamecats (categorical ({'a'}), 'b', 'a')

%!test
%! X = categorical ({'Frog', 'Cat', 'Cat', 'Ant', 'Frog'});
%! Xcats = categories (X);
%! assert_equal (Xcats, {'Ant'; 'Cat'; 'Frog'});
%! Y = categorical ({'Deer', 'Bear', 'Eagle', 'Deer'});
%! Ycats = categories (Y);
%! assert_equal (Ycats, {'Bear'; 'Deer'; 'Eagle'});
%! A = [X, Y];
%! Acats = categories (A);
%! S = {'Frog'; 'Cat'; 'Cat'; 'Ant'; 'Frog'; 'Deer'; 'Bear'; 'Eagle'; 'Deer'};
%! assert_equal (Acats, {'Ant'; 'Cat'; 'Frog'; 'Bear'; 'Deer'; 'Eagle'});
%! assert_equal (Acats(double (A)), S);
%! B = reordercats (A);
%! Bcats = categories (B);
%! assert_equal (Bcats, {'Ant'; 'Bear'; 'Cat'; 'Deer'; 'Eagle'; 'Frog'});
%! assert_equal (Bcats(double (B)), S);
%!test
%! A = categorical ({'red', 'green', 'blue', 'red', 'green', 'red', 'blue', 'blue'});
%! assert_equal (categories (A), {'blue'; 'green'; 'red'});
%! B = reordercats (A, string ({'red', 'green', 'blue'}));
%! assert_equal (categories (B), {'red'; 'green'; 'blue'});
%!test
%! A = categorical ({'plane', 'car', 'train', 'car', 'plane', 'car'}, ...
%!                  {'car', 'train', 'plane'}, 'Ordinal', true);
%! assert_equal (categories (A), {'car'; 'train'; 'plane'});
%! A_out = categorical ({'plane', 'train', 'plane'}, ...
%!                      {'car', 'train', 'plane'}, 'Ordinal', true);
%! assert_equal (A(A > "car"), A_out);
%! B = reordercats (A, {'train', 'car', 'plane'});
%! assert_equal (categories (B), {'train'; 'car'; 'plane'});
%! B_out = categorical ({'plane', 'plane'}, ...
%!                      {'train', 'car', 'plane'}, 'Ordinal', true);
%! assert_equal (B(B > "car"), B_out);
%!test
%! A = categorical ({'plane', 'car', 'train', 'car', 'car', 'plane', 'car'});
%! assert_equal (categories (A), {'car'; 'plane'; 'train'});
%! assert_equal (countcats (A), [4, 2, 1]);
%! [~, neworder] = sort (countcats (A));
%! B = reordercats (A, neworder);
%! assert_equal (categories (B), {'train'; 'plane'; 'car'});
%! assert_equal (A, B);
%!error<categorical.reordercats: NEWORDER numeric input must be a vector of positive integers.> ...
%! reordercats (categorical, 1.5)
%!error<categorical.reordercats: NEWORDER numeric input must be a vector of positive integers.> ...
%! reordercats (categorical, [0, 1, 2])
%!error<categorical.reordercats: NEWORDER numeric input must be a vector of positive integers.> ...
%! reordercats (categorical, [])
%!error<categorical.reordercats: NEWORDER numeric input must index a permutation of existing categories.> ...
%! reordercats (categorical ({'a', 'b', 'c'}), [1, 2, 2])
%!error<categorical.reordercats: NEWORDER numeric input must index a permutation of existing categories.> ...
%! reordercats (categorical ({'a', 'b', 'c'}), [1, 2, 4])
%!error<categorical.reordercats: NEWORDER cannot be empty.> ...
%! reordercats (categorical, '')
%!error<categorical.reordercats: NEWORDER cannot be logical.> ...
%! reordercats (categorical, true)
%!error<categorical.reordercats: NEWORDER cannot be a character vector.> ...
%! reordercats (categorical, 'a')
%!error<categorical.reordercats: NEWORDER cannot be converted to cellstr.> ...
%! reordercats (categorical, struct ('a', 1))
%!error<categorical.reordercats: NEWORDER must contain the same set with the existing categories.> ...
%! reordercats (categorical ({'a', 'b', 'c'}), {'a', 'd', 'c'})

%!test
%! A = categorical ({'blue', 'black', 'red', 'red', 'blue', 'black', 'black', 'red', 'blue'});
%! B = setcats (A, string ({'red', 'black'}));
%! assert_equal (categories (B), {'red'; 'black'});
%! Bout = categorical ({'', 'black', 'red', 'red', '', 'black', 'black', 'red', ''}, ...
%!                     {'red', 'black'});
%! assert_equal (isequaln (B, Bout), true);
%!test
%! A = categorical ({'blue', 'black', 'red', 'red', 'blue', 'black', 'black', 'red', 'blue'});
%! B = setcats (A, {'red', 'pink', 'blue'});
%! assert_equal (categories (B), {'red'; 'pink'; 'blue'});
%! Bout = categorical ({'blue', '', 'red', 'red', 'blue', '', '', 'red', 'blue'}, ...
%!                     {'red', 'pink', 'blue'});
%! assert_equal (isequaln (B, Bout), true);
%!test
%! A = setcats (categorical, {});
%! assert_equal (categorical, categorical);
%! assert_equal (isempty (A), true);
%! assert_equal (categories (A), {});
%!test
%! A = setcats (categorical ({'a', 'b', 'b'}), {});
%! assert_equal (double (A), [NaN, NaN, NaN]);
%! assert_equal (categories (A), {});
%!test
%! A = setcats (categorical ({'a','b'}, 'Protected', true), {'e','a'});
%! assert_equal (categories (A), {'e'; 'a'});
%! assert_equal (double (A), [2, NaN]);
%! assert_equal (isprotected (A), true);
%!error<categorical.setcats: too few input arguments.> setcats (categorical)
%!error<categorical.setcats: NEWCATS cannot be empty.> setcats (categorical, [])
%!error<categorical.setcats: NEWCATS cannot be empty.> setcats (categorical, '')
%!error<categorical.setcats: NEWCATS cannot be numeric or logical.> ...
%! setcats (categorical, 1)
%!error<categorical.setcats: NEWCATS cannot be numeric or logical.> ...
%! setcats (categorical, true)
%!error<categorical.setcats: NEWCATS cannot be converted to cellstr.> ...
%! setcats (categorical, struct ('a', 1))

%!test
%! colors = categorical ({'blue', 'red', 'green', 'black'});
%! signs = categorical ({'+', '-', '-', '+'});
%! C = colors .* signs;
%! cats = categories (C);
%! assert_equal (cats, {'black +'; 'black -'; 'blue +' ; 'blue -'; ...
%!                      'green +'; 'green -'; 'red +'; 'red -'});
%! C_out = categorical ({'blue +', 'red -', 'green -', 'black +'}, cats);
%! assert_equal (C, C_out);
%!error<categorical.times: too few input arguments.> times (categorical)
%!error<categorical.times: A and B must be categorical arrays.> ...
%! categorical .* 1
%!error<categorical.times: A and B must be categorical arrays.> ...
%! categorical .* string ('a')
%!error<categorical.times: A and B must be categorical arrays.> ...
%! 'cat' .* categorical
%!error<categorical.times: A and B must be categorical arrays.> ...
%! {'cat'} .* categorical

################################################################################
##                        ** Relational Operations **                         ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'eq'               'ge'               'gt'               'le'              ##
## 'lt'               'ne'                                                    ##
##                                                                            ##
################################################################################

%!assert_equal (categorical ({'a'}) == 'a', true)
%!assert_equal (string ('1') == categorical ({'a'}), false)
%!assert_equal (categorical ({'a', ''}) == {'a'}, [true, false])
%!test
%! A = categorical ({'a', 'b', 'c'});
%! B = categorical ({'a'; 'b'; 'c'});
%! assert_equal (A == B, logical (eye (3)));
%!test
%! A = categorical ({'a', 'b', 'c'});
%! B = categorical ({'a'; 'b'; 'c'});
%! B = repmat (B, 1, 3);
%! assert_equal (A == B, logical (eye (3)));
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X == X, [true, true, false]);
%! assert_equal (X == '1', [true, false, false]);
%!error<categorical.eq: any text representation must specify a single categorical value.> ...
%! categorical == {'a', 'b'}
%!error<categorical.eq: any text representation must specify a single categorical value.> ...
%! categorical ({'e', 'r'}) == string ({'e', 'r'})
%!error<categorical.eq: any text representation must specify a single categorical value.> ...
%! string ({'e', 'r'}) == categorical ({'e', 'r'})
%!error<categorical.eq: any text representation must specify a single categorical value.> ...
%! {'a', 'b'} == categorical
%!error<categorical.eq: comparison between ordinal arrays requires that both have the same categories, which must be ordered in the same way.> ...
%! categorical ({'e', 'r'}, {'e', 'r'}, 'Ordinal', true) == categorical ({'e', 'r'}, {'e'}, 'Ordinal', true)
%!error<categorical.eq: cannot compare a categorical array that is ordinal with one that is not.> ...
%! categorical ({'e', 'r'}, {'e', 'r'}, 'Ordinal', true) == categorical ({'e', 'r'})
%!error<categorical.eq: arrays have incompatible sizes.> ...
%! categorical ([1, 2, 3]) == categorical ([1, 2, 3, 4])
%!error<categorical.eq: comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical ([1, 2, 3]) == 1
%!error<categorical.eq: comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 == categorical ([1, 2, 3])

%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X >= '2', [false, true, false]);
%! assert_equal (X >= '1', [true, true, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X >= {'2'}, [false, true, true]);
%! assert_equal (X >= string ('1'), [true, true, true]);
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal ('2' >= X, [true, true, false]);
%! assert_equal ('1' >= X, [true, false, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal ({'2'} >= X, [true, true, false]);
%! assert_equal (string ('1') >= X, [true, false, false]);
%!test
%! X = categorical ([1, 2, 4], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X >= X', logical ([1, 1, 0; 0, 1, 0; 0, 0, 0]));
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X >= X', logical ([1, 1, 1; 0, 1, 1; 0, 0, 1]));
%!error<categorical.ge: categorical array is not ordinal.> categorical >= '1'
%!error<categorical.ge: categorical array is not ordinal.> {'1'} >= categorical
%!error<categorical.ge: any text representation must specify a single categorical value.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) >= {'1', '2'}
%!error<categorical.ge: any text representation must specify a single categorical value.> ...
%! string ({'1', '2'}) >= categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.ge: category does not exist in array.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) >= {'4'}
%!error<categorical.ge: category does not exist in array.> ...
%! '4' >= categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.ge: relational comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical >= 1
%!error<categorical.ge: relational comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 >= categorical
%!error<categorical.ge: relational comparison is not allowed between non-ordinal categorical arrays.> ...
%! categorical >= categorical

%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X > '2', [false, false, false]);
%! assert_equal (X > '1', [false, true, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X > {'2'}, [false, false, true]);
%! assert_equal (X > string ('1'), [false, true, true]);
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal ('2' > X, [true, false, false]);
%! assert_equal ('1' > X, [false, false, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal ({'2'} > X, [true, false, false]);
%! assert_equal (string ('1') > X, [false, false, false]);
%!test
%! X = categorical ([1, 2, 4], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X > X', logical ([0, 1, 0; 0, 0, 0; 0, 0, 0]));
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X > X', logical ([0, 1, 1; 0, 0, 1; 0, 0, 0]));
%!error<categorical.gt: categorical array is not ordinal.> categorical > '1'
%!error<categorical.gt: categorical array is not ordinal.> {'1'} > categorical
%!error<categorical.gt: any text representation must specify a single categorical value.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) > {'1', '2'}
%!error<categorical.gt: any text representation must specify a single categorical value.> ...
%! string ({'1', '2'}) > categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.gt: category does not exist in array.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) > {'4'}
%!error<categorical.gt: category does not exist in array.> ...
%! '4' > categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.gt: relational comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical > 1
%!error<categorical.gt: relational comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 > categorical
%!error<categorical.gt: relational comparison is not allowed between non-ordinal categorical arrays.> ...
%! categorical > categorical

%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X <= '2', [true, true, false]);
%! assert_equal (X <= '1', [true, false, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X <= {'2'}, [true, true, false]);
%! assert_equal (X <= string ('1'), [true, false, false]);
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal ('2' <= X, [false, true, false]);
%! assert_equal ('1' <= X, [true, true, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal ({'2'} <= X, [false, true, true]);
%! assert_equal (string ('1') <= X, [true, true, true]);
%!test
%! X = categorical ([1, 2, 4], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X <= X', logical ([1, 0, 0; 1, 1, 0; 0, 0, 0]));
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X <= X', logical ([1, 0, 0; 1, 1, 0; 1, 1, 1]));
%!error<categorical.le: categorical array is not ordinal.> categorical <= '1'
%!error<categorical.le: categorical array is not ordinal.> {'1'} <= categorical
%!error<categorical.le: any text representation must specify a single categorical value.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) <= {'1', '2'}
%!error<categorical.le: any text representation must specify a single categorical value.> ...
%! string ({'1', '2'}) <= categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.le: category does not exist in array.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) <= {'4'}
%!error<categorical.le: category does not exist in array.> ...
%! '4' <= categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.le: relational comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical <= 1
%!error<categorical.le: relational comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 <= categorical
%!error<categorical.le: relational comparison is not allowed between non-ordinal categorical arrays.> ...
%! categorical <= categorical

%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X < '2', [true, false, false]);
%! assert_equal (X < '1', [false, false, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X < {'2'}, [true, false, false]);
%! assert_equal (X < string ('1'), [false, false, false]);
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal ('2' < X, [false, false, false]);
%! assert_equal ('1' < X, [false, true, false]);
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal ({'2'} < X, [false, false, true]);
%! assert_equal (string ('1') < X, [false, true, true]);
%!test
%! X = categorical ([1, 2, 4], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X < X', logical ([0, 0, 0; 1, 0, 0; 0, 0, 0]));
%!test
%! X = categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X < X', logical ([0, 0, 0; 1, 0, 0; 1, 1, 0]));
%!error<categorical.lt: categorical array is not ordinal.> categorical < '1'
%!error<categorical.lt: categorical array is not ordinal.> {'1'} < categorical
%!error<categorical.lt: any text representation must specify a single categorical value.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) < {'1', '2'}
%!error<categorical.lt: any text representation must specify a single categorical value.> ...
%! string ({'1', '2'}) < categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.lt: category does not exist in array.> ...
%! categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true) < {'4'}
%!error<categorical.lt: category does not exist in array.> ...
%! '4' < categorical ([1, 2, 3], [1, 2, 3], 'Ordinal', true)
%!error<categorical.lt: relational comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical < 1
%!error<categorical.lt: relational comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 < categorical
%!error<categorical.lt: relational comparison is not allowed between non-ordinal categorical arrays.> ...
%! categorical < categorical

%!assert_equal (categorical ({'a'}) != 'a', false)
%!assert_equal (string ('1') != categorical ({'a'}), true)
%!assert_equal (categorical ({'a', ''}) != {'a'}, [false, true])
%!test
%! A = categorical ({'a', 'b', 'c'});
%! B = categorical ({'a'; 'b'; 'c'});
%! assert_equal (A != B, ! logical (eye (3)));
%!test
%! A = categorical ({'a', 'b', 'c'});
%! B = categorical ({'a'; 'b'; 'c'});
%! B = repmat (B, 1, 3);
%! assert_equal (A != B, ! logical (eye (3)));
%!test
%! X = categorical ([1, 2, NaN], [1, 2, 3], 'Ordinal', true);
%! assert_equal (X != X, [false, false, true]);
%! assert_equal (X != '1', [false, true, true]);
%!error<categorical.ne: any text representation must specify a single categorical value.> ...
%! categorical != {'a', 'b'}
%!error<categorical.ne: any text representation must specify a single categorical value.> ...
%! categorical ({'e', 'r'}) != string ({'e', 'r'})
%!error<categorical.ne: any text representation must specify a single categorical value.> ...
%! string ({'e', 'r'}) != categorical ({'e', 'r'})
%!error<categorical.ne: any text representation must specify a single categorical value.> ...
%! {'a', 'b'} != categorical
%!error<categorical.ne: comparison between ordinal arrays requires that both have the same categories, which must be ordered in the same way.> ...
%! categorical ({'e', 'r'}, {'e', 'r'}, 'Ordinal', true) != categorical ({'e', 'r'}, {'e'}, 'Ordinal', true)
%!error<categorical.ne: cannot compare a categorical array that is ordinal with one that is not.> ...
%! categorical ({'e', 'r'}, {'e', 'r'}, 'Ordinal', true) != categorical ({'e', 'r'})
%!error<categorical.ne: arrays have incompatible sizes.> ...
%! categorical ([1, 2, 3]) != categorical ([1, 2, 3, 4])
%!error<categorical.ne: comparison is not defined between 'categorical' and 'double' arrays.> ...
%! categorical ([1, 2, 3]) != 1
%!error<categorical.ne: comparison is not defined between 'double' and 'categorical' arrays.> ...
%! 1 != categorical ([1, 2, 3])

################################################################################
##                        ** Arithmetic Operations **                         ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'min'              'mink'             'max'              'maxk'            ##
## 'median'           'mode'             'histcounts'                         ##
##                                                                            ##
################################################################################

%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X);
%! M_out = categorical (1, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 1);
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 'includenan');
%! M_out = categorical (NaN, [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 1, 'includenan');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 2, 'omitnan');
%! M_out = categorical (1, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 1, 'omitnan');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 'includeundefined');
%! M_out = categorical (NaN, [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 1, 'includeundefined');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 2, 'omitundefined');
%! M_out = categorical (1, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = min (X, [], 1, 'omitundefined');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical (magic (3), [1:9], 'Ordinal', true);
%! M = min (X);
%! M_out = categorical ([3, 1, 2], [1:9], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical (magic (3), [1:9], 'Ordinal', true);
%! M = min (X, [], 2);
%! M_out = categorical ([1; 3; 2], [1:9], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! x = categorical (reshape (1:8, [2, 2, 2]), 1:8, 'Ordinal', true);
%! out = reshape ([1, 3, 5, 7], [1, 2, 2]);
%! assert_equal (min (x, [], 1), categorical (out, 1:8, 'Ordinal', true));
%! out = reshape ([1, 2, 5, 6], [2, 1, 2]);
%! assert_equal (min (x, [], 2), categorical (out, 1:8, 'Ordinal', true));
%! [y, i] = min (x, [], 3);
%! assert_equal (ndims (y), 2);
%! assert_equal (y, categorical ([1, 3; 2, 4], 1:8, 'Ordinal', true));
%! assert_equal (ndims (i), 2);
%! assert_equal (i, ones (2));
%! [~, i] = min (x, [], 3, 'linear');
%! assert_equal (i, [1, 3; 2, 4]);
%!test
%! x = categorical (reshape (1:8, [2, 2, 2]), 1:8, 'Ordinal', true);
%! assert_equal (min (x, [], [1, 3]), categorical ([1, 3], 1:8, 'Ordinal', true));
%! assert_equal (min (x, [], [2, 3]), categorical ([1; 2], 1:8, 'Ordinal', true));
%! out = reshape ([1, 5], [1, 1, 2]);
%! assert_equal (min (x, [], [1, 2]), categorical (out, 1:8, 'Ordinal', true));
%! assert_equal (min (x, [], [1, 2, 3]), min (x, [], "all"));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! assert_equal (min (X, Y), categorical ([1, 2, 2, 1], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical (3, 1:4, 'Ordinal', true);
%! assert_equal (min (X, Y), categorical ([1, 2, 3, 3], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical (2, 1:4, 'Ordinal', true);
%! assert_equal (min (Y, X), categorical ([1, 2, 2, 2], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! out = min ([1, 2, 3, 4], [4; 3; 2; 1]);
%! assert_equal (min (X, Y'), categorical (out, 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! out = min ([1; 2; 3; 4], [4, 3, 2, 1]);
%! assert_equal (min (X', Y), categorical (out, 1:4, 'Ordinal', true));
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [2, 3], 'linear');
%! assert_equal (m, categorical (ones (3, 1, 1, 2), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), [13; 14; 15]);
%! assert_equal (i(:,:,1,2), [253; 254; 255]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [1, 3], 'linear');
%! assert_equal (m, categorical (x(1,:,1,:), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), [1:3:46]);
%! assert_equal (i(:,:,1,2), [241:3:286]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [2, 4], 'linear');
%! assert_equal (m, categorical (ones (3, 1, 5), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1), [13; 14; 15]);
%! assert_equal (i(:,:,2), [61; 62; 63]);
%! assert_equal (i(:,:,3), [109; 110; 111]);
%! assert_equal (i(:,:,4), [157; 158; 159]);
%! assert_equal (i(:,:,5), [205; 206; 207]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [1, 4], 'linear');
%! assert_equal (m, categorical (x(1,:,:,1), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1), [1:3:46]);
%! assert_equal (i(:,:,2), [49:3:94]);
%! assert_equal (i(:,:,3), [97:3:142]);
%! assert_equal (i(:,:,4), [145:3:190]);
%! assert_equal (i(:,:,5), [193:3:238]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [1, 2, 3], 'linear');
%! assert_equal (m, categorical (ones (1, 1, 1, 2), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), 13);
%! assert_equal (i(:,:,1,2), 253);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = min (categorical (x, 1:5, 'Ordinal', true), [], [2, 3, 4], 'linear');
%! assert_equal (m, categorical ([1; 1; 1], 1:5, 'Ordinal', true));
%! assert_equal (i, [13; 14; 15]);
%!error<categorical.min: categorical array A is not ordinal.> ...
%! min (categorical)
%!error<categorical.min: too many input arguments.> ...
%! min (categorical ([], 1, 'Ordinal', true), [], 1, 2, 3, 4)
%!error<categorical.min: two output arguments are not supported for two input arrays.> ...
%! [m, i] = min (categorical ([], 1, 'Ordinal', true), categorical)
%!error<categorical.min: categorical array B must be ordinal.> ...
%! min (categorical ([], 1, 'Ordinal', true), categorical)
%!error<categorical.min: categorical arrays must have the same set of categories, including their order.> ...
%! min (categorical ([], 1, 'Ordinal', true), categorical (1, 2, 'Ordinal', true))

%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X);
%! M_out = categorical (2, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 1);
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 'includenan');
%! M_out = categorical (NaN, [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 1, 'includenan');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 2, 'omitnan');
%! M_out = categorical (2, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 1, 'omitnan');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 'includeundefined');
%! M_out = categorical (NaN, [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 1, 'includeundefined');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 2, 'omitundefined');
%! M_out = categorical (2, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,3], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 2, 'omitundefined');
%! M_out = categorical (3, [1,2,3], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical ([1,2,4], [1,2,3], 'Ordinal', true);
%! M = max (X, [], 1, 'omitundefined');
%! M_out = categorical ([1, 2, NaN], [1,2,3], 'Ordinal', true);
%! assert_equal (isequaln (M, X), true);
%! assert_equal (isequaln (M, M_out), true);
%!test
%! X = categorical (magic (3), [1:9], 'Ordinal', true);
%! M = max (X);
%! M_out = categorical ([8, 9, 7], [1:9], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! X = categorical (magic (3), [1:9], 'Ordinal', true);
%! M = max (X, [], 2);
%! M_out = categorical ([8; 7; 9], [1:9], 'Ordinal', true);
%! assert_equal (M, M_out);
%!test
%! x = categorical (reshape (1:8, [2, 2, 2]), 1:8, 'Ordinal', true);
%! out = reshape ([2, 4, 6, 8], [1, 2, 2]);
%! assert_equal (max (x, [], 1), categorical (out, 1:8, 'Ordinal', true));
%! out = reshape ([3, 4, 7, 8], [2, 1, 2]);
%! assert_equal (max (x, [], 2), categorical (out, 1:8, 'Ordinal', true));
%! [y, i] = max (x, [], 3);
%! assert_equal (ndims (y), 2);
%! assert_equal (y, categorical ([5, 7; 6, 8], 1:8, 'Ordinal', true));
%! assert_equal (ndims (i), 2);
%! assert_equal (i, 2 * ones (2));
%! [~, i] = max (x, [], 3, 'linear');
%! assert_equal (i, [5, 7; 6, 8]);
%!test
%! x = categorical (reshape (1:8, [2, 2, 2]), 1:8, 'Ordinal', true);
%! assert_equal (max (x, [], [1, 3]), categorical ([6, 8], 1:8, 'Ordinal', true));
%! assert_equal (max (x, [], [2, 3]), categorical ([7; 8], 1:8, 'Ordinal', true));
%! out = reshape ([4, 8], [1, 1, 2]);
%! assert_equal (max (x, [], [1, 2]), categorical (out, 1:8, 'Ordinal', true));
%! assert_equal (max (x, [], [1, 2, 3]), max (x, [], "all"));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! assert_equal (max (X, Y), categorical ([4, 3, 3, 4], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical (3, 1:4, 'Ordinal', true);
%! assert_equal (max (X, Y), categorical ([3, 3, 3, 4], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical (2, 1:4, 'Ordinal', true);
%! assert_equal (max (Y, X), categorical ([2, 2, 3, 4], 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! out = max ([1, 2, 3, 4], [4; 3; 2; 1]);
%! assert_equal (max (X, Y'), categorical (out, 1:4, 'Ordinal', true));
%!test
%! X = categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true);
%! Y = categorical ([4, 3, 2, 1], 1:4, 'Ordinal', true);
%! out = max ([1; 2; 3; 4], [4, 3, 2, 1]);
%! assert_equal (max (X', Y), categorical (out, 1:4, 'Ordinal', true));
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [2, 3], 'linear');
%! assert_equal (m, categorical (5 * ones (3, 1, 1, 2), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), [16; 17; 18]);
%! assert_equal (i(:,:,1,2), [256; 257; 258]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [1, 3], 'linear');
%! assert_equal (m, categorical (x(1,:,1,:), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), [1:3:46]);
%! assert_equal (i(:,:,1,2), [241:3:286]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [2, 4], 'linear');
%! assert_equal (m, categorical (5 * ones (3, 1, 5), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1), [16; 17; 18]);
%! assert_equal (i(:,:,2), [64; 65; 66]);
%! assert_equal (i(:,:,3), [112; 113; 114]);
%! assert_equal (i(:,:,4), [160; 161; 162]);
%! assert_equal (i(:,:,5), [208; 209; 210]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [1, 4], 'linear');
%! assert_equal (m, categorical (x(1,:,:,1), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1), [1:3:46]);
%! assert_equal (i(:,:,2), [49:3:94]);
%! assert_equal (i(:,:,3), [97:3:142]);
%! assert_equal (i(:,:,4), [145:3:190]);
%! assert_equal (i(:,:,5), [193:3:238]);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [1, 2, 3], 'linear');
%! assert_equal (m, categorical (5 * ones (1, 1, 1, 2), 1:5, 'Ordinal', true));
%! assert_equal (i(:,:,1,1), 16);
%! assert_equal (i(:,:,1,2), 256);
%!test
%! x = repmat ([4, 3, 2, 4, 1, 5, 3, 2], 3, 2, 5, 2);
%! [m, i] = max (categorical (x, 1:5, 'Ordinal', true), [], [2, 3, 4], 'linear');
%! assert_equal (m, categorical ([5; 5; 5], 1:5, 'Ordinal', true));
%! assert_equal (i, [16; 17; 18]);
%!error<categorical.max: categorical array A is not ordinal.> ...
%! max (categorical)
%!error<categorical.max: too many input arguments.> ...
%! max (categorical ([], 1, 'Ordinal', true), [], 1, 2, 3, 4)
%!error<categorical.max: two output arguments are not supported for two input arrays.> ...
%! [m, i] = max (categorical ([], 1, 'Ordinal', true), categorical)
%!error<categorical.max: categorical array B must be ordinal.> ...
%! max (categorical ([], 1, 'Ordinal', true), categorical)
%!error<categorical.max: categorical arrays must have the same set of categories, including their order.> ...
%! max (categorical ([], 1, 'Ordinal', true), categorical (1, 2, 'Ordinal', true))

%!test
%! M = median (categorical ([2, 4, 6, 8], 1:8, 'Ordinal', true));
%! assert_equal (M, categorical (5, 1:8, 'Ordinal', true));
%!test
%! M = median (categorical ([8, 2, 6, 4], 1:8, 'Ordinal', true));
%! assert_equal (M, categorical (5, 1:8, 'Ordinal', true));
%!test
%! M = median (categorical ([1, 2, 3, 4], 1:4, 'Ordinal', true));
%! assert_equal (M, categorical (3, 1:4, 'Ordinal', true));
%!test
%! M = median (categorical ([1, 2, 3, 4], 1:3, 'Ordinal', true));
%! assert_equal (isequaln (M, categorical (NaN, 1:3, 'Ordinal', true)), true);
%!test
%! M = median (categorical ([1, 2, 3, 4], 1:3, 'Ordinal', true), 'omitundefined');
%! assert_equal (M, categorical (2, 1:3, 'Ordinal', true));
%!test
%! M = median (categorical (magic (3), 1:9, 'Ordinal', true));
%! assert_equal (M, categorical ([4, 5, 6], 1:9, 'Ordinal', true));
%!test
%! M = median (categorical (magic (3), 1:9, 'Ordinal', true), 2);
%! assert_equal (M, categorical ([6; 5; 4], 1:9, 'Ordinal', true));
%!test
%! M = median (categorical (magic (3), 1:9, 'Ordinal', true), 3);
%! assert_equal (M, categorical (magic (3), 1:9, 'Ordinal', true));
%!test
%! M = median (categorical (magic (3), 1:9, 'Ordinal', true), [1, 2]);
%! assert_equal (M, categorical (5, 1:9, 'Ordinal', true));
%!test
%! M = median (categorical (magic (3), 1:9, 'Ordinal', true), 'all');
%! assert_equal (M, categorical (5, 1:9, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:16, 'Ordinal', true));
%! assert_equal (M, categorical ([7, 9, 8, 10], 1:16, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:16, 'Ordinal', true), 2);
%! assert_equal (M, categorical ([8; 9; 8; 9], 1:16, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:16, 'Ordinal', true), 3);
%! assert_equal (M, categorical (magic (4), 1:16, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:16, 'Ordinal', true), [1, 2]);
%! assert_equal (M, categorical (9, 1:16, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:16, 'Ordinal', true), 'all');
%! assert_equal (M, categorical (9, 1:16, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true));
%! assert_equal (isequaln (M, categorical ([NaN, 9, NaN, 10], 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 2);
%! assert_equal (isequaln (M, categorical ([NaN; 9; 8; NaN], 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 3);
%! assert_equal (isequaln (M, categorical (magic (4), 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), [1, 2]);
%! assert_equal (isequaln (M, categorical (NaN, 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 'all');
%! assert_equal (isequaln (M, categorical (NaN, 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 'omitundefined');
%! assert_equal (M, categorical ([5, 9, 6, 10], 1:14, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 2, 'omitundefined');
%! assert_equal (M, categorical ([3; 9; 8; 4], 1:14, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 3, 'omitundefined');
%! assert_equal (isequaln (M, categorical (magic (4), 1:14, 'Ordinal', true)), true);
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), [1, 2], 'omitundefined');
%! assert_equal (M, categorical (8, 1:14, 'Ordinal', true));
%!test
%! M = median (categorical (magic (4), 1:14, 'Ordinal', true), 'all', 'omitundefined');
%! assert_equal (M, categorical (8, 1:14, 'Ordinal', true));
%!error<categorical.median: categorical array A is not ordinal.> ...
%! median (categorical)
%!error<categorical.median: too many input arguments.> ...
%! median (categorical (1, 1, 'Ordinal', true), 1, 2, 3)

%!test
%! [m, f, c] = mode (categorical (toeplitz (1:5)));
%! assert_equal (m, categorical ([1, 2, 2, 2, 1], 1:5));
%! assert_equal (f, [1, 2, 2, 2, 1]);
%! assert_equal (size (c), [1, 5]);
%! assert_equal (c{1}, categorical ([1:5]', 1:5));
%! assert_equal (c{2}, categorical (2, 1:5));
%! assert_equal (c{3}, categorical ([2; 3], 1:5));
%! assert_equal (c{4}, categorical (2, 1:5));
%! assert_equal (c{5}, categorical ([1:5]', 1:5));
%!test
%! [m, f, c] = mode (categorical (toeplitz (1:5)), 2);
%! assert_equal (m, categorical ([1; 2; 2; 2; 1], 1:5));
%! assert_equal (f, [1; 2; 2; 2; 1]);
%! assert_equal (size (c), [5, 1]);
%! assert_equal (c{1}, categorical ([1:5]', 1:5));
%! assert_equal (c{2}, categorical (2, 1:5));
%! assert_equal (c{3}, categorical ([2; 3], 1:5));
%! assert_equal (c{4}, categorical (2, 1:5));
%! assert_equal (c{5}, categorical ([1:5]', 1:5));
%!test
%! [m, f, c] = mode (categorical (toeplitz (1:5)), 3);
%! assert_equal (m, categorical (toeplitz (1:5), 1:5));
%! assert_equal (f, ones (5));
%! assert_equal (size (c), [5, 5]);
%! C = arrayfun (@(x) categorical (x, 1:5), toeplitz (1:5), 'UniformOutput', false);
%! assert_equal (c, C);
%!test
%! [m, f, c] = mode (categorical (toeplitz (1:5)), [1, 2]);
%! assert_equal (m, categorical (2, 1:5));
%! assert_equal (f, 8);
%! assert_equal (isscalar (c), true);
%! assert_equal (c{1}, categorical (2, 1:5));
%!test
%! [m, f, c] = mode (categorical (toeplitz (1:5)), 'all');
%! assert_equal (m, categorical (2, 1:5));
%! assert_equal (f, 8);
%! assert_equal (isscalar (c), true);
%! assert_equal (c{1}, categorical (2, 1:5));

%!test
%! A = [0 0 1 1 1 0 0 0 0 NaN NaN 1 0 0 0 1 0 1 0 1 0 0 0 1 1 1 1];
%! C = categorical (A, [1, 0, NaN], {'yes', 'no', 'undecided'});
%! [N, Cats] = histcounts (C);
%! assert_equal (N, [11, 14, 2]);
%! assert_equal (Cats, {'yes', 'no', 'undecided'});
%! N = histcounts (C, 'Normalization', 'countdensity');
%! assert_equal (N, [11, 14, 2]);
%! N = histcounts (C, 'Normalization', 'probability');
%! assert_equal (N, [0.4074, 0.5185, 0.0741], 1e-4);
%! N = histcounts (C, 'Normalization', 'pdf');
%! assert_equal (N, [0.4074, 0.5185, 0.0741], 1e-4);
%! N = histcounts (C, 'Normalization', 'cumcount');
%! assert_equal (N, [11, 25, 27]);
%! N = histcounts (C, 'Normalization', 'cdf');
%! assert_equal (N, [0.4074, 0.9259, 1.0000], 1e-4);
%!test
%! A = [0 0 1 1 1 0 0 0 0 NaN NaN 1 0 0 0 1 0 1 0 1 0 0 0 1 1 1 1];
%! C = categorical (A, [1, 0, NaN], {'yes', 'no', 'undecided'});
%! [N, Cats] = histcounts (C, {'yes', 'no'});
%! assert_equal (N, [11, 14]);
%! assert_equal (Cats, {'yes', 'no'});
%! N = histcounts (C, {'yes', 'no'}, 'Normalization', 'countdensity');
%! assert_equal (N, [11, 14]);
%! N = histcounts (C, {'yes', 'no'}, 'Normalization', 'probability');
%! assert_equal (N, [0.4074, 0.5185], 1e-4);
%! N = histcounts (C, {'yes', 'no'}, 'Normalization', 'pdf');
%! assert_equal (N, [0.4074, 0.5185], 1e-4);
%! N = histcounts (C, {'yes', 'no'}, 'Normalization', 'cumcount');
%! assert_equal (N, [11, 25]);
%! N = histcounts (C, {'yes', 'no'}, 'Normalization', 'cdf');
%! assert_equal (N, [0.4074, 0.9259], 1e-4);
%!test
%! A = [0 0 1 1 1 0 0 0 0 NaN NaN 1 0 0 0 1 0 1 0 1 0 0 0 1 1 1 1];
%! C = categorical (A);
%! [N, Cats] = histcounts (C);
%! assert_equal (N, [14, 11]);
%! assert_equal (Cats, {'0', '1'});
%! N = histcounts (C, 'Normalization', 'countdensity');
%! assert_equal (N, [14, 11]);
%! N = histcounts (C, 'Normalization', 'probability');
%! assert_equal (N, [0.5185, 0.4074], 1e-4);
%! N = histcounts (C, 'Normalization', 'pdf');
%! assert_equal (N, [0.5185, 0.4074], 1e-4);
%! N = histcounts (C, 'Normalization', 'cumcount');
%! assert_equal (N, [14, 25]);
%! N = histcounts (C, 'Normalization', 'cdf');
%! assert_equal (N, [0.5185, 0.9259], 1e-4);
%!test
%! [N, Cats] = histcounts (categorical, {});
%! assert_equal (N, ones (1, 0));
%! assert_equal (Cats, cell (1, 0));
%!error<categorical.histcounts: invalid 'Normalization' type.> ...
%! histcounts (categorical, 'Normalization', 'some')
%!error<categorical.histcounts: invalid type for CATS input argument.> ...
%! histcounts (categorical, 1)
%!error<categorical.histcounts: invalid type for CATS input argument.> ...
%! histcounts (categorical, '1')
%!error<categorical.histcounts: invalid type for CATS input argument.> ...
%! histcounts (categorical, {1})
%!error<categorical.histcounts: input argument CATS references non-existing categories in A.> ...
%! histcounts (categorical, {'1'})
%!error<categorical.histcounts: input argument CATS references non-existing categories in A.> ...
%! histcounts (categorical, categorical (1))
%!error<categorical.histcounts: input argument CATS references non-existing categories in A.> ...
%! histcounts (categorical, string ('a'))

################################################################################
##                   ** Sort, Filter, and Set Operations **                   ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'sort'             'sortrows'         'topkrows'         'unique'          ##
## 'intersect'        'setdiff'          'setxor'           'union'           ##
##                                                                            ##
################################################################################

%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C);
%! assert_equal (B, categorical ([1, -4; 1, 1; 2, 2; 2, 3]));
%! assert_equal (I, [1, 3; 2, 4; 3, 1; 4, 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C, 1);
%! assert_equal (B, categorical ([1, -4; 1, 1; 2, 2; 2, 3]));
%! assert_equal (I, [1, 3; 2, 4; 3, 1; 4, 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C, 2);
%! assert_equal (B, categorical ([1, 2; 1, 3; -4, 2; 1, 2]));
%! assert_equal (I, [1, 2; 1, 2; 2, 1; 2, 1]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C, 'descend');
%! assert_equal (B, categorical ([2, 3; 2, 2; 1, 1; 1, -4]));
%! assert_equal (I, [3, 2; 4, 1; 1, 4; 2, 3]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C, 1, 'descend');
%! assert_equal (B, categorical ([2, 3; 2, 2; 1, 1; 1, -4]));
%! assert_equal (I, [3, 2; 4, 1; 1, 4; 2, 3]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sort (C, 2, 'descend');
%! assert_equal (B, categorical ([2, 1; 3, 1; 2, -4; 2, 1]));
%! assert_equal (I, [2, 1; 2, 1; 1, 2; 1, 2]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C);
%! X = [-2, -4, 1; 1, 1, 2; 1, 2, 2; 1, 3, 2; 2, 3, 3; 2, NaN, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5, 3, 2; 1, 4, 1; 2, 1, 3; 6, 2, 6; 3, 6, 5; 4, 5, 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 1);
%! X = [-2, -4, 1; 1, 1, 2; 1, 2, 2; 1, 3, 2; 2, 3, 3; 2, NaN, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5, 3, 2; 1, 4, 1; 2, 1, 3; 6, 2, 6; 3, 6, 5; 4, 5, 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 2);
%! X = [1, 2, 2; 1, 1, 3; -4, 2, 2; 1, 2, NaN; -2, 3, NaN; 1, 2, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1, 2, 3; 1, 3, 2; 2, 1, 3; 2, 1, 3; 1, 3, 2; 1, 3, 2]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 1, 'MissingPlacement', 'first');
%! X = [-2, NaN, NaN; 1, -4, 1; 1, 1, 2; 1, 2, 2; 2, 3, 2; 2, 3, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5, 5, 4; 1, 3, 2; 2, 4, 1; 6, 1, 3; 3, 2, 6; 4, 6, 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 2, 'MissingPlacement', 'first');
%! X = [1, 2, 2; 1, 1, 3; -4, 2, 2; NaN, 1, 2; NaN, -2, 3; 1, 2, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1, 2, 3; 1, 3, 2; 2, 1, 3; 3, 2, 1; 2, 1, 3; 1, 3, 2]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B1, I1] = sort (C, 1);
%! [B2, I2] = sort (C, 1, 'MissingPlacement', 'last');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B1, I1] = sort (C, 2);
%! [B2, I2] = sort (C, 2, 'MissingPlacement', 'last');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 'descend');
%! X = [2, NaN, NaN; 2, 3, 3; 1, 3, 2; 1, 2, 2; 1, 1, 2; -2, -4, 1];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3, 5, 4; 4, 2, 5; 1, 6, 1; 2, 1, 3; 6, 4, 6; 5, 3, 2]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 1, 'descend');
%! X = [2, NaN, NaN; 2, 3, 3; 1, 3, 2; 1, 2, 2; 1, 1, 2; -2, -4, 1];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3, 5, 4; 4, 2, 5; 1, 6, 1; 2, 1, 3; 6, 4, 6; 5, 3, 2]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 2, 'descend');
%! X = [2, 2, 1; 3, 1, 1; 2, 2, -4; NaN, 2, 1; NaN, 3, -2; 3, 2, 1];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2, 3, 1; 2, 1, 3; 1, 3, 2; 3, 1, 2; 2, 3, 1; 2, 3, 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B1, I1] = sort (C, 1, 'descend');
%! [B2, I2] = sort (C, 1, 'descend', 'MissingPlacement', 'first');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B1, I1] = sort (C, 2, 'descend');
%! [B2, I2] = sort (C, 2, 'descend', 'MissingPlacement', 'first');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 1, 'descend', 'MissingPlacement', 'last');
%! X = [2, 3, 3; 2, 3, 2; 1, 2, 2; 1, 1, 2; 1, -4, 1; -2, NaN, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3, 2, 5; 4, 6, 1; 1, 1, 3; 2, 4, 6; 6, 3, 2; 5, 5, 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, -4, 2; 2, 1, NaN; -2, NaN, 3; 1, 3, 2]);
%! [B, I] = sort (C, 2, 'descend', 'MissingPlacement', 'last');
%! X = [2, 2, 1; 3, 1, 1; 2, 2, -4; 2, 1, NaN; 3, -2, NaN; 3, 2, 1];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2, 3, 1; 2, 1, 3; 1, 3, 2; 1, 2, 3; 3, 1, 2; 2, 3, 1]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, 1; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C);
%! X = categorical ([2, 3; 2, 2; 1, 1; 1, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [3, 2; 4, 1; 1, 3; 2, 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, 1; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C, 1);
%! X = categorical ([2, 3; 2, 2; 1, 1; 1, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [3, 2; 4, 1; 1, 3; 2, 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, 1; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C, 2);
%! X = categorical ([2, 1; 3, 1; 2, 1; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [2, 1; 2, 1; 1, 2; 1, 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, NaN; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C, 'descend');
%! X = categorical ([1, NaN; 1, 1; 2, 2; 2, 3], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [1, 3; 2, 4; 3, 1; 4, 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, NaN; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C, 1, 'descend');
%! X = categorical ([1, NaN; 1, 1; 2, 2; 2, 3], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [1, 3; 2, 4; 3, 1; 4, 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, NaN; 2, 1], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! [B, I] = sort (C, 2, 'descend');
%! X = categorical ([1, 2; 1, 3; NaN, 2; 1, 2], [3, 2, 1], {'a', 'b', 'c'}, 'Ordinal', true);
%! assert_equal (isequaln (B, X), true);
%! assert_equal (I, [1, 2; 1, 2; 2, 1; 2, 1]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C);
%! Xs = [2, 3, 3; 2, 3, 2; 2, 2, 2; 1, 1, 2; 1, 4, 1; 1, NaN, NaN];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [3, 2, 5; 4, 6, 1; 5, 1, 3; 1, 4, 6; 2, 3, 2; 6, 5, 4]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 1);
%! Xs = [2, 3, 3; 2, 3, 2; 2, 2, 2; 1, 1, 2; 1, 4, 1; 1, NaN, NaN];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [3, 2, 5; 4, 6, 1; 5, 1, 3; 1, 4, 6; 2, 3, 2; 6, 5, 4]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 2);
%! Xs = [2, 2, 1; 3, 1, 1; 2, 2, 4; 2, 1, NaN; 3, 2, NaN; 3, 2, 1];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [2, 3, 1; 2, 1, 3; 1, 3, 2; 1, 2, 3; 3, 1, 2; 2, 3, 1]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 1, 'MissingPlacement', 'first');
%! Xs = [2, NaN, NaN; 2, 3, 3; 2, 3, 2; 1, 2, 2; 1, 1, 2; 1, 4, 1];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [3, 5, 4; 4, 2, 5; 5, 6, 1; 1, 1, 3; 2, 4, 6; 6, 3, 2]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 2, 'MissingPlacement', 'first');
%! Xs = [2, 2, 1; 3, 1, 1; 2, 2, 4; NaN, 2, 1; NaN, 3, 2; 3, 2, 1];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [2, 3, 1; 2, 1, 3; 1, 3, 2; 3, 1, 2; 2, 3, 1; 2, 3, 1]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B1, I1] = sort (C, 1);
%! [B2, I2] = sort (C, 1, 'MissingPlacement', 'last');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B1, I1] = sort (C, 2);
%! [B2, I2] = sort (C, 2, 'MissingPlacement', 'last');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 'descend');
%! Xs = [1, NaN, NaN; 1, 4, 1; 1, 1, 2; 2, 2, 2; 2, 3, 2; 2, 3, 3];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [1, 5, 4; 2, 3, 2; 6, 4, 1; 3, 1, 3; 4, 2, 6; 5, 6, 5]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 1, 'descend');
%! Xs = [1, NaN, NaN; 1, 4, 1; 1, 1, 2; 2, 2, 2; 2, 3, 2; 2, 3, 3];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [1, 5, 4; 2, 3, 2; 6, 4, 1; 3, 1, 3; 4, 2, 6; 5, 6, 5]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 2, 'descend');
%! Xs = [1, 2, 2; 1, 1, 3; 4, 2, 2; NaN, 1, 2; NaN, 2, 3; 1, 2, 3];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [1, 2, 3; 1, 3, 2; 2, 1, 3; 3, 2, 1; 2, 1, 3; 1, 3, 2]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B1, I1] = sort (C, 1, 'descend');
%! [B2, I2] = sort (C, 1, 'descend', 'MissingPlacement', 'first');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B1, I1] = sort (C, 2, 'descend');
%! [B2, I2] = sort (C, 2, 'descend', 'MissingPlacement', 'first');
%! assert_equal (isequaln (B1, B2), true);
%! assert_equal (I1, I2);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 1, 'descend', 'MissingPlacement', 'last');
%! Xs = [1, 4, 1; 1, 1, 2; 1, 2, 2; 2, 3, 2; 2, 3, 3; 2, NaN, NaN];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [1, 3, 2; 2, 4, 1; 6, 1, 3; 3, 2, 6; 4, 6, 5; 5, 5, 4]);
%!test
%! X = [1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2];
%! C = categorical (X, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! [B, I] = sort (C, 2, 'descend', 'MissingPlacement', 'last');
%! Xs = [1, 2, 2; 1, 1, 3; 4, 2, 2; 1, 2, NaN; 2, 3, NaN; 1, 2, 3];
%! Cs = categorical (Xs, [3, 2, 1, 4], {'a', 'b', 'c', 'd'}, 'Ordinal', true);
%! assert_equal (isequaln (B, Cs), true);
%! assert_equal (I, [1, 2, 3; 1, 3, 2; 2, 1, 3; 2, 1, 3; 1, 3, 2; 1, 3, 2]);
%!error<categorical.sort: invalid value for 'MissingPlacement'.> ...
%! sort (categorical, 'MissingPlacement', 'on')

%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C);
%! assert_equal (B, categorical ([1, 2; 1, 3; 2, -4; 2, 1]));
%! assert_equal (I, [1; 2; 3; 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, 1);
%! assert_equal (B, categorical ([1, 2; 1, 3; 2, -4; 2, 1]));
%! assert_equal (I, [1; 2; 3; 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, -1);
%! assert_equal (B, categorical ([2, -4; 2, 1; 1, 2; 1, 3]));
%! assert_equal (I, [3; 4; 1; 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, 2);
%! assert_equal (B, categorical ([2, -4; 2, 1; 1, 2; 1, 3]));
%! assert_equal (I, [3; 4; 1; 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, -2);
%! assert_equal (B, categorical ([1, 3; 1, 2; 2, 1; 2, -4]));
%! assert_equal (I, [2; 1; 4; 3]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, [1, -2]);
%! assert_equal (B, categorical ([1, 3; 1, 2; 2, 1; 2, -4]));
%! assert_equal (I, [2; 1; 4; 3]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = sortrows (C, [-1, -2]);
%! assert_equal (B, categorical ([2, 1; 2, -4; 1, 3; 1, 2]));
%! assert_equal (I, [4; 3; 2; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C);
%! X = [1, 2, 2; 1, 3, 1; 1, 3, 2; 2, 1, NaN; 2, 4, 2; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1; 2; 6; 4; 3; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, 'MissingPlacement', 'first');
%! X = [1, 2, 2; 1, 3, 1; 1, 3, 2; 2, NaN, 3; 2, 1, NaN; 2, 4, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1; 2; 6; 5; 4; 3]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, 'MissingPlacement', 'last');
%! X = [1, 2, 2; 1, 3, 1; 1, 3, 2; 2, 1, NaN; 2, 4, 2; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1; 2; 6; 4; 3; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2], 'MissingPlacement', 'first');
%! X = [2, NaN, 3; 2, 4, 2; 2, 1, NaN; 1, 3, 1; 1, 3, 2; 1, 2, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5; 3; 4; 2; 6; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2], 'MissingPlacement', 'last');
%! X = [2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 1; 1, 3, 2; 1, 2, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3; 4; 5; 2; 6; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, 2, 'MissingPlacement', 'first');
%! X = [2, NaN, 3; 2, 1, NaN; 1, 2, 2; 1, 3, 1; 1, 3, 2; 2, 4, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5; 4; 1; 2; 6; 3]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, 2, 'MissingPlacement', 'last');
%! X = [2, 1, NaN; 1, 2, 2; 1, 3, 1; 1, 3, 2; 2, 4, 2; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [4; 1; 2; 6; 3; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, -2, 'MissingPlacement', 'first');
%! X = [2, NaN, 3; 2, 4, 2; 1, 3, 1; 1, 3, 2; 1, 2, 2; 2, 1, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5; 3; 2; 6; 1; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, -2, 'MissingPlacement', 'last');
%! X = [2, 4, 2; 1, 3, 1; 1, 3, 2; 1, 2, 2; 2, 1, NaN; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3; 2; 6; 1; 4; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, 3]);
%! X = [1, 3, 1; 1, 3, 2; 1, 2, 2; 2, NaN, 3; 2, 4, 2; 2, 1, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2; 6; 1; 5; 3; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2, 3]);
%! X = [2, NaN, 3; 2, 4, 2; 2, 1, NaN; 1, 3, 1; 1, 3, 2; 1, 2, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5; 3; 4; 2; 6; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2], {'ascend', 'descend'});
%! X = [1, 3, 1; 1, 3, 2; 1, 2, 2; 2, NaN, 3; 2, 4, 2; 2, 1, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2; 6; 1; 5; 3; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2], {'descend', 'ascend'});
%! X = [2, 1, NaN; 2, 4, 2; 2, NaN, 3; 1, 2, 2; 1, 3, 1; 1, 3, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [4; 3; 5; 1; 2; 6]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2, 3], {'ascend', 'ascend', 'descend'});
%! X = [1, 2, 2; 1, 3, 2; 1, 3, 1; 2, 1, NaN; 2, 4, 2; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1; 6; 2; 4; 3; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, 3], {'ascend', 'ascend', 'descend'});
%! X = [1, 2, 2; 1, 3, 2; 1, 3, 1; 2, 1, NaN; 2, 4, 2; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [1; 6; 2; 4; 3; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, 3], 'MissingPlacement', 'first');
%! X = [1, 3, 1; 1, 3, 2; 1, 2, 2; 2, NaN, 3; 2, 4, 2; 2, 1, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2; 6; 1; 5; 3; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2, 3], 'MissingPlacement', 'first');
%! X = [2, NaN, 3; 2, 4, 2; 2, 1, NaN; 1, 3, 1; 1, 3, 2; 1, 2, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [5; 3; 4; 2; 6; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, -3], 'MissingPlacement', 'first');
%! X = [1, 3, 2; 1, 3, 1; 1, 2, 2; 2, NaN, 3; 2, 4, 2; 2, 1, NaN];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [6; 2; 1; 5; 3; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, 3], 'MissingPlacement', 'last');
%! X = [1, 3, 1; 1, 3, 2; 1, 2, 2; 2, 4, 2; 2, 1, NaN; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [2; 6; 1; 3; 4; 5]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [-1, -2, 3], 'MissingPlacement', 'last');
%! X = [2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 1; 1, 3, 2; 1, 2, 2];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [3; 4; 5; 2; 6; 1]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = sortrows (C, [1, -2, -3], 'MissingPlacement', 'last');
%! X = [1, 3, 2; 1, 3, 1; 1, 2, 2; 2, 4, 2; 2, 1, NaN; 2, NaN, 3];
%! assert_equal (isequaln (B, categorical (X)), true);
%! assert_equal (I, [6; 2; 1; 3; 4; 5]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4], [4, 3, 2, 1], 'Ordinal', true);
%! [B, I] = sortrows (C);
%! out = categorical ([2, 3, 4; 1, 2, 3], [4, 3, 2, 1], 'Ordinal', true);
%! assert_equal (B, out);
%! assert_equal (I, [2; 1]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4], [1, 2, 3, 4], 'Ordinal', true);
%! [B, I] = sortrows (C);
%! out = categorical ([1, 2, 3; 2, 3, 4], [1, 2, 3, 4], 'Ordinal', true);
%! assert_equal (B, out);
%! assert_equal (I, [1; 2]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4; 1, 2, NaN], [1:4], 'Ordinal', true);
%! [B, I] = sortrows (C);
%! assert_equal (double (B), [1, 2, 3; 1, 2, NaN; 2, 3, 4]);
%! assert_equal (I, [1; 3; 2]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4; 1, 2, NaN], [1:4], 'Ordinal', true);
%! [B, I] = sortrows (C, 'MissingPlacement', 'first');
%! assert_equal (double (B), [1, 2, NaN; 1, 2, 3; 2, 3, 4]);
%! assert_equal (I, [3; 1; 2]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4; 1, 2, NaN], [1:4], 'Ordinal', true);
%! [B, I] = sortrows (C, 'descend');
%! assert_equal (double (B), [2, 3, 4; 1, 2, NaN; 1, 2, 3]);
%! assert_equal (I, [2; 3; 1]);
%!test
%! C = categorical ([1, 2, 3; 2, 3, 4; 1, 2, NaN], [1:4], 'Ordinal', true);
%! [B, I] = sortrows (C, 'descend', 'MissingPlacement', 'last');
%! assert_equal (double (B), [2, 3, 4; 1, 2, 3; 1, 2, NaN]);
%! assert_equal (I, [2; 1; 3]);
%!error<categorical.sortrows: A must be a 2-D matrix.> ...
%! sortrows (categorical (ones (2, 3, 4)))
%!error<categorical.sort: invalid value for 'MissingPlacement'.> ...
%! sortrows (categorical (ones (2)), 'MissingPlacement', 2)
%!error<categorical.sortrows: too many input arguments.> ...
%! sortrows (categorical (1), 1, 'ascend', 2)
%!error<categorical.sortrows: COL must be a vector of nonzero integers indexing columns in A.> ...
%! sortrows (categorical (1), 1.5)
%!error<categorical.sortrows: COL must be a vector of nonzero integers indexing columns in A.> ...
%! sortrows (categorical (1), ones (2))
%!error<categorical.sortrows: COL indexes non-existing column.> ...
%! sortrows (categorical (1), 2)
%!error<categorical.sortrows: DIRECTION input must contain either 'ascend' or 'descend' values.> ...
%! sortrows (categorical (1), {'a'})
%!error<categorical.sortrows: DIRECTION does not match the elements in COL.> ...
%! sortrows (categorical (1), 1, {'ascend', 'descend'})
%!error<categorical.sortrows: invalid type for COL argument.> ...
%! sortrows (categorical (1), {1})
%!error<categorical.sortrows: invalid third input argument.> ...
%! sortrows (categorical (1), {'ascend'}, 'ascend')
%!error<categorical.sortrows: invalid third input argument.> ...
%! sortrows (categorical (1), {'ascend'}, 1)
%!error<categorical.sortrows: invalid type for DIRECTION argument.> ...
%! sortrows (categorical (1), 1, 1)
%!error<categorical.sortrows: DIRECTION input must contain either 'ascend' or 'descend' values.> ...
%! sortrows (categorical (1), 1, {'a'})
%!error<categorical.sortrows: DIRECTION does not match the elements in COL.> ...
%! sortrows (categorical (1), 1, {'ascend', 'descend'})

%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2);
%! assert_equal (B, categorical ([2, 1; 2, -4]));
%! assert_equal (I, [4; 3]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 5);
%! assert_equal (B, sortrows (C, 'descend'));
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, 'ascend');
%! assert_equal (B, categorical ([1, 2; 1, 3]));
%! assert_equal (I, [1; 2]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, -1);
%! assert_equal (B, categorical ([2, -4; 2, 1]));
%! assert_equal (I, [3; 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, 2);
%! assert_equal (B, categorical ([2, -4; 2, 1]));
%! assert_equal (I, [3; 4]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, -2);
%! assert_equal (B, categorical ([1, 3; 1, 2]));
%! assert_equal (I, [2; 1]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, [1, -2]);
%! assert_equal (B, categorical ([1, 3; 1, 2]));
%! assert_equal (I, [2; 1]);
%!test
%! C = categorical ([1, 2; 1, 3; 2, -4; 2, 1]);
%! [B, I] = topkrows (C, 2, [-1, -2]);
%! assert_equal (B, categorical ([2, 1; 2, -4]));
%! assert_equal (I, [4; 3]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = topkrows (C, 3);
%! assert_equal (double (B), [2, NaN, 3; 2, 4, 2; 2, 1, NaN]);
%! assert_equal (I, [5; 3; 4]);
%!test
%! C = categorical ([1, 2, 2; 1, 3, 1; 2, 4, 2; 2, 1, NaN; 2, NaN, 3; 1, 3, 2]);
%! [B, I] = topkrows (C, 3, 'ascend');
%! assert_equal (B, categorical ([1, 2, 2; 1, 3, 1; 1, 3, 2]));
%! assert_equal (I, [1; 2; 6]);
%!error<categorical.topkrows: too few input arguments.> ...
%! topkrows (categorical (1))
%!error<categorical.topkrows: K must be a positive integer scalar.> ...
%! topkrows (categorical (1), 0)

%!assert_equal (unique (categorical ([1, 2, 2, 3, 4, 4])), categorical ([1:4]))
%!assert_equal (unique (categorical ([1, 2, 2, 3, 4, 4]')), categorical ([1:4]'))
%!test
%! C = categorical ([1, 2, 2, 3, 4, 4]);
%! [c, i, j] = unique (C);
%! assert_equal (c, C(i));
%! assert_equal (c(j), C);
%!assert_equal (unique (categorical ([1, 4, 2, 2, 3]), 'sorted'), categorical (1:4))
%!test
%! C = categorical ([1, 4, 2, 2, 3]);
%! [c, i, j] = unique (C, 'sorted');
%! assert_equal (c, categorical (1:4));
%! assert_equal (c, C(i));
%! assert_equal (c(j), C);
%!test
%! C = categorical ([1, 4, 2, 2, 3]);
%! [c, i, j] = unique (C, 'stable');
%! assert_equal (c, categorical ([1, 4, 2, 3]));
%! assert_equal (c, C(i));
%! assert_equal (c(j), C);
%!test
%! C = categorical ([1, 4, 2, 2, 3]);
%! [c, i, j] = unique (C, 'sorted', 'last');
%! assert_equal (i, [1; 4; 5; 2]);
%! assert_equal (c, C(i));
%! assert_equal (c(j), C);
%!test
%! C = categorical ([1, 4, 2, 2, 3]);
%! [c, i, j] = unique (C, 'stable', 'last');
%! assert_equal (i, [1; 2; 4; 5]);
%! assert_equal (c, C(i));
%! assert_equal (c(j), C);
%!test
%! C = categorical ([3, 1, 3; 3, 3, 1; 1, 3, 3; 3, 2, 3; 2, 3, 3; ...
%!            1, 1, 3; 1, 2, 3; 2, 3, 2; 3, 3, 2; 3, 3, 1]);
%! [c, i, j] = unique (C(:,[1:2]), 'rows');
%! assert_equal (c, categorical ([1, 1; 1, 2; 1, 3; 2, 3; 3, 1; 3, 2; 3, 3]));
%! assert_equal (i, [6; 7; 3; 5; 1; 4; 2]);
%! assert_equal (j, [5; 7; 3; 6; 4; 1; 2; 4; 7; 7]);
%! assert_equal (C(i,[1:2]), categorical ([1, 1; 1, 2; 1, 3; 2, 3; 3, 1; 3, 2; 3, 3]));
%!test
%! [B, ia, ib] = unique (categorical ([1, 2; 2, 3; 1, 2]), 'rows', 'sorted', 'first');
%! assert_equal (B, categorical ([1, 2; 2, 3]));
%! assert_equal (ia, [1; 2]);
%! assert_equal (ib, [1; 2; 1]);
%!test
%! [B, ia, ib] = unique (categorical ([1, 2; 2, 3; 1, 2]), 'rows', 'sorted', 'last');
%! assert_equal (B, categorical ([1, 2; 2, 3]));
%! assert_equal (ia, [3; 2]);
%! assert_equal (ib, [1; 2; 1]);
%!test
%! [B, ia, ib] = unique (categorical ([1, 2; 2, 3; 1, 2]), 'rows', 'stable', 'first');
%! assert_equal (B, categorical ([1, 2; 2, 3]));
%! assert_equal (ia, [1; 2]);
%! assert_equal (ib, [1; 2; 1]);
%!test
%! [B, ia, ib] = unique (categorical ([1, 2; 2, 3; 1, 2]), 'rows', 'stable', 'last');
%! assert_equal (B, categorical ([2, 3; 1, 2]));
%! assert_equal (ia, [2; 3]);
%! assert_equal (ib, [2; 1; 2]);
%!error<duration.unique: 'legacy' option is not supported.> ...
%! unique (categorical ([1:3]), 'legacy')

%!test
%! A = categorical ([7 1 7 7 4]);
%! B = categorical ([7 0 4 4 0]);
%! [C, ixA, ixB] = intersect (A, B);
%! assert_equal (C, categorical ([4, 7]));
%! assert_equal (ixA, [5; 1]);
%! assert_equal (ixB, [3; 1]);
%!test
%! A = categorical ([7 1 7 7 4]);
%! B = categorical ([7 0 4 4 0]);
%! [C, ixA, ixB] = intersect (A, B, 'sorted');
%! assert_equal (C, categorical ([4, 7]));
%! assert_equal (ixA, [5; 1]);
%! assert_equal (ixB, [3; 1]);
%!test
%! A = categorical ([7 1 7 7 4]);
%! B = categorical ([7 0 4 4 0]);
%! [C, ixA, ixB] = intersect (A, B, 'stable');
%! assert_equal (C, categorical ([7, 4]));
%! assert_equal (ixA, [1; 5]);
%! assert_equal (ixB, [1; 3]);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1; 1, 2, 3; 1, 1, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 0]);
%! [C, ixA, ixB] = intersect (A, B, 'rows');
%! assert_equal (C, categorical ([1, 2, 3; 2, 2, 2]));
%! assert_equal (ixA, [3; 1]);
%! assert_equal (ixB, [1; 2]);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1; 1, 2, 3; 1, 1, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 0]);
%! [C, ixA, ixB] = intersect (A, B, 'sorted', 'rows');
%! assert_equal (C, categorical ([1, 2, 3; 2, 2, 2]));
%! assert_equal (ixA, [3; 1]);
%! assert_equal (ixB, [1; 2]);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1; 1, 2, 3; 1, 1, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 0]);
%! [C, ixA, ixB] = intersect (A, B, 'stable', 'rows');
%! assert_equal (C, categorical ([2, 2, 2; 1, 2, 3]));
%! assert_equal (ixA, [1; 3]);
%! assert_equal (ixB, [2; 1]);
%!assert_equal (intersect (categorical ([1, 2]), '2'), categorical (2, [1, 2]))
%!assert_equal (intersect (categorical ([1, 2]), {'1'}), categorical (1, [1, 2]))
%!assert_equal (intersect (categorical ([1, 2]), string ('2')), categorical (2, [1, 2]))
%!assert_equal (intersect ('2', categorical ([1, 2])), categorical (2, [1, 2]))
%!assert_equal (intersect ({'1'}, categorical ([1, 2])), categorical (1, [1, 2]))
%!assert_equal (intersect (string ('2'), categorical ([1, 2])), categorical (2, [1, 2]))
%!error<categorical.intersect: 'legacy' option is not supported.> ...
%! intersect (categorical, categorical, 'legacy')
%!error<categorical.intersect: invalid type input arrays.> ...
%! intersect (categorical, 1)
%!error<categorical.intersect: if A is ordinal, B must be ordinal.> ...
%! intersect (categorical, categorical (1, 1, 'Ordinal', true))
%!error<categorical.intersect: ordinal arrays must have the same set of categories in the same order.> ...
%! intersect (categorical (1, [1, 2], 'Ordinal', true), categorical (1, 1, 'Ordinal', true))

%!test
%! A = categorical ([3, 6, 2, 1, 5, 1, 1]);
%! B = categorical ([2, 4, 6]);
%! [C, index] = setdiff (A, B);
%! assert_equal (C, categorical ([1, 3, 5]));
%! assert_equal (index, [4; 1; 5]);
%!test
%! A = categorical ([3, 6, 2, 1, 5, 1, 1]);
%! B = categorical ([2, 4, 6]);
%! [C, index] = setdiff (A, B, 'sorted');
%! assert_equal (C, categorical ([1, 3, 5]));
%! assert_equal (index, [4; 1; 5]);
%!test
%! A = categorical ([3, 6, 2, 1, 5, 1, 1]);
%! B = categorical ([2, 4, 6]);
%! [C, index] = setdiff (A, B, 'stable');
%! assert_equal (C, categorical ([3, 1, 5]));
%! assert_equal (index, [1; 4; 5]);
%!test
%! A = categorical ([7, 9, 7; 0, 0, 0; 7, 9, 7; 5, 5, 5; 1, 4, 5]);
%! B = categorical ([0, 0, 0; 5, 5, 5]);
%! [C, index] = setdiff (A, B, 'rows');
%! assert_equal (C, categorical ([1, 4, 5; 7, 9, 7]));
%! assert_equal (index, [5; 1]);
%!test
%! A = categorical ([7, 9, 7; 0, 0, 0; 7, 9, 7; 5, 5, 5; 1, 4, 5]);
%! B = categorical ([0, 0, 0; 5, 5, 5]);
%! [C, index] = setdiff (A, B, 'sorted', 'rows');
%! assert_equal (C, categorical ([1, 4, 5; 7, 9, 7]));
%! assert_equal (index, [5; 1]);
%!test
%! A = categorical ([7, 9, 7; 0, 0, 0; 7, 9, 7; 5, 5, 5; 1, 4, 5]);
%! B = categorical ([0, 0, 0; 5, 5, 5]);
%! [C, index] = setdiff (A, B, 'stable', 'rows');
%! assert_equal (C, categorical ([7, 9, 7; 1, 4, 5]));
%! assert_equal (index, [1; 5]);
%!assert_equal (setdiff (categorical ([1, 2]), '2'), categorical (1, [1, 2]))
%!assert_equal (setdiff (categorical ([1, 2]), {'1'}), categorical (2, [1, 2]))
%!assert_equal (setdiff (categorical ([1, 2]), string ('2')), categorical (1, [1, 2]))
%!assert_equal (size (setdiff (categorical ([1, 2]), string ({'2', 1}))), [1, 0])
%!assert_equal (class (setdiff (categorical ([1, 2]), string ({'2', 1}))), 'categorical')
%!assert_equal (size (setdiff ('2', categorical ([1, 2]))), [1, 0])
%!assert_equal (class (setdiff ('2', categorical ([1, 2]))), 'categorical')
%!assert_equal (size (setdiff ({'1'}, categorical ([1, 2]))), [1, 0])
%!assert_equal (class (setdiff ({'1'}, categorical ([1, 2]))), 'categorical')
%!assert_equal (size (setdiff (string ({'1', '2'}), categorical ([1, 2]))), [1, 0])
%!assert_equal (class (setdiff (string ({'1', '2'}), categorical ([1, 2]))), 'categorical')
%!error<categorical.setdiff: 'legacy' option is not supported.> ...
%! setdiff (categorical, categorical, 'legacy')
%!error<categorical.setdiff: invalid type input arrays.> ...
%! setdiff (categorical, 1)
%!error<categorical.setdiff: if A is ordinal, B must be ordinal.> ...
%! setdiff (categorical, categorical (1, 1, 'Ordinal', true))
%!error<categorical.setdiff: ordinal arrays must have the same set of categories in the same order.> ...
%! setdiff (categorical (1, [1, 2], 'Ordinal', true), categorical (1, 1, 'Ordinal', true))

%!test
%! A = categorical ([5, 1, 3, 3, 3]);
%! B = categorical ([4, 1, 2]);
%! [C, ixA, ixB] = setxor (A, B);
%! assert_equal (C, categorical ([2, 3, 4, 5]));
%! assert_equal (ixA, [3; 1]);
%! assert_equal (ixB, [3; 1]);
%!test
%! A = categorical ([5, 1, 3, 3, 3]);
%! B = categorical ([4, 1, 2]);
%! [C, ixA, ixB] = setxor (A, B, 'sorted');
%! assert_equal (C, categorical ([2, 3, 4, 5]));
%! assert_equal (ixA, [3; 1]);
%! assert_equal (ixB, [3; 1]);
%!test
%! A = categorical ([5, 1, 3, 3, 3]);
%! B = categorical ([4, 1, 2]);
%! [C, ixA, ixB] = setxor (A, B, 'stable');
%! assert_equal (C, categorical ([5, 3, 4, 2]));
%! assert_equal (ixA, [1; 3]);
%! assert_equal (ixB, [1; 3]);
%!test
%! A = categorical ([7, 8, 9; 7, 7, 1; 7, 7, 1; 1, 2, 3; 4, 5, 6]);
%! B = categorical ([1, 2, 3; 4, 5, 6; 7, 7, 2]);
%! [C, ixA, ixB] = setxor (A, B, 'rows');
%! assert_equal (C, categorical ([7, 7, 1; 7, 7, 2; 7, 8, 9]));
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, [3]);
%!test
%! A = categorical ([7, 8, 9; 7, 7, 1; 7, 7, 1; 1, 2, 3; 4, 5, 6]);
%! B = categorical ([1, 2, 3; 4, 5, 6; 7, 7, 2]);
%! [C, ixA, ixB] = setxor (A, B, 'sorted', 'rows');
%! assert_equal (C, categorical ([7, 7, 1; 7, 7, 2; 7, 8, 9]));
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, [3]);
%!test
%! A = categorical ([7, 8, 9; 7, 7, 1; 7, 7, 1; 1, 2, 3; 4, 5, 6]);
%! B = categorical ([1, 2, 3; 4, 5, 6; 7, 7, 2]);
%! [C, ixA, ixB] = setxor (A, B, 'stable', 'rows');
%! assert_equal (C, categorical ([7, 8 ,9; 7, 7, 1; 7, 7, 2]));
%! assert_equal (ixA, [1; 2]);
%! assert_equal (ixB, [3]);
%!assert_equal (setxor (categorical ([1, 2]), '2'), categorical (1, [1, 2]))
%!assert_equal (setxor (categorical ([1, 2]), {'1'}), categorical (2, [1, 2]))
%!assert_equal (setxor (categorical ([1, 2]), string ('2')), categorical (1, [1, 2]))
%!assert_equal (size (setxor (categorical ([1, 2]), string ({'2', '1'}))), [1, 0])
%!assert_equal (class (setxor (categorical ([1, 2]), string ({'2', '1'}))), 'categorical')
%!assert_equal (setxor ('2', categorical ([1, 2])), categorical (1, [1, 2]))
%!assert_equal (setxor ({'1'}, categorical ([1, 2])), categorical (2, [1, 2]))
%!assert_equal (setxor (string ('2'), categorical ([1, 2])), categorical (1, [1, 2]))
%!error<categorical.setxor: 'legacy' option is not supported.> ...
%! setxor (categorical, categorical, 'legacy')
%!error<categorical.setxor: invalid type input arrays.> ...
%! setxor (categorical, 1)
%!error<categorical.setxor: if A is ordinal, B must be ordinal.> ...
%! setxor (categorical, categorical (1, 1, 'Ordinal', true))
%!error<categorical.setxor: ordinal arrays must have the same set of categories in the same order.> ...
%! setxor (categorical (1, [1, 2], 'Ordinal', true), categorical (1, 1, 'Ordinal', true))

%!test
%! A = categorical ([5, 7, 1]);
%! B = categorical ([3, 1, 1]);
%! [C, ixA, ixB] = union (A, B);
%! assert_equal (C, categorical ([1, 3, 5, 7]));
%! assert_equal (ixA, [3; 1; 2]);
%! assert_equal (ixB, 1);
%!test
%! A = categorical ([5, 7, 1]);
%! B = categorical ([3, 1, 1]);
%! [C, ixA, ixB] = union (A, B, 'sorted');
%! assert_equal (C, categorical ([1, 3, 5, 7]));
%! assert_equal (ixA, [3; 1; 2]);
%! assert_equal (ixB, 1);
%!test
%! A = categorical ([5, 7, 1]);
%! B = categorical ([3, 1, 1]);
%! [C, ixA, ixB] = union (A, B, 'stable');
%! assert_equal (C, categorical ([5, 7, 1, 3]));
%! assert_equal (ixA, [1; 2; 3]);
%! assert_equal (ixB, 1);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 2]);
%! [C, ixA, ixB] = union (A, B, 'rows');
%! assert_equal (C, categorical ([0, 0, 1; 1, 2, 3; 2, 2, 2]));
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, 1);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 2]);
%! [C, ixA, ixB] = union (A, B, 'sorted', 'rows');
%! assert_equal (C, categorical ([0, 0, 1; 1, 2, 3; 2, 2, 2]));
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, 1);
%!test
%! A = categorical ([2, 2, 2; 0, 0, 1]);
%! B = categorical ([1, 2, 3; 2, 2, 2; 2, 2, 2]);
%! [C, ixA, ixB] = union (A, B, 'stable', 'rows');
%! assert_equal (C, categorical ([2, 2, 2; 0, 0, 1; 1, 2, 3]));
%! assert_equal (ixA, [1; 2]);
%! assert_equal (ixB, 1);
%!assert_equal (union (categorical ([1, 2]), '2'), categorical ([1, 2], [1, 2]))
%!assert_equal (union (categorical ([1, 2]), {'1'}), categorical ([1, 2], [1, 2]))
%!assert_equal (union (categorical ([1, 2]), string ('3')), categorical ([1, 2, 3]))
%!assert_equal (union ('2', categorical ([1, 2])), categorical ([1, 2]))
%!assert_equal (union ({'1'}, categorical ([1, 2])), categorical ([1, 2]))
%!assert_equal (union (string ('4'), categorical ([1, 2])), categorical ([1, 2, 4]))
%!assert_equal (union (string ({'4', '5'}), categorical ([1, 2])), categorical ([1, 2, 4, 5]))
%!error<categorical.union: 'legacy' option is not supported.> ...
%! union (categorical, categorical, 'legacy')
%!error<categorical.union: invalid type input arrays.> ...
%! union (categorical, 1)
%!error<categorical.union: if A is ordinal, B must be ordinal.> ...
%! union (categorical, categorical (1, 1, 'Ordinal', true))
%!error<categorical.union: ordinal arrays must have the same set of categories in the same order.> ...
%! union (categorical (1, [1, 2], 'Ordinal', true), categorical (1, 1, 'Ordinal', true))

################################################################################
##                           ** Array Operations **                           ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'cat'              'horzcat'          'vertcat'          'repmat'          ##
## 'repelem'          'repelems'         'reshape'          'circshift'       ##
## 'permute'          'ipermute'         'transpose'        'ctranspose'      ##
##                                                                            ##
################################################################################

%!assert_equal (size (cat (1, categorical, categorical)), [0, 0])
%!assert_equal (size (cat (2, categorical, categorical)), [0, 0])
%!assert_equal (size (cat (1, categorical (ones (1, 0)), categorical (ones (1, 0)))), [2, 0])
%!assert_equal (size (cat (2, categorical (ones (0, 1)), categorical (ones (0, 1)))), [0, 2])
%!test
%! C = cat (1, categorical (1), categorical ([1; 2]));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1; 1; 2]));
%!test
%! C = cat (2, categorical (1), categorical ([1, 2]));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1, 1, 2]));
%!test
%! C = cat (3, categorical (1), categorical (2));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical (reshape ([1, 2], 1, 1, 2)));
%!test
%! C = cat (2, {'1', '3'}, categorical ([2, 1]));
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (C, categorical ([1, 3, 2, 1]));
%! assert_equal (double (C), [1, 3, 2, 1]);
%!test
%! C = cat (2, '1', categorical ([2, 1]));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1, 2, 1]));
%! assert_equal (double (C), [1, 2, 1]);
%!test
%! C = cat (2, categorical ([2, 1]), '1');
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([2, 1, 1]));
%! assert_equal (double (C), [2, 1, 1]);
%!test
%! C = cat (1, categorical ([2, 1]), {'1', '3'});
%! assert_equal (categories (C), {'1'; '2'; '3'});
%! assert_equal (C, categorical ([2, 1; 1, 3]));
%! assert_equal (double (C), [2, 1; 1, 3]);
%!test
%! C = cat (1, categorical ([2; 1]), '13');
%! assert_equal (categories (C), {'1'; '2'; '13'});
%! assert_equal (C, categorical ([2; 1; 13]));
%! assert_equal (double (C), [2; 1; 3]);
%!test
%! C = cat (2, categorical ([2, 1]), '13');
%! assert_equal (categories (C), {'1'; '2'; '13'});
%! assert_equal (C, categorical ([2, 1, 13]));
%! assert_equal (double (C), [2, 1, 3]);
%!test
%! C = cat (1, '13', categorical ([2; 1]));
%! assert_equal (categories (C), {'1'; '2'; '13'});
%! assert_equal (C, categorical ([13; 2; 1]));
%! assert_equal (double (C), [3; 2; 1]);
%!test
%! C = cat (1, categorical ([3; 1]), categorical ([2; 1]));
%! assert_equal (categories (C), {'1'; '3'; '2'});
%! assert_equal (C, categorical ([3; 1; 2; 1]));
%! assert_equal (double (C), [2; 1; 3; 1]);
%!test
%! C = cat (2, categorical ([3, 1]), categorical ([2, 1]));
%! assert_equal (categories (C), {'1'; '3'; '2'});
%! assert_equal (C, categorical ([3, 1, 2, 1]));
%! assert_equal (double (C), [2, 1, 3, 1]);
%!test
%! C = cat (2, categorical (1, [1, 2], 'Ordinal', true), ...
%!          categorical ([1, 2, 2], [1, 2], 'Ordinal', true));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1, 1, 2, 2], [1, 2], 'Ordinal', true));
%! assert_equal (double (C), [1, 1, 2, 2]);
%!test
%! C = cat (2, categorical (1, [2, 1], 'Ordinal', true), ...
%!          categorical ([1, 2, 2], [2, 1], 'Ordinal', true));
%! assert_equal (categories (C), {'2'; '1'});
%! assert_equal (C, categorical ([1, 1, 2, 2], [2, 1], 'Ordinal', true));
%! assert_equal (double (C), [2, 2, 1, 1]);
%!test
%! C = cat (1, categorical ([1, 2], [1, 2], 'Ordinal', true), ...
%!          categorical ([2, 2], [1, 2], 'Ordinal', true));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1, 2; 2, 2], [1, 2], 'Ordinal', true));
%! assert_equal (double (C), [1, 2; 2, 2]);
%!test
%! C = cat (1, categorical ([1, 1], [2, 1], 'Ordinal', true), ...
%!          categorical ([1, 2], [2, 1], 'Ordinal', true));
%! assert_equal (categories (C), {'2'; '1'});
%! assert_equal (C, categorical ([1, 1; 1, 2], [2, 1], 'Ordinal', true));
%! assert_equal (double (C), [2, 2; 2, 1]);
%!test
%! C = cat (2, categorical (1, [2, 1], 'Protected', true), ...
%!          categorical ([1, 2, 2], [1, 2], 'Protected', true));
%! assert_equal (categories (C), {'2'; '1'});
%! assert_equal (C, categorical ([1, 1, 2, 2], [2, 1], 'Protected', true));
%! assert_equal (double (C), [2, 2, 1, 1]);
%!test
%! C = cat (2, categorical (1, [1, 2], 'Protected', true), ...
%!          categorical ([1, 2, 2], [1, 2], 'Protected', true));
%! assert_equal (categories (C), {'1'; '2'});
%! assert_equal (C, categorical ([1, 1, 2, 2], [2, 1], 'Protected', true));
%! assert_equal (double (C), [1, 1, 2, 2]);
%!error<categorical.cat: DIM must be a valid dimension.> ...
%! cat (0, categorical, categorical)
%!error<categorical.cat: dimensions mismatch.> ...
%! cat (1, categorical (ones (2, 3, 4)), categorical (ones (1, 0)))
%!error<categorical.cat: size mismatch.> ...
%! cat (1, categorical (ones (0, 1)), categorical (ones (1, 0)))
%!error<categorical.cat: cannot concatenate ordinal categorical arrays unless they have the same ordered set of categories.> ...
%! cat (1, categorical (1, 1, 'Ordinal', true), categorical (1, 2, 'Ordinal', true))
%!error<categorical.cat: cannot concatenate ordinal with non-ordinal categorical arrays.> ...
%! cat (1, categorical (1, 1, 'Ordinal', true), categorical (1, 1, 'Ordinal', false))
%!error<categorical.cat: cannot concatenate ordinal with non-ordinal categorical arrays.> ...
%! cat (1, categorical (1, 1, 'Ordinal', true), {'1'})
%!error<categorical.cat: cannot concatenate protected categorical arrays that do not have the same set of categories.> ...
%! cat (1, categorical (1, 1, 'Protected', true), categorical (1, 2, 'Protected', true))

%!test
%! C = [categorical(3), categorical([4, 2]), categorical([0, 1, 0])];
%! assert_equal (C, categorical ([3, 4, 2, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '2'; '4'; '0'; '1'});
%! assert_equal (double (C), [1, 3, 2, 4, 5, 4]);
%!test
%! C = [{'1', '2'}, categorical(3), categorical([4, 2]), categorical([0, 1, 0])];
%! assert_equal (C, categorical ([1, 2, 3, 4, 2, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '1'; '2'; '4'; '0'});
%! assert_equal (double (C), [2, 3, 1, 4, 3, 5, 2, 5]);
%!test
%! C = [{'5', '3'}, categorical(3), categorical([4, 2]), '8', categorical([0, 1, 0])];
%! assert_equal (C, categorical ([5, 3, 3, 4, 2, 8, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '5'; '2'; '4'; '8'; '0'; '1'});
%! assert_equal (double (C), [2, 1, 1, 4, 3, 5, 6, 7, 6]);
%!test
%! C = [categorical(3), categorical([4, 2]), {'1', '3'}, categorical([0, 1, 0])];
%! assert_equal (C, categorical ([3, 4, 2, 1, 3, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '2'; '4'; '1'; '0'});
%! assert_equal (double (C), [1, 3, 2, 4, 1, 5, 4, 5]);
%!test
%! C = [{'5', '2'}, categorical(3), categorical([4, 2]), '8', categorical([0, 1, 0])];
%! assert_equal (C, categorical ([5, 2, 3, 4, 2, 8, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '2'; '5'; '4'; '8'; '0'; '1'});
%! assert_equal (double (C), [3, 2, 1, 4, 2, 5, 6, 7, 6]);
%!test
%! C = [{'5'}, {'2'}, categorical(3), categorical([4, 2]), '8', categorical([0, 1, 0])];
%! assert_equal (C, categorical ([5, 2, 3, 4, 2, 8, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '5'; '2'; '4'; '8'; '0'; '1'});
%! assert_equal (double (C), [2, 3, 1, 4, 3, 5, 6, 7, 6]);
%!test
%! C = [{'5', '2'}, {'8'}, categorical(3), categorical([4, 2]), '8', categorical([0, 1, 0])];
%! assert_equal (C, categorical ([5, 2, 8, 3, 4, 2, 8, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '2'; '5'; '8'; '4'; '0'; '1'});
%! assert_equal (double (C), [3, 2, 4, 1, 5, 2, 4, 6, 7, 6]);
%!test
%! C = [{'5', '2'}, {'8'}, categorical([5, 3]), categorical([4, 2]), '8', categorical([0, 1, 0])];
%! assert_equal (C, categorical ([5, 2, 8, 5, 3, 4, 2, 8, 0, 1, 0]));
%! assert_equal (categories (C), {'3'; '5'; '2'; '8'; '4'; '0'; '1'});
%! assert_equal (double (C), [2, 3, 4, 2, 1, 5, 3, 4, 6, 7, 6]);

%!test
%! C = [categorical([3, 1]); categorical([4, 2]); categorical([0, 1])];
%! assert_equal (C, categorical ([3, 1; 4, 2; 0, 1]));
%! assert_equal (categories (C), {'1'; '3'; '2'; '4'; '0'});
%! assert_equal (double (C), [2, 1; 4, 3; 5, 1]);
%!test
%! C = [{'1', '2'}; categorical([3, 1]); categorical([4, 2]); categorical([1, 0])];
%! assert_equal (C, categorical ([1, 2; 3, 1; 4, 2; 1, 0]));
%! assert_equal (categories (C), {'1'; '3'; '2'; '4'; '0'});
%! assert_equal (double (C), [1, 3; 2, 1; 4, 3; 1, 5]);
%!test
%! C = [{'5', '3'}; categorical([3, 2]); categorical([4, 2]); {'8', '2'}; categorical([0, 1])];
%! assert_equal (C, categorical ([5, 3; 3, 2; 4, 2; 8, 2; 0, 1]));
%! assert_equal (categories (C), {'2'; '3'; '5'; '4'; '8'; '0'; '1'});
%! assert_equal (double (C), [3, 2; 2, 1; 4, 1; 5, 1; 6, 7]);
%!test
%! C = [categorical([3, 2]); categorical([4, 2]); {'1', '3'}; categorical([0, 0])];
%! assert_equal (C, categorical ([3, 2; 4, 2; 1, 3; 0, 0]));
%! assert_equal (categories (C), {'2'; '3'; '4'; '1'; '0'});
%! assert_equal (double (C), [2, 1; 3, 1; 4, 2; 5, 5]);
%!test
%! C = [{'5', '2'}; categorical([1, 3]); categorical([4, 2]); {'8','8'}; categorical([1, 1])];
%! assert_equal (C, categorical ([5, 2; 1, 3; 4, 2; 8, 8; 1, 1]));
%! assert_equal (categories (C), {'1'; '3'; '2'; '5'; '4'; '8'});
%! assert_equal (double (C), [4, 3; 1, 2; 5, 3; 6, 6; 1, 1]);
%!test
%! C = [{'5', '4'}; {'2', '5'}; categorical([2, 3]); categorical([4, 2]); {'9', '8'}; categorical([0, 0])];
%! assert_equal (C, categorical ([5, 4; 2, 5; 2, 3; 4, 2; 9, 8; 0, 0]));
%! assert_equal (categories (C), {'2'; '3'; '4'; '5'; '8'; '9'; '0'});
%! assert_equal (double (C), [4, 3; 1, 4; 1, 2; 3, 1; 6, 5; 7, 7]);
%!test
%! C = [{'5', '2'}; {'8', '8'}; categorical([3, 3]); categorical([4, 2]); {'8', '8'}; categorical([0, 1])];
%! assert_equal (C, categorical ([5, 2; 8, 8; 3, 3; 4, 2; 8, 8; 0, 1]));
%! assert_equal (categories (C), {'3'; '2'; '5'; '8'; '4'; '0'; '1'});
%! assert_equal (double (C), [3, 2; 4, 4; 1, 1; 5, 2; 4, 4; 6, 7]);
%!test
%! C = [{'5', '2'}; {'8', '6'}; categorical([5, 3]); categorical([4, 2]); {'8', '8'}; categorical([0, 0])];
%! assert_equal (C, categorical ([5, 2; 8, 6; 5, 3; 4, 2; 8, 8; 0, 0]));
%! assert_equal (categories (C), {'3'; '5'; '2'; '6'; '8'; '4'; '0'});
%! assert_equal (double (C), [2, 3; 5, 4; 2, 1; 6, 3; 5, 5; 7, 7]);

%!assert_equal (repmat (categorical ([1, 2]), 4), categorical (repmat ([1, 2], 4)))
%!assert_equal (repmat (categorical ([1, 2]), 2, 3, 4), categorical (repmat ([1, 2], 2, 3, 4)))
%!assert_equal (repmat (categorical ([1, 2]), [2, 3]), categorical (repmat ([1, 2], [2, 3])))

%!assert_equal (repelem (categorical ([1:3]), 4), categorical (repelem ([1:3], 4)))
%!assert_equal (repelem (categorical ([1:3]), 2, 3, 4), categorical (repelem ([1:3], 2, 3, 4)))
%!assert_equal (repelem (categorical ([1:3]), [2, 3, 3]), categorical (repelem ([1:3], [2, 3, 3])))

%!assert_equal (repelems (categorical ([1:5]), [1, 3, 5; 2, 1, 2]), categorical ([1, 1, 3, 5, 5]))

%!assert_equal (reshape (categorical (magic (4)), 2, 2, 4), categorical (reshape (magic (4), 2, 2, 4)))
%!assert_equal (reshape (categorical (magic (4)), [2, 2, 4]), categorical (reshape (magic (4), [2, 2, 4])))
%!assert_equal (reshape (categorical (magic (4)), [], 2, 2), categorical (reshape (magic (4), [], 2, 2)))

%!assert_equal (circshift (categorical (magic (4)), 2), categorical (circshift (magic (4), 2)))
%!assert_equal (circshift (categorical (magic (4)), 1, 2), categorical (circshift (magic (4), 1, 2)))
%!assert_equal (circshift (categorical (magic (4)), 1, 1), categorical (circshift (magic (4), 1, 1)))
%!assert_equal (circshift (categorical (magic (4)), 1, 1), categorical (circshift (magic (4), 1)))

%!test
%! X = randi (10, 2, 3, 4);
%! D = categorical (X);
%! assert_equal (size (permute (D, [3, 1, 2])), [4, 2, 3]);
%! assert_equal (permute (D, [3, 1, 2]), categorical (permute (X, [3, 1, 2])));
%! assert_equal (D, ipermute (permute (D, [3, 1, 2]), [3, 1, 2]));

%!assert_equal (transpose (categorical ([1, 2; 3, 4])), categorical ([1, 3; 2, 4]))
%!assert_equal (transpose (categorical ([1, 2; 3, 4])), ctranspose (categorical ([1, 2; 3, 4])))
%!error<categorical.transpose: not defined for N-D arrays.> ...
%! transpose (categorical (ones (2, 2, 2)))

%!assert_equal (ctranspose (categorical ([1, 2; 3, 4])), categorical ([1, 3; 2, 4]))
%!assert_equal (ctranspose (categorical ([1, 2; 3, 4])), transpose (categorical ([1, 2; 3, 4])))
%!error<categorical.ctranspose: not defined for N-D arrays.> ...
%! ctranspose (categorical (ones (2, 2, 2)))

################################################################################
##                  ** Reference and Assignment Operations **                 ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'end'              'subsref'          'subsasgn'                           ##
##                                                                            ##
################################################################################

%!test
%! X = magic (3);
%! C = categorical (X);
%! assert_equal (C(:, end), categorical (X(:,end)));
%!test
%! X = magic (3);
%! C = categorical (X);
%! assert_equal (C([1:end-1], end), categorical ([6; 7]));

%!test
%! C = categorical ([1:5]);
%! out = C([2,3]);
%! assert_equal (size (out), [1, 2]);
%! assert_equal (out, categorical ([2, 3]));
%!error<categorical.subsref: '{}' invalid indexing for referencing values. Use '\(\)' instead.> ...
%! C = categorical ([1:5]); C{1}
%!error<categorical.subsref: '.' invalid indexing for referencing field of non-structure array.> ...
%! C = categorical ([1:5]); C.prop

%!test
%! C = categorical (1:5);
%! C([1,3]) = [];
%! assert_equal (C, categorical ([2, 4, 5], [1, 2, 3, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [2, 4, 5]);
%!test
%! C = categorical (1:5);
%! C([1,3]) = missing;
%! assert_equal (C, categorical ([NaN, 2, NaN, 4, 5], [1, 2, 3, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [NaN, 2, NaN, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(2) = '';
%! assert_equal (isequaln (C, categorical ([1, NaN, 3, 4, 5], [1, 2, 3, 4, 5])), true);
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [1, NaN, 3, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(2) = {''};
%! assert_equal (isequaln (C, categorical ([1, NaN, 3, 4, 5], [1, 2, 3, 4, 5])), true);
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [1, NaN, 3, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(2) = string ('');
%! assert_equal (isequaln (C, categorical ([1, NaN, 3, 4, 5], [1, 2, 3, 4, 5])), true);
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [1, NaN, 3, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(2) = string (missing);
%! assert_equal (isequaln (C, categorical ([1, NaN, 3, 4, 5], [1, 2, 3, 4, 5])), true);
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [1, NaN, 3, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(1) = '5';
%! assert_equal (C, categorical ([5, 2, 3, 4, 5]));
%!test
%! C = categorical (1:5);
%! C([1,3]) = {'5', '6'};
%! assert_equal (C, categorical ([5, 2, 6, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'; '6'});
%! assert_equal (double (C), [5, 2, 6, 4, 5]);
%!test
%! C = categorical (1:5);
%! C(3) = {'0'};
%! assert_equal (C, categorical ([1, 2, 0, 4, 5]));
%! C([1,3]) = {'5', '6'};
%! assert_equal (C, categorical ([5, 2, 6, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'; '0'; '6'});
%!test
%! C = categorical (1:5, 1:5, 'Ordinal', true);
%! C([1,3]) = categorical ([5, 5], 1:5, 'Ordinal', true);
%! assert_equal (C, categorical ([5, 2, 5, 4, 5], 1:5, 'Ordinal', true));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [5, 2, 5, 4, 5]);
%!test
%! C = categorical (1:5, 1:5, 'Protected', true);
%! C([1,3]) = categorical ([5, 5], 'Protected', true);
%! assert_equal (C, categorical ([5, 2, 5, 4, 5], 1:5, 'Protected', true));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [5, 2, 5, 4, 5]);
%!test
%! C = categorical (1:5, 1:5, 'Protected', true);
%! C([1,3]) = categorical ([5, 5]);
%! assert_equal (C, categorical ([5, 2, 5, 4, 5], 1:5, 'Protected', true));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'});
%! assert_equal (double (C), [5, 2, 5, 4, 5]);
%!test
%! C = categorical (1:5);
%! C([1,3]) = categorical ({'5', '6'});
%! assert_equal (C, categorical ([5, 2, 6, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'; '6'});
%! assert_equal (double (C), [5, 2, 6, 4, 5]);
%!test
%! C = categorical (1:5);
%! C([1,3]) = categorical ([5, 6]);
%! assert_equal (C, categorical ([5, 2, 6, 4, 5]));
%! assert_equal (categories (C), {'1'; '2'; '3'; '4'; '5'; '6'});
%! assert_equal (double (C), [5, 2, 6, 4, 5]);
%!test
%! C = categorical ([1, 2, 4, 5]);
%! C([1,3]) = categorical ([2, 6]);
%! assert_equal (C, categorical ([2, 2, 6, 5], [1, 2, 4, 5, 6]));
%! assert_equal (categories (C), {'1'; '2'; '4'; '5'; '6'});
%! assert_equal (double (C), [2, 2, 5, 4]);
%!test
%! C = categorical ([1, 2, 4, 5]);
%! C([1,3]) = categorical ([2, 3]);
%! assert_equal (C, categorical ([2, 2, 3, 5], [1, 2, 4, 5, 3]));
%! assert_equal (categories (C), {'1'; '2'; '4'; '5'; '3'});
%! assert_equal (double (C), [2, 2, 5, 4]);
%!error<categorical.subsasgn: chained subscripts not allowed.> ...
%! C = categorical (1:5); C(1:3)(2) = {'3'}
%!error<categorical.subsasgn: assignment value has no elements.> ...
%! C = categorical (1:5); C(2) = {}
%!error<categorical.subsasgn: assignment value must be a categorical array or text representing a single or multiple categories.> ...
%! C = categorical (1:5); C(2) = 1
%!error<categorical.subsasgn: cannot assign unordered categorical array to ordinal categorical array.> ...
%! C = categorical (1:5, 1:5, 'Ordinal', true); C(2) = '1'
%!error<categorical.subsasgn: cannot assign unordered categorical array to ordinal categorical array.> ...
%! C = categorical (1:5, 1:5, 'Ordinal', true); C(2) = categorical (1)
%!error<categorical.subsasgn: cannot assign ordinal categorical array to unordered categorical array.> ...
%! C = categorical (1:5, 1:5, 'Ordinal', false); C(2) = categorical (1, 1:5, 'Ordinal', true)
%!error<categorical.subsasgn: cannot assign new categories to protected categorical array.> ...
%! C = categorical (1:5, 1:5, 'Protected', true); C(2) = '6'
%!error<categorical.subsasgn: '{}' invalid indexing for assigning values. Use '\(\)' instead.> ...
%! C = categorical ([1:5]); C{2} = '1'
%!error<categorical.subsasgn: '.' invalid indexing for assigning field of non-structure array.> ...
%! C = categorical ([1:5]); C.prop = 1
