## 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 'calendarDuration' type **               ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'duration'         'dispstrings'      'cellstr'          'char'            ##
## 'datevec'          'time'             'split'            'caldays'         ##
## 'calweeks'         'calmonths'        'calquarters'      'calyears'        ##
##                                                                            ##
################################################################################

%!assert_equal (isscalar (calendarDuration ()), true)
%!assert_equal (isempty (calendarDuration ()), false)
%!assert_equal (class (calendarDuration), 'calendarDuration')
%!assert_equal (dispstrings (calendarDuration ()), {'0d'})
%!assert_equal (size (calendarDuration (zeros (4, 3))), [4, 1])
%!assert_equal (size (calendarDuration (ones (5, 6))), [5, 1])
%!test
%! X = calendarDuration ([0, 0, 1]);
%! assert_equal (caldays (X), 1);
%! assert_equal (dispstrings (X), {'1d'});
%! assert_equal (char (X), '1d');
%!test
%! X = calendarDuration ([0, 2, 14]);
%! assert_equal (calmonths (X), 2);
%! assert_equal (char (X), '2mo 14d');
%!test
%! X = calendarDuration ([1, 5, 15]);
%! assert_equal (calmonths (X), 17);
%! assert_equal (calquarters (X), 5);
%! assert_equal (dispstrings (X), {'1y 5mo 15d'});
%! assert_equal (char (X, 'mdt'), '17mo 15d');
%! assert_equal (cellstr (X, 'mwdt'), {'17mo 2w 1d'});
%!test
%! X = calendarDuration ([1, 0, 0, 12, 30, 6.5]);
%! assert_equal (calmonths (X), 12);
%! assert_equal (calquarters (X), 4);
%! assert_equal (dispstrings (X), {'1y 12h 30m 6.5s'});
%! assert_equal (class (time (X)), 'duration');
%! assert_equal (hours (time (X)), 12.5018, 1e-4);
%! assert_equal (minutes (time (X)), 750.1083, 1e-4);
%! assert_equal (seconds (time (X)), 45006.5);
%!assert_equal (size (calendarDuration ([1, 2; 9, 8], 0, 1)), [2, 2])
%!assert_equal (size (calendarDuration (1, zeros (2, 3, 4), 1)), [2, 3, 4])
%!test
%! X = calendarDuration (0, 0, 1);
%! assert_equal (caldays (X), 1);
%! assert_equal (dispstrings (X), {'1d'});
%! assert_equal (char (X), '1d');
%!test
%! X = calendarDuration ([0, 2, 14]);
%! assert_equal (calmonths (X), 2);
%! assert_equal (char (X), '2mo 14d');
%!test
%! X = calendarDuration (1, 5, 15);
%! assert_equal (calmonths (X), 17);
%! assert_equal (calquarters (X), 5);
%! assert_equal (dispstrings (X), {'1y 5mo 15d'});
%! assert_equal (char (X, 'mdt'), '17mo 15d');
%! assert_equal (cellstr (X, 'mwdt'), {'17mo 2w 1d'});
%!test
%! X = calendarDuration ([1;NaN], 2, 3);
%! assert_equal (char (X(1)), '1y 2mo 3d');
%! assert_equal (char (X(2)), 'NaN');
%!assert_equal (size (calendarDuration (1, 2, 1, days (ones (2, 3, 4)))), [2, 3, 4])
%!test
%! X = calendarDuration (1, 0, 0, duration (12, 30, 6.5));
%! assert_equal (calmonths (X), 12);
%! assert_equal (calquarters (X), 4);
%! assert_equal (dispstrings (X), {'1y 12h 30m 6.5s'});
%! assert_equal (class (time (X)), 'duration');
%! assert_equal (hours (time (X)), 12.5018, 1e-4);
%! assert_equal (minutes (time (X)), 750.1083, 1e-4);
%! assert_equal (seconds (time (X)), 45006.5);
%!test
%! X = calendarDuration (1, 0, 0, 12, 30, 6.5);
%! assert_equal (calmonths (X), 12);
%! assert_equal (calquarters (X), 4);
%! assert_equal (dispstrings (X), {'1y 12h 30m 6.5s'});
%! assert_equal (class (time (X)), 'duration');
%! assert_equal (hours (time (X)), 12.5018, 1e-4);
%! assert_equal (minutes (time (X)), 750.1083, 1e-4);
%! assert_equal (seconds (time (X)), 45006.5);
%!test
%! X = calendarDuration ([1, NaN], 0, 0, 12, 30, 6.5);
%! assert_equal (calmonths (X(1)), 12);
%! assert_equal (calquarters (X(1)), 4);
%! assert_equal (dispstrings (X(1)), {'1y 12h 30m 6.5s'});
%! assert_equal (class (time (X(1))), 'duration');
%! assert_equal (hours (time (X(1))), 12.5018, 1e-4);
%! assert_equal (minutes (time (X(1))), 750.1083, 1e-4);
%! assert_equal (seconds (time (X(1))), 45006.5);
%! assert_equal (calmonths (X(2)), NaN);
%! assert_equal (calquarters (X(2)), NaN);
%! assert_equal (char (X(2)), 'NaN');
%! assert_equal (class (time (X(2))), 'duration');
%! assert_equal (hours (time (X(2))), NaN);
%! assert_equal (minutes (time (X(2))), NaN);
%! assert_equal (seconds (time (X(2))), NaN);
%!error<calendarDuration: 'Format' must be a character vector or a string scalar.> ...
%! calendarDuration ([0, 0, 1], 'Format', 1)
%!error<calendarDuration: 'Format' must be a character vector or a string scalar.> ...
%! calendarDuration ([0, 0, 1], 'Format', {'YY-MM-DD'})
%!error<calendarDuration: 'Format' contains duplicate characters.> ...
%! calendarDuration ([0, 0, 1], 'Format', 'yymd')
%!error<calendarDuration: 'Format' must contain 'm', 'd', and 't'.> ...
%! calendarDuration ([0, 0, 1], 'Format', 'ymd')
%!error<calendarDuration: 'Format' has invalid order of characters.> ...
%! calendarDuration ([0, 0, 1], 'Format', 'mtd')
%!error<calendarDuration: X must be a numeric matrix.> calendarDuration ({0, 0})
%!error<calendarDuration: X must be a numeric matrix.> calendarDuration ('asd')
%!error<calendarDuration: X must be real.> calendarDuration (1+i)
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration ([0.5, 1, 1])
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration ([1, 0.5, 1])
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration ([1, 1, 0.5])
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration ([0.5, 0, 0, 0, 0, 0])
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration ([0, 0.5, 0, 0, 0, 0])
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration ([0, 0, 0.5, 0, 0, 0])
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration ([0, 0, 0, 0.5, 0, 0])
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration ([0, 0, 0, 0, 0.5, 0])
%!error<calendarDuration: X must have either 3 or 6 columns.> ...
%! calendarDuration ([1, 1])
%!error<calendarDuration: X must have either 3 or 6 columns.> ...
%! calendarDuration ([1, 1, 1, 0.5, 0])
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration ('a', 2, 3)
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration (1, {2}, 3)
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration (1, 2, {'s'})
%!error<calendarDuration: Y, MO, and D must be real.> calendarDuration (i, 2, 3)
%!error<calendarDuration: Y, MO, and D must be real.> calendarDuration (1, i, 3)
%!error<calendarDuration: Y, MO, and D must be real.> calendarDuration (1, 2, i)
%!error<calendarDuration: Y, MO, and D must be of common size or scalars.> ...
%! calendarDuration ([1, 2], 2, [1; 2])
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1.2, 2, 3)
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1, 2.5, 3)
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1, 2, 0.3)
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration ('a', 2, 3, days (1))
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration (1, {2}, 3, days (1))
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration (1, 2, {'s'}, days (1))
%!error<calendarDuration: Y, MO, and D must be real.> ...
%! calendarDuration (i, 2, 3, days (1))
%!error<calendarDuration: Y, MO, and D must be real.> ...
%! calendarDuration (1, i, 3, days (1))
%!error<calendarDuration: Y, MO, and D must be real.> ...
%! calendarDuration (1, 2, i, days (1))
%!error<calendarDuration: T must be a duration array.> ...
%! calendarDuration (1, 2, 3, 2)
%!error<calendarDuration: Y, MO, D, and T must be of common size or scalars.> ...
%! calendarDuration ([1, 2], 2, 1, days ([1; 2]))
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1.2, 2, 3, days (1))
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1, 2.5, 3, days (1))
%!error<calendarDuration: years, months, and days must be integer values.> ...
%! calendarDuration (1, 2, 0.3, days (1))
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration ('a', 0, 0, 12, 30, 6.5)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration (1, {0}, 0, 12, 30, 6.5)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration (1, 0, string (0), 12, 30, 6.5)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration (1, 0, 0, {12}, 30, 6.5)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration (1, 0, 0, 12, '30', 6.5)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be numeric arrays.> ...
%! calendarDuration (1, 0, 0, 12, 30, struct ('s', 6.5))
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (i, 0, 0, 12, 30, 6.5)
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (1, i, 0, 12, 30, 6.5)
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (1, 0, i, 12, 30, 6.5)
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (1, 0, 0, i, 30, 6.5)
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (1, 0, 0, 12, i, 6.5)
%!error<calendarDuration: numeric input data must be real.> ...
%! calendarDuration (1, 0, 0, 12, 30, i)
%!error<calendarDuration: Y, MO, D, H, MI, and S must be of common size or scalars.> ...
%! calendarDuration ([1, 2], 0, [0; 0], 12, 30, 6.5)
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration (1.2, 0, 1, 12, 30, 6.5)
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration (1, [0.1, NaN], 1, 12, 30, 6.5)
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration (1, 0, 1.5, 12, 30, 6.5)
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration (1, 0, 1, 1.2, 30, 6.5)
%!error<calendarDuration: years, months, days, hours, and minutes must be integer values.> ...
%! calendarDuration (1, 0, 1, 12, 29.5, 6.5)
%!error<calendarDuration: invalid number of input arguments.> ...
%! calendarDuration (1, [0.1, NaN])
%!error<calendarDuration: invalid number of input arguments.> ...
%! calendarDuration (1, [0.1, NaN], 1, 12, 30)
%!test
%! X = calendarDuration (1, 2, 3, 'Format', 'qmdt');
%! Y = calendarDuration (X);
%! assert_equal (class (Y), 'calendarDuration');
%! assert_equal (Y.Format, 'qmdt');
%! assert_equal (calmonths (Y), 14);
%!test
%! X = calendarDuration (ones (2, 3), 2, 3, 'Format', 'mdt');
%! Y = calendarDuration (X);
%! assert_equal (size (Y), [2, 3]);
%! assert_equal (Y.Format, 'mdt');
%!test
%! X = calendarDuration (1, 2, 3, 'Format', 'qmdt');
%! Y = calendarDuration (X, 'Format', 'mdt');
%! assert_equal (Y.Format, 'mdt');
%! assert_equal (X.Format, 'qmdt');
%!test
%! X = calendarDuration ([], [], []);
%! Y = calendarDuration (X);
%! assert_equal (size (Y), [0, 0]);
%! assert_equal (isempty (Y), true);
%!test
%! X = calendarDuration (1, 2, 3, 'Format', string ('mdt'));
%! assert_equal (class (X.Format), 'char');
%! assert_equal (X.Format, 'mdt');
%! assert_equal (dispstrings (X), {'14mo 3d'});
%!test
%! X = calendarDuration (1, 2, 3);
%! X.Format = string ('mdt');
%! assert_equal (class (X.Format), 'char');
%! assert_equal (X.Format, 'mdt');
%!test
%! X = calendarDuration ('Format', 'mdt');
%! assert_equal (X.Format, 'mdt');
%! assert_equal (dispstrings (X), {'0d'});
%!assert_equal (cellstr (calendarDuration (1, 2, 3), string ('mdt')), {'14mo 3d'})
%!assert_equal (char (calendarDuration (1, 2, 3), string ('mdt')), '14mo 3d')
%!error<calendarDuration: 'Format' must be a character vector or a string scalar.> ...
%! calendarDuration (1, 2, 3, 'Format', [])
%!error<calendarDuration: 'Format' must be a character vector or a string scalar.> ...
%! calendarDuration (1, 2, 3, 'Format', string ({'ymdt', 'mdt'}))
%!error<calendarDuration: 'Format' must be a character vector or a string scalar.> ...
%! calendarDuration (1, 2, 3, 'Format', ['ymdt'; 'ymdt'])
%!error<calendarDuration: 'Format' must only contain characters from the 'yqmwdt' sequence.> ...
%! calendarDuration (1, 2, 3, 'Format', 'ymdtz')
%!error<calendarDuration: 'Format' must only contain characters from the 'yqmwdt' sequence.> ...
%! calendarDuration (1, 2, 3, 'Format', 'YMDT')
%!error<calendarDuration: 'Format' must contain 'm', 'd', and 't'.> ...
%! calendarDuration (1, 2, 3, 'Format', '')
%!error<calendarDuration: X must be numeric. Convert a duration array with 'years', 'days', 'hours', 'minutes', or 'seconds' first.> ...
%! calendarDuration (duration (1, 2, 3))
%!error<calendarDuration: Y, MO, and D must be a numeric arrays.> ...
%! calendarDuration (calendarDuration (1, 2, 3), 1, 2)

%!assert_equal (dispstrings (calendarDuration ([0, 0, NaN])), {'NaN'})
%!assert_equal (dispstrings (calendarDuration ([0, 0, Inf])), {'Inf'})
%!assert_equal (dispstrings (calendarDuration ([0, NaN, Inf])), {'NaN'})
%!assert_equal (dispstrings (calendarDuration ([0, 0, -Inf])), {'-Inf'})
%!assert_equal (dispstrings (calendarDuration ([0, Inf, -Inf])), {'NaN'})
%!assert_equal (char (calendarDuration (0, 0, NaN)), 'NaN')
%!assert_equal (char (calendarDuration (0, 0, Inf)), 'Inf')
%!assert_equal (char (calendarDuration (0, NaN, Inf)), 'NaN')
%!assert_equal (char (calendarDuration (0, 0, -Inf)), '-Inf')
%!assert_equal (char (calendarDuration (0, Inf, -Inf)), 'NaN')
%!assert_equal (dispstrings (calendarDuration (1, 3, 1, 'Format', 'mwdt')), {'15mo 1d'})
%!assert_equal (dispstrings (calendarDuration (1, 3, 1, 'Format', 'ymwdt')), {'1y 3mo 1d'})
%!assert_equal (dispstrings (calendarDuration (1, 3, 15, 'Format', 'ymwdt')), {'1y 3mo 2w 1d'})
%!assert_equal (dispstrings (calendarDuration (1, 3, 15, 'Format', 'mwdt')), {'15mo 2w 1d'})
%!assert_equal (dispstrings (calendarDuration (1, 3, 15, 'Format', 'ymdt')), {'1y 3mo 15d'})
%!assert_equal (dispstrings (calendarDuration (1, 3, 15, 'Format', 'mdt')), {'15mo 15d'})
%!assert_equal (dispstrings (calendarDuration (0, 15, 15, 'Format', 'ymdt')), {'1y 3mo 15d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0)), {'0d'})
%!assert_equal (dispstrings (calendarDuration (1, 0, 0)), {'1y'})
%!assert_equal (dispstrings (calendarDuration (0, 1, 0)), {'1mo'})
%!assert_equal (dispstrings (calendarDuration (0, 12, 0)), {'1y'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 1)), {'1d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 14)), {'14d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 14, 'Format', 'mwdt')), {'2w'})
%!assert_equal (dispstrings (calendarDuration (0, 1, 0, minutes (5))), {'1mo 0h 5m 0s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (5))), {'0h 5m 0s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (50.05))), {'0h 50m 3s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (5.005))), {'0h 5m 0.3s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (5.000005))), {'0h 5m 0.0003s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (5.0000005))), {'0h 5m 3e-05s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, minutes (-5.000005))), {'0h -5m -0.0003s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, duration (3, 5, 0.3))), {'3h 5m 0.3s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, -0.5)), {'0h 0m -0.5s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, -1.5)), {'0h 0m -1.5s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, -0.25)), {'0h 0m -0.25s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, -0.001)), {'0h 0m -0.001s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, -1e-9)), {'0h 0m -1e-09s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, -1, -2, -3.5)), {'-1h -2m -3.5s'})
%!assert_equal (dispstrings (calendarDuration (1, 2, 3, 0, 0, -0.5)), {'1y 2mo 3d 0h 0m -0.5s'})
%!assert_equal (dispstrings (calendarDuration (-1, -2, -3, -4, -5, -6.5)), {'-1y -2mo -3d -4h -5m -6.5s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 0.0005)), {'0h 0m 0.0005s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 0.9995)), {'0h 0m 0.9995s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 6.0005)), {'0h 0m 6.0005s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 6.123456789)), {'0h 0m 6.1235s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 0.00012345678)), {'0h 0m 0.00012346s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 2.5e-05)), {'0h 0m 2.5e-05s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 59.9999)), {'0h 0m 60s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 0, 0, 12345.6789)), {'3h 25m 45.679s'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 999999)), {'999999d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 1000000)), {'1e+06d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 1234567)), {'1.23457e+06d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, -1234567)), {'-1.23457e+06d'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 1e10)), {'1e+10d'})
%!assert_equal (dispstrings (calendarDuration (1000000, 0, 0)), {'1e+06y'})
%!assert_equal (dispstrings (calendarDuration (0, 1234567, 0, 'Format', 'mdt')), {'1.23457e+06mo'})
%!assert_equal (dispstrings (calendarDuration (0, 2^53, 0, 'Format', 'mdt')), {'9.0072e+15mo'})
%!assert_equal (dispstrings (calendarDuration (0, 0, 0, 1000000, 0, 0)), {'1e+06h 0m 0s'})

%!assert_equal (cellstr (calendarDuration (1, 3, 1), 'mwdt'), {'15mo 1d'})
%!assert_equal (cellstr (calendarDuration (1, 3, 1), 'ymwdt'), {'1y 3mo 1d'})
%!assert_equal (cellstr (calendarDuration (1, 3, 15), 'ymwdt'), {'1y 3mo 2w 1d'})
%!assert_equal (cellstr (calendarDuration (1, 3, 15), 'mwdt'), {'15mo 2w 1d'})
%!assert_equal (cellstr (calendarDuration (1, 3, 15), 'ymdt'), {'1y 3mo 15d'})
%!assert_equal (cellstr (calendarDuration (1, 3, 15), 'mdt'), {'15mo 15d'})
%!assert_equal (cellstr (calendarDuration (0, 15, 15), 'ymdt'), {'1y 3mo 15d'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0)), {'0d'})
%!assert_equal (cellstr (calendarDuration (1, 0, 0)), {'1y'})
%!assert_equal (cellstr (calendarDuration (0, 1, 0)), {'1mo'})
%!assert_equal (cellstr (calendarDuration (0, 12, 0)), {'1y'})
%!assert_equal (cellstr (calendarDuration (0, 0, 1)), {'1d'})
%!assert_equal (cellstr (calendarDuration (0, 0, 14)), {'14d'})
%!assert_equal (cellstr (calendarDuration (0, 0, 14), 'mwdt'), {'2w'})
%!assert_equal (cellstr (calendarDuration (0, 1, 0, minutes (5))), {'1mo 0h 5m 0s'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0, minutes (5))), {'0h 5m 0s'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0, minutes (50.05))), {'0h 50m 3s'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0, minutes (5.005))), {'0h 5m 0.3s'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0, minutes (5.000005))), {'0h 5m 0.0003s'})
%!assert_equal (cellstr (calendarDuration (0, 0, 0, duration (3, 5, 0.3))), {'3h 5m 0.3s'})
%!error<calendarDuration.cellstr: FMT must be a character vector or a string scalar.> ...
%! cellstr (calendarDuration ([0, 0, 1]), 1)
%!error<calendarDuration.cellstr: FMT must be a character vector or a string scalar.> ...
%! cellstr (calendarDuration ([0, 0, 1]), {'YY-MM-DD'})
%!error<calendarDuration.cellstr: FMT contains duplicate characters.> ...
%! cellstr (calendarDuration ([0, 0, 1]), 'yymd')
%!error<calendarDuration.cellstr: FMT must contain 'm', 'd', and 't'.> ...
%! cellstr (calendarDuration ([0, 0, 1]), 'ymd')
%!error<calendarDuration.cellstr: FMT has invalid order of characters.> ...
%! cellstr (calendarDuration ([0, 0, 1]), 'mtd')
%!error<calendarDuration.cellstr: FMT must only contain characters from the 'yqmwdt' sequence.> ...
%! cellstr (calendarDuration ([0, 0, 1]), 'ymdtz')
%!assert_equal (cellstr (calendarDuration (1, 2, 3), ''), {'1y 2mo 3d'})
%!test
%! X = calendarDuration ([1, 2; 3, 4], 2, 3);
%! assert_equal (cellstr (X), {'1y 2mo 3d', '2y 2mo 3d'; '3y 2mo 3d', '4y 2mo 3d'});
%!test
%! X = calendarDuration (ones (2, 2, 2), 0, 0);
%! assert_equal (size (cellstr (X)), [2, 2, 2]);
%!test
%! X = calendarDuration ([], [], []);
%! assert_equal (size (cellstr (X)), [0, 0]);
%! assert_equal (class (cellstr (X)), 'cell');
%!test
%! X = calendarDuration ([1, 1000000], 0, [3, 0]);
%! assert_equal (cellstr (X), {'1y 3d', '1e+06y'});
%!assert_equal (cellstr (calendarDuration (1, 2, 3, 'Format', 'qmdt')), {'4q 2mo 3d'})

%!assert_equal (char (calendarDuration (1, 3, 1), 'mwdt'), '15mo 1d')
%!assert_equal (char (calendarDuration (1, 3, 1), 'ymwdt'), '1y 3mo 1d')
%!assert_equal (char (calendarDuration (1, 3, 15), 'ymwdt'), '1y 3mo 2w 1d')
%!assert_equal (char (calendarDuration (1, 3, 15), 'mwdt'), '15mo 2w 1d')
%!assert_equal (char (calendarDuration (1, 3, 15), 'ymdt'), '1y 3mo 15d')
%!assert_equal (char (calendarDuration (1, 3, 15), 'mdt'), '15mo 15d')
%!assert_equal (char (calendarDuration (0, 15, 15), 'ymdt'), '1y 3mo 15d')
%!assert_equal (char (calendarDuration (0, 0, 0)), '0d')
%!assert_equal (char (calendarDuration (1, 0, 0)), '1y')
%!assert_equal (char (calendarDuration (0, 1, 0)), '1mo')
%!assert_equal (char (calendarDuration (0, 12, 0)), '1y')
%!assert_equal (char (calendarDuration (0, 0, 1)), '1d')
%!assert_equal (char (calendarDuration (0, 0, 14)), '14d')
%!assert_equal (char (calendarDuration (0, 0, 14), 'mwdt'), '2w')
%!assert_equal (char (calendarDuration (0, 1, 0, minutes (5))), '1mo 0h 5m 0s')
%!assert_equal (char (calendarDuration (0, 0, 0, minutes (5))), '0h 5m 0s')
%!assert_equal (char (calendarDuration (0, 0, 0, minutes (50.05))), '0h 50m 3s')
%!assert_equal (char (calendarDuration (0, 0, 0, minutes (5.005))), '0h 5m 0.3s')
%!assert_equal (char (calendarDuration (0, 0, 0, minutes (5.000005))), '0h 5m 0.0003s')
%!assert_equal (char (calendarDuration (0, 0, 0, duration (3, 5, 0.3))), '3h 5m 0.3s')
%!error<calendarDuration.char: FMT must be a character vector or a string scalar.> ...
%! char (calendarDuration ([0, 0, 1]), 1)
%!error<calendarDuration.char: FMT must be a character vector or a string scalar.> ...
%! char (calendarDuration ([0, 0, 1]), {'YY-MM-DD'})
%!error<calendarDuration.char: FMT contains duplicate characters.> ...
%! char (calendarDuration ([0, 0, 1]), 'yymd')
%!error<calendarDuration.char: FMT must contain 'm', 'd', and 't'.> ...
%! char (calendarDuration ([0, 0, 1]), 'ymd')
%!error<calendarDuration.char: FMT has invalid order of characters.> ...
%! char (calendarDuration ([0, 0, 1]), 'mtd')
%!error<calendarDuration.char: FMT must only contain characters from the 'yqmwdt' sequence.> ...
%! char (calendarDuration ([0, 0, 1]), 'ymdtz')
%!assert_equal (char (calendarDuration (1, 2, 3), ''), '1y 2mo 3d')
%!assert_equal (char (calendarDuration (1, 2, 3), string ('mdt')), '14mo 3d')
%!test
%! X = calendarDuration ([1, 1000000], 0, [3, 0]);
%! assert_equal (char (X), [' 1y 3d'; '1e+06y']);
%!test
%! X = calendarDuration ([1, NaN], 2, 3);
%! assert_equal (char (X), ['1y 2mo 3d'; '      NaN']);
%!test
%! X = calendarDuration ([1, 2; 3, 4], 2, 3);
%! cmat = char (X);
%! assert_equal (size (cmat), [4, 9]);
%! assert_equal (cmat(2,:), '3y 2mo 3d');
%!test
%! X = calendarDuration (ones (2, 2, 2), 0, 0);
%! assert_equal (size (char (X)), [8, 2]);
%!test
%! X = calendarDuration ([], [], []);
%! assert_equal (size (char (X)), [0, 0]);
%! assert_equal (class (char (X)), 'char');

%!test
%! X = calendarDuration ([1:5]', 3, 15);
%! DV = datevec (X);
%! assert_equal (size (X), [5, 1]);
%! assert_equal (size (DV), [5, 6]);
%! assert_equal (DV(:,1:3), [1, 3, 15; 2, 3, 15; 3, 3, 15; 4, 3, 15; 5, 3, 15]);
%! assert_equal (DV(:,4:6), zeros (5, 3));
%!test
%! X = calendarDuration ([1:5]', 3, 15);
%! [Y, MO, D] = datevec (X);
%! assert_equal (size (Y), [5, 1]);
%! assert_equal (size (MO), [5, 1]);
%! assert_equal (size (D), [5, 1]);
%! assert_equal (Y, [1:5]');
%! assert_equal (MO, [3; 3; 3; 3; 3]);
%! assert_equal (D, 15 * ones (5, 1));
%!test
%! X = calendarDuration ([1:5]', 3, 15);
%! [~, ~, ~, H, MI, S] = datevec (X);
%! assert_equal (size (H), [5, 1]);
%! assert_equal (size (MI), [5, 1]);
%! assert_equal (size (S), [5, 1]);
%! assert_equal (H, zeros (5, 1));
%! assert_equal (MI, zeros (5, 1));
%! assert_equal (S, zeros (5, 1));
%!test
%! X = calendarDuration (reshape (1:24, 2, 3, 4), 0, reshape (25:48, 2, 3, 4));
%! DV = datevec (X);
%! assert_equal (size (X), [2, 3, 4]);
%! assert_equal (size (DV), [24, 6]);
%! assert_equal (DV(:,1), [1:24]');
%! assert_equal (DV(:,2), zeros (24, 1));
%! assert_equal (DV(:,3), [25:48]');
%! assert_equal (DV(:,4:6), zeros (24, 3));
%!test
%! X = calendarDuration (reshape (1:24, 2, 3, 4), 0, reshape (25:48, 2, 3, 4));
%! [Y, MO, D] = datevec (X);
%! assert_equal (size (Y), [2, 3, 4]);
%! assert_equal (size (MO), [2, 3, 4]);
%! assert_equal (size (D), [2, 3, 4]);
%! assert_equal (Y, reshape (1:24, 2, 3, 4));
%! assert_equal (MO, zeros (2, 3, 4));
%! assert_equal (D, reshape (25:48, 2, 3, 4));
%!test
%! X = calendarDuration (reshape (1:24, 2, 3, 4), 0, reshape (25:48, 2, 3, 4));
%! [~, ~, ~, H, MI, S] = datevec (X);
%! assert_equal (size (H), [2, 3, 4]);
%! assert_equal (size (MI), [2, 3, 4]);
%! assert_equal (size (S), [2, 3, 4]);
%! assert_equal (H, zeros (2, 3, 4));
%! assert_equal (MI, zeros (2, 3, 4));
%! assert_equal (S, zeros (2, 3, 4));
%!test
%! D = days (0.51);
%! X = calendarDuration (1, 0, 0, D);
%! [Y, ~, ~, H, MI, S] = datevec (X);
%! [h, mi, s] = hms (D);
%! assert_equal (Y, 1);
%! assert_equal (H, h);
%! assert_equal (MI, mi);
%! assert_equal (S, s);
%!test
%! D = days (repmat ([0.51, 0.46; 0.2, 1.23], 1, 2, 3));
%! X = calendarDuration (1, 0, 0, D);
%! [Y, ~, ~, H, MI, S] = datevec (X);
%! [h, mi, s] = hms (D);
%! assert_equal (Y, ones (2, 4, 3));
%! assert_equal (H, h);
%! assert_equal (MI, mi);
%! assert_equal (S, s);
%!test
%! X = calendarDuration (1, 0, 1, 0, reshape (1:24, 2, 3, 4), 0);
%! [Y, MO, D, H, MI, S] = datevec (X);
%! assert_equal (Y, ones (2, 3, 4));
%! assert_equal (MO, zeros (2, 3, 4));
%! assert_equal (D, ones (2, 3, 4));
%! assert_equal (H, zeros (2, 3, 4));
%! assert_equal (MI, reshape (1:24, 2, 3, 4));
%! assert_equal (S, zeros (2, 3, 4));
%! DV = datevec (X);
%! assert_equal (DV(:,[1, 3]), ones (24, 2));
%! assert_equal (DV(:,[2, 4, 6]), zeros (24, 3));
%! assert_equal (DV(:,5), [1:24]');
%!error<calendarDuration.datevec: too many output arguments.> ...
%! [Y, MO, D, H, MI, S, MS] = datevec (calendarDuration)
%!assert_equal (datevec (calendarDuration (Inf, 0, 0)), Inf (1, 6))
%!assert_equal (datevec (calendarDuration (-Inf, 0, 0)), -Inf (1, 6))
%!assert_equal (datevec (calendarDuration (0, 0, Inf)), Inf (1, 6))
%!assert_equal (datevec (calendarDuration (0, 0, 0, 0, 0, Inf)), Inf (1, 6))
%!assert_equal (datevec (calendarDuration (NaN, 0, 0)), NaN (1, 6))
%!test
%! [Y, MO, D, H, MI, S] = datevec (calendarDuration (Inf, 0, 0));
%! assert_equal ([Y, MO, D, H, MI, S], Inf (1, 6));
%!test
%! DV = datevec (calendarDuration ([1, NaN, Inf], 2, 3));
%! assert_equal (size (DV), [3, 6]);
%! assert_equal (DV(1,:), [1, 2, 3, 0, 0, 0]);
%! assert_equal (DV(2,:), NaN (1, 6));
%! assert_equal (DV(3,:), Inf (1, 6));
%!assert_equal (datevec (calendarDuration (-1, -2, -3, -4, -5, -6.5)), [-1, -2, -3, -4, -5, -6.5])
%!assert_equal (datevec (calendarDuration (0, 0, 0, 0, 0, -0.5)), [0, 0, 0, 0, 0, -0.5])
%!assert_equal (datevec (calendarDuration (0, 0, 0, 0, 0, 6.123456789)), [0, 0, 0, 0, 0, 6.123456789], 1e-9)
%!assert_equal (datevec (calendarDuration (0, 25, 0)), [2, 1, 0, 0, 0, 0])
%!assert_equal (datevec (calendarDuration (0, -25, 0)), [-2, -1, 0, 0, 0, 0])
%!assert_equal (datevec (calendarDuration (0, 0, 0, 25, 0, 0)), [0, 0, 0, 25, 0, 0])
%!assert_equal (datevec (calendarDuration (0, 0, 0, 0, 90, 0)), [0, 0, 0, 1, 30, 0])
%!assert_equal (size (datevec (calendarDuration ([], [], []))), [0, 6])
%!test
%! DV = datevec (calendarDuration (ones (2, 2, 2), 2, 3));
%! assert_equal (size (DV), [8, 6]);
%!test
%! [Y, MO, D, H, MI, S] = datevec (calendarDuration (ones (2, 2, 2), 2, 3));
%! assert_equal (size (Y), [2, 2, 2]);
%! assert_equal (size (S), [2, 2, 2]);

%!assert_equal (size (time (calendarDuration (1, 1, 1))), [1, 1])
%!assert_equal (size (time (calendarDuration (ones (2, 3, 4), 1, 1))), [2, 3, 4])
%!assert_equal (size (time (calendarDuration (1, 1, 1, days (1)))), [1, 1])
%!assert_equal (size (time (calendarDuration (0, 0, 0, ones (2, 3, 4), 1, 1))), [2, 3, 4])
%!test
%! t = time (calendarDuration (1, 1, 1));
%! assert_equal (class (t), 'duration');
%! [h, m, s] = hms (t);
%! assert_equal (h, 0);
%! assert_equal (m, 0);
%! assert_equal (s, 0);
%!test
%! t = time (calendarDuration (1, ones (2), 1, hours (1.5)));
%! assert_equal (size (t), [2, 2]);
%! [h, m, s] = hms (t);
%! assert_equal (h, ones (2));
%! assert_equal (m, 30 * ones (2));
%! assert_equal (s, zeros (2));
%!test
%! t = time (calendarDuration (1, ones (2), 1, 0, 0, 0.0251));
%! assert_equal (size (t), [2, 2]);
%! [h, m, s] = hms (t);
%! assert_equal (s, [0.0251, 0.0251; 0.0251, 0.0251]);
%!assert_equal (seconds (time (calendarDuration (0, 0, 0, 1, 0, 1))), 3601)
%!assert_equal (seconds (time (calendarDuration (0, 0, 1, 1, 0, 1))), 3601)
%!assert_equal (seconds (time (calendarDuration (0, 0, 0, 1, 30, 2.5))), 5402.5)
%!assert_equal (seconds (time (calendarDuration (0, 1, 0, 1, 30, 2.5))), 5402.5)
%!assert_equal (minutes (time (calendarDuration (0, 0, 0, 1, 0, 1))), 60.017, 1e-3)
%!assert_equal (minutes (time (calendarDuration (0, 1, 1, 1, 0, 1))), 60.017, 1e-3)
%!assert_equal (minutes (time (calendarDuration (0, 0, 1, 1, 30, 2.5))), 90.042, 1e-3)
%!assert_equal (minutes (time (calendarDuration (1, 1, 0, 1, 30, 62.5))), 91.042, 1e-3)
%!assert_equal (fix (minutes (time (calendarDuration (0, 0, 0, 1, 0, 1)))), 60)
%!assert_equal (fix (minutes (time (calendarDuration (0, 1, 1, 1, 0, 1)))), 60)
%!assert_equal (fix (minutes (time (calendarDuration (0, 0, 1, 1, 30, 2.5)))), 90)
%!assert_equal (fix (minutes (time (calendarDuration (1, 1, 0, 1, 30, 62.5)))), 91)
%!assert_equal (hours (time (calendarDuration (0, 0, 1, 1, 0, 0))), 1)
%!assert_equal (hours (time (calendarDuration (0, 0, 1, 1, 30, 0))), 1.5)
%!assert_equal (hours (time (calendarDuration (0, 0, 1, 1, 0, 1800))), 1.5)
%!assert_equal (hours (time (calendarDuration (0, [1, 2], 1, 1, 0, 0))), [1, 1])
%!assert_equal (hours (time (calendarDuration (0, 0, 1, [1; 2], 30, 0))), [1.5; 2.5])
%!assert_equal (hours (time (calendarDuration (0, 0, 1, 1, [0, 0], 1800))), [1.5, 1.5])
%!assert_equal (seconds (time (calendarDuration (Inf, 0, 0))), Inf)
%!assert_equal (seconds (time (calendarDuration (-Inf, 0, 0))), -Inf)
%!assert_equal (seconds (time (calendarDuration (NaN, 0, 0))), NaN)
%!assert_equal (size (time (calendarDuration ([], [], []))), [0, 0])
%!assert_equal (hours (time (calendarDuration (0, 0, 0, 25, 0, 0))), 25)
%!assert_equal (seconds (time (calendarDuration (-1, -14, -40, -4, -5, -6.5))), -14706.5)

%!test
%! X = calendarDuration (1, 0, 15);
%! assert_equal (split (X, 'years'), 1);
%! [Y, Q, W, D] = split (X, {'years', 'quarters', 'weeks', 'days'});
%! assert_equal (Y, 1);
%! assert_equal (Q, 0);
%! assert_equal (W, 2);
%! assert_equal (D, 1);
%!test
%! X = calendarDuration (1, 0, 15);
%! T = split (X, 'time');
%! assert_equal (isduration (T), true);
%! assert_equal (hours (T), 0);
%! assert_equal (minutes (T), 0);
%! assert_equal (seconds (T), 0);
%! assert_equal (dispstrings (T), {'00:00:00'});
%!test
%! X = calendarDuration (1, ones (2), 15);
%! T = split (X, 'time');
%! assert_equal (isduration (T), true);
%! assert_equal (hours (T), zeros (2));
%! assert_equal (minutes (T), zeros (2));
%! assert_equal (seconds (T), zeros (2));
%! assert_equal (dispstrings (T), repmat ({'00:00:00'}, 2, 2));
%!test
%! X = calendarDuration (1, 0, 15);
%! assert_equal (split (X, 'years'), 1);
%! [Q, W, D] = split (X, {'quarters', 'weeks', 'days'});
%! assert_equal (Q, 4);
%! assert_equal (W, 2);
%! assert_equal (D, 1);
%!test
%! X = calendarDuration (1, zeros (2, 3, 4), 15);
%! assert_equal (split (X, 'years'), ones (2, 3, 4));
%! [Q, W, D] = split (X, {'quarters', 'weeks', 'days'});
%! assert_equal (Q, 4 * ones (2, 3, 4));
%! assert_equal (W, 2 * ones (2, 3, 4));
%! assert_equal (D, 1 * ones (2, 3, 4));
%!test
%! X = calendarDuration (1, 0, 15);
%! assert_equal (split (X, 'years'), 1);
%! [Q, D] = split (X, {'quarters', 'days'});
%! assert_equal (Q, 4);
%! assert_equal (D, 15);
%!test
%! X = calendarDuration (1, 4, 64);
%! assert_equal (split (X, 'years'), 1);
%! [Q, M, D] = split (X, {'quarters', 'months', 'days'});
%! assert_equal (Q, 5);
%! assert_equal (M, 1);
%! assert_equal (D, 64);
%!test
%! X = calendarDuration (1, 4, 64:69);
%! assert_equal (split (X, 'years'), ones (1, 6));
%! [Q, M, D] = split (X, {'quarters', 'months', 'days'});
%! assert_equal (Q, [5, 5, 5, 5, 5, 5]);
%! assert_equal (M, [1, 1, 1, 1, 1, 1]);
%! assert_equal (D, [64:69]);
%!test
%! X = calendarDuration (1, 4, 64, 0, 35, 30);
%! [Q, M, W, T] = split (X, {'quarters', 'months', 'weeks', 'time'});
%! assert_equal (Q, 5);
%! assert_equal (M, 1);
%! assert_equal (W, 9);
%! assert_equal (minutes (T), 35.5);
%! assert_equal (seconds (T), 2130);
%!test
%! X = calendarDuration (1, 4, 64, 0, 35, [30; 60]);
%! [Q, M, W, T] = split (X, {'quarters', 'months', 'weeks', 'time'});
%! assert_equal (Q, [5; 5]);
%! assert_equal (M, [1; 1]);
%! assert_equal (W, [9; 9]);
%! assert_equal (minutes (T), [35.5; 36]);
%! assert_equal (seconds (T), [2130; 2160]);
%!error<calendarDuration.split: too few input arguments.> split (calendarDuration)
%!error<calendarDuration.split: invalid input type for UNITS.> ...
%! split (calendarDuration, 1)
%!error<calendarDuration.split: invalid input type for UNITS.> ...
%! split (calendarDuration, {1})
%!error<calendarDuration.split: 'ert' is not a valid time unit.> ...
%! split (calendarDuration, 'ert')
%!error<calendarDuration.split: 'ert' is not a valid time unit.> ...
%! split (calendarDuration, {'time', 'ert'})
%!error<calendarDuration.split: 'ert' is not a valid time unit.> ...
%! split (calendarDuration, string ({'time', 'ert'}))
%!error<calendarDuration.split: UNITS must be specified in descending order.> ...
%! split (calendarDuration, {'time', 'years'})
%!error<calendarDuration.split: UNITS must be specified in descending order.> ...
%! split (calendarDuration, string ({'months', 'weeks', 'years'}))
%!error<calendarDuration.split: too many output arguments.> ...
%! [Y, M, D] = split (calendarDuration, {'years', 'months'})
%!error<calendarDuration.split: too many output arguments.> ...
%! [Y, M] = split (calendarDuration, {'years', 'years'})
%!error<calendarDuration.split: UNITS must name at least one unit.> ...
%! split (calendarDuration, {})
%!error<calendarDuration.split: UNITS must not contain empty names.> ...
%! split (calendarDuration, '')
%!error<calendarDuration.split: 'years ' is not a valid time unit.> ...
%! split (calendarDuration, 'years ')
%!error<calendarDuration.split: 'yearss' is not a valid time unit.> ...
%! split (calendarDuration, 'yearss')
%!error<calendarDuration.split: 'times' is not a valid time unit.> ...
%! split (calendarDuration, 'times')
%!test
%! X = calendarDuration (1, 14, 40, 4, 5, 6.5);
%! assert_equal (split (X, 'y'), 2);
%! assert_equal (split (X, 'Year'), 2);
%! assert_equal (split (X, 'YEARS'), 2);
%! assert_equal (split (X, 'q'), 8);
%! assert_equal (split (X, 'm'), 26);
%! assert_equal (split (X, 'mo'), 26);
%! assert_equal (split (X, 'w'), 5);
%! assert_equal (split (X, 'd'), 40);
%! assert_equal (seconds (split (X, 't')), 14706.5);
%! assert_equal (seconds (split (X, 'tim')), 14706.5);
%!test
%! X = calendarDuration (1, 14, 40, 4, 5, 6.5);
%! Y = split (X, {'years', 'quarters', 'months'});
%! assert_equal (Y, 2);
%! [Y, Q] = split (X, {'years', 'quarters', 'months'});
%! assert_equal ([Y, Q], [2, 0]);
%! [Y, Q, MO] = split (X, {'years', 'quarters', 'months'});
%! assert_equal ([Y, Q, MO], [2, 0, 2]);
%!test
%! X = calendarDuration (1, 14, 40, 4, 5, 6.5);
%! assert_equal (split (X, {'years', 'years'}), 2);
%! assert_equal (split (X, {'months', 'months'}), 26);
%! [Y, MO] = split (X, {'year', 'month'});
%! assert_equal ([Y, MO], [2, 2]);
%!test
%! X = calendarDuration (Inf, 0, 0);
%! [Y, Q, MO, W, D, T] = split (X, ...
%!                       {'years', 'quarters', 'months', 'weeks', 'days', 'time'});
%! assert_equal ([Y, Q, MO, W, D], Inf (1, 5));
%! assert_equal (seconds (T), Inf);
%!test
%! X = calendarDuration (-Inf, 0, 0);
%! [Y, MO, D] = split (X, {'years', 'months', 'days'});
%! assert_equal ([Y, MO, D], -Inf (1, 3));
%!test
%! X = calendarDuration (NaN, 0, 0);
%! [Y, MO, D] = split (X, {'years', 'months', 'days'});
%! assert_equal ([Y, MO, D], NaN (1, 3));

%!assert_equal (caldays (caldays (5)), 5)
%!assert_equal (caldays (caldays (1:10)), 1:10)
%!assert_equal (caldays (caldays (ones (2, 3, 4))), ones (2, 3, 4))
%!assert_equal (caldays (calendarDuration (0, 0, [1, 2; 3, 4])), [1, 2; 3, 4])
%!test
%! x = randi (20, 2, 3);
%! assert_equal (caldays (calendarDuration (0, 0, x)), x);

%!assert_equal (calweeks (calweeks (5)), 5)
%!assert_equal (calweeks (calweeks (1:10)), 1:10)
%!assert_equal (calweeks (calweeks (ones (2, 3, 4))), ones (2, 3, 4))
%!assert_equal (calweeks (calendarDuration (0, 0, 7 * [1, 2; 3, 4])), [1, 2; 3, 4])
%!test
%! x = randi (20, 2, 3, 4);
%! assert_equal (calweeks (calendarDuration (0, 0, x * 7)), x);

%!assert_equal (calmonths (calmonths (5)), 5)
%!assert_equal (calmonths (calmonths (1:10)), 1:10)
%!assert_equal (calmonths (calmonths (ones (2, 3, 4))), ones (2, 3, 4))
%!assert_equal (calmonths (calendarDuration (0, [1, 2; 3, 4], 0)), [1, 2; 3, 4])
%!test
%! x = randi (20, 2, 3);
%! assert_equal (calmonths (calendarDuration (0, x, 0)), x);

%!assert_equal (calquarters (calquarters (5)), 5)
%!assert_equal (calquarters (calquarters (1:10)), 1:10)
%!assert_equal (calquarters (calquarters (ones (2, 3, 4))), ones (2, 3, 4))
%!assert_equal (calquarters (calendarDuration (0, 3 * [1, 2; 3, 4], 0)), [1, 2; 3, 4])
%!test
%! x = randi (20, 2, 3, 4);
%! assert_equal (calquarters (calendarDuration (0, x * 3, 0)), x);

%!assert_equal (calyears (calyears (5)), 5)
%!assert_equal (calyears (calyears (1:10)), 1:10)
%!assert_equal (calyears (calyears (ones (2, 3, 4))), ones (2, 3, 4))
%!assert_equal (calyears (calendarDuration ([1, 2; 3, 4], 0, 0)), [1, 2; 3, 4])
%!test
%! x = randi (20, 2, 3);
%! assert_equal (calyears (calendarDuration (x, 0, 0)), x);
%!assert_equal (calweeks (calendarDuration (0, 0, -21:21)), floor ((-21:21) / 7))
%!assert_equal (calweeks (calendarDuration (0, 0, -15)), -3)
%!assert_equal (calweeks (calendarDuration (0, 0, -13)), -2)
%!assert_equal (calweeks (calendarDuration (0, 0, -8)), -2)
%!assert_equal (calweeks (calendarDuration (0, 0, -7)), -1)
%!assert_equal (calweeks (calendarDuration (0, 0, -1)), -1)
%!assert_equal (calquarters (calendarDuration (0, -13:13, 0)), floor ((-13:13) / 3))
%!assert_equal (calquarters (calendarDuration (0, -1, 0)), -1)
%!assert_equal (calquarters (calendarDuration (0, -4, 0)), -2)
%!assert_equal (calyears (calendarDuration (0, -13:13, 0)), floor ((-13:13) / 12))
%!assert_equal (calyears (calendarDuration (0, -1, 0)), -1)
%!assert_equal (calyears (calendarDuration (0, -13, 0)), -2)
%!assert_equal (calquarters (calendarDuration (0, Inf, 0)), Inf)
%!assert_equal (calyears (calendarDuration (0, -Inf, 0)), -Inf)
%!assert_equal (caldays (calendarDuration ([], [], [])), [])
%!assert_equal (calweeks (calendarDuration ([], [], [])), [])
%!assert_equal (caldays (calendarDuration (0, 0, 15, 5, 0, 0)), 15)
%!assert_equal (calweeks (calendarDuration (0, 0, 15, 5, 0, 0)), 2)
%!assert_equal (caldays (calendarDuration (0, [0, 0], [3, 4])), [3, 4])
%!assert_equal (calmonths (calendarDuration (0, [0, 2], [3, 4])), [0, 2])
%!assert_equal (calyears (calendarDuration (0, [0, 2], [3, 4])), [0, 0])
%!assert_equal (calquarters (calendarDuration (0, [0, 2], [3, 4])), [0, 0])
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (0, 2, 14))
%!error<calendarDuration.calweeks: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! calweeks (calendarDuration (0, 2, 14))
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (1, 0, 0))
%!assert_equal (caldays (calendarDuration (NaN, 0, 0)), NaN)
%!assert_equal (caldays (calendarDuration (Inf, 0, 0)), Inf)
%!assert_equal (calweeks (calendarDuration (0, 0, Inf)), Inf)
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (0, [0, 2], [3, 4]))
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (0, -2, 14))
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (-1, 2, 0))
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (-1, 0, 14))
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (0, -1, 0, 5, 0, 0))
%!assert_equal (caldays (calendarDuration (0, -Inf, 0)), -Inf)
%!assert_equal (caldays (calendarDuration (0, [0, NaN], [3, 4])), [3, NaN])
%!assert_equal (caldays (calendarDuration (0, [0, Inf], [3, 4])), [3, Inf])
%!error<calendarDuration.calweeks: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! calweeks (calendarDuration (0, -2, -14))
%!assert_equal (calweeks (calendarDuration (0, 0, NaN)), NaN)
%!assert_equal (caldays (calendarDuration (0, 0, -15, 5, 0, 0)), -15)
%!assert_equal (caldays (calendarDuration (0, 0, -15, -5, 0, 0)), -15)
%!assert_equal (caldays (calendarDuration (0, 0, 0, -5, 0, 0)), 0)
%!assert_equal (caldays (-calendarDuration (0, 0, 3, 4, 5, 6.5)), -3)
%!assert_equal (calweeks (calendarDuration (0, 0, -15, -5, 0, 0)), -3)
%!assert_equal (calmonths (calendarDuration (-1, 2, 0)), -10)
%!assert_equal (calmonths (calendarDuration (1, -2, 0)), 10)
%!assert_equal (calmonths (calendarDuration (0, -14, 0)), -14)
%!assert_equal (calmonths (calyears (1) - calmonths (14)), -2)
%!assert_equal (calmonths (calendarDuration (1, 2, 3) - calendarDuration (0, 0, 10)), 14)
%!error<calendarDuration.caldays: cannot convert a calendarDuration to days or weeks when it contains a non-zero number of months. Use 'split' instead.> ...
%! caldays (calendarDuration (1, 2, 3) - calendarDuration (0, 0, 10))
%!assert_equal (caldays (caldays (3) - caldays (10)), -7)
%!assert_equal (calweeks (caldays (3) - caldays (10)), -1)
%!assert_equal (caldays (caldays ([1, 2, NaN, 4])), [1, 2, NaN, 4])
%!assert_equal (calweeks (calweeks ([1, 2, NaN, 4])), [1, 2, NaN, 4])
%!assert_equal (caldays (caldays ([1, Inf])), [1, Inf])
%!assert_equal (calmonths (calendarDuration (0, [0, NaN], [3, 4])), [0, NaN])

################################################################################
##                         ** Summary Information **                          ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'size'             'ndims'            'numel'            'nnz'             ##
## 'length'           'keyHash'                                               ##
##                                                                            ##
################################################################################

%!assert_equal (size (calendarDuration), [1, 1])
%!assert_equal (size (calendarDuration ([], [], [])), [0, 0])
%!assert_equal (size (calendarDuration (ones (0, 3))), [0, 1])
%!assert_equal (size (calendarDuration (ones (0, 1), ones (0, 1), ones (0, 1))), [0, 1])
%!assert_equal (size (calendarDuration (ones (0, 3), ones (0, 3), ones (0, 3))), [0, 3])
%!assert_equal (size (calendarDuration (ones (1, 0), ones (1, 0), ones (1, 0))), [1, 0])
%!assert_equal (size (calendarDuration (ones (5, 3))), [5, 1])
%!assert_equal (size (calendarDuration (ones (5, 6))), [5, 1])
%!assert_equal (size (calendarDuration (ones (2, 3, 4), 0, 0)), [2, 3, 4])
%!assert_equal (size (calendarDuration (ones (1:10), 0, 0)), 1:10)
%!test
%! X = calendarDuration (ones (2, 3, 4), 0, 0);
%! [d1, d2, d3] = size (X);
%! assert_equal (d1, 2);
%! assert_equal (d2, 3);
%! assert_equal (d3, 4);
%!test
%! X = calendarDuration (0, 0, 0, minutes (randi (30, 2, 3, 4)));
%! [d1, d2, d3] = size (X);
%! assert_equal (d1, 2);
%! assert_equal (d2, 3);
%! assert_equal (d3, 4);
%!test
%! X = calendarDuration (0, 0, 0, minutes (randi (30, 2, 3, 4)));
%! [d1, d2, d3] = size (X);
%! assert_equal (size (X, 1), 2);
%! assert_equal (size (X, 2), 3);
%! assert_equal (size (X, 3), 4);
%!test
%! [d1, d2] = size (calendarDuration (ones (2, 3, 4), 0, 0));
%! assert_equal ([d1, d2], [2, 12]);

%!assert_equal (ndims (calendarDuration ([], [], [])), 2)
%!assert_equal (ndims (calendarDuration (1, 1, 1)), 2)
%!assert_equal (ndims (calendarDuration), 2)
%!assert_equal (ndims (calendarDuration (ones (2), 1, 1)), 2)
%!assert_equal (ndims (calendarDuration (ones (2, 3, 4), 1, 1)), 3)
%!assert_equal (ndims (calendarDuration (ones (1:10), 1, 1)), 10)

%!assert_equal (numel (calendarDuration ([], [], [])), 0)
%!assert_equal (numel (calendarDuration (1, 1, 1)), 1)
%!assert_equal (numel (calendarDuration), 1)
%!assert_equal (numel (calendarDuration (ones (2), 1, 1)), 4)
%!assert_equal (numel (calendarDuration (ones (2, 3, 4), 1, 1)), 24)
%!assert_equal (numel (calendarDuration (ones (1, 10), 1, 1)), 10)

%!assert_equal (nnz (calendarDuration (1, 1, 1)), 1)
%!assert_equal (nnz (calendarDuration (0, 0, 0)), 0)
%!assert_equal (nnz (calendarDuration (0:5, 0, 0)), 5)
%!assert_equal (nnz (calendarDuration ([0, 0, 0, 1, 1], 0, 0)), 2)
%!assert_equal (nnz (calendarDuration (ones (2, 3, 4), 0, 0)), 24)

%!assert_equal (length (calendarDuration (1, 1, 1)), 1)
%!assert_equal (length (calendarDuration (0, 0, 0)), 1)
%!assert_equal (length (calendarDuration (0:5, 0, 0)), 6)
%!assert_equal (length (calendarDuration ([0, 0, 0, 1, 1], 0, 0)), 5)
%!assert_equal (length (calendarDuration (ones (2, 3, 4), 0, 0)), 4)
%!assert_equal (length (calendarDuration (ones (2, 10, 4), 0, 0)), 10)

%!assert_equal (keyHash (calendarDuration), uint64 (9404037721690811995))
%!assert_equal (keyHash (calendarDuration (0, 0, 0)), uint64 (9404037721690811995))
%!assert_equal (keyHash (calendarDuration ([], [], [])), uint64 (10790283023733344549))
%!assert_equal (keyHash (calendarDuration (1, 1, 1)), uint64 (11245130726649642670))
%!test
%! key = keyHash (calendarDuration ([1, 2; 3, 4], 1, 1));
%! assert_equal (key, uint64 (3011565863366139772));
%!test
%! key = keyHash (calendarDuration (0, 0, 0, days ([1, 2, 3])));
%! assert_equal (key, uint64 (6673583750979954156));
%!test
%! base_key = uint64 (2342124352342344234);
%! key = keyHash (calendarDuration (0, 0, 0, days ([1, 2, 3])), base_key);
%! assert_equal (key, uint64 (17128873702567354935));
%!error<calendarDuration.keyHash: BASE must be a UINT64 scalar.> ...
%! keyHash (calendarDuration (0, 0, 0), uint64 ([1, 2]))
%!error<calendarDuration.keyHash: BASE must be a UINT64 scalar.> ...
%! keyHash (calendarDuration (0, 0, 0), 2231107818551636405)

## size with no dimension named pads with ones when more outputs are asked for
## than the array has dimensions, and folds the trailing dimensions into the
## last output when fewer are asked for.  Naming a dimension instead fixes how
## many outputs there may be.
%!assert_equal (size ([calmonths(1), calmonths(2), calmonths(3)]), [1, 3])
%!assert_equal (size (calendarDuration ([], [], [])), [0, 0])
%!test
%! A = calmonths (1);  A(1) = [];
%! assert_equal (size (A), [1, 0]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! assert_equal (size (A), [2, 3]);
%! assert_equal (size (A, 1), 2);
%! assert_equal (size (A, 2), 3);
%! assert_equal (size (A, 3), 1);
%! assert_equal (size (A, 4), 1);
%! assert_equal (size (A, [1, 2]), [2, 3]);
%! assert_equal (size (A, [2, 3]), [3, 1]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! [r, c] = size (A);
%! assert_equal ([r, c], [2, 3]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! [r, c, p] = size (A);
%! assert_equal ([r, c, p], [2, 3, 1]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! [r, c, p, q] = size (A);
%! assert_equal ([r, c, p, q], [2, 3, 1, 1]);
%!test
%! A = reshape (repmat (calmonths (1), 2, 4), 2, 2, 2);
%! assert_equal (size (A), [2, 2, 2]);
%! [r, c] = size (A);
%! assert_equal ([r, c], [2, 4]);
%!test
%! A = reshape (repmat (calmonths (1), 2, 4), 2, 2, 2);
%! [r, c, p] = size (A);
%! assert_equal ([r, c, p], [2, 2, 2]);
%!error<calendarDuration.size: nargout does not match the number of requested dimensions.> ...
%! [r, c] = size (calmonths ([1, 2, 3]), 1)

## ndims, numel and length.  length is the longest dimension, and zero for an
## empty array however it is shaped.
%!assert_equal (ndims ([calmonths(1), calmonths(2)]), 2)
%!assert_equal (ndims (calendarDuration ([], [], [])), 2)
%!assert_equal (ndims (reshape (repmat (calmonths (1), 2, 4), 2, 2, 2)), 3)
%!assert_equal (numel ([calmonths(1), calmonths(2), calmonths(3)]), 3)
%!assert_equal (numel (calmonths (1)), 1)
%!assert_equal (numel (calendarDuration ([], [], [])), 0)
%!assert_equal (numel (reshape (repmat (calmonths (1), 2, 4), 2, 2, 2)), 8)
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! assert_equal (numel (A), 6);
%! assert_equal (length (A), 3);
%!assert_equal (length ([calmonths(1), calmonths(2), calmonths(3)]), 3)
%!assert_equal (length (reshape (repmat (calmonths (1), 2, 4), 2, 2, 2)), 2)
%!assert_equal (length (calendarDuration ([], [], [])), 0)
%!test
%! A = calmonths (1);  A(1) = [];
%! assert_equal (length (A), 0);

## nnz counts elements that are not a zero span; a time-only span counts, and
## so does a missing one, neither being zero.
%!assert_equal (nnz ([calmonths(1), calendarDuration(0, 0, 0), ...
%!                    calendarDuration(0, 0, 2)]), 2)
%!assert_equal (nnz (calendarDuration ([0, 0], [0, 0], [0, 0])), 0)
%!assert_equal (nnz (calendarDuration (0, 0, 0, 5, 0, 0)), 1)
%!assert_equal (nnz (calendarDuration (NaN, 0, 0)), 1)

## keyHash returns a uint64 for the whole array.  Its value is not a contract
## and is never asserted -- MATLAB's varies between sessions -- only that equal
## spans hash alike and unequal ones do not.
%!assert_equal (class (keyHash (calmonths (1))), 'uint64')
%!assert_equal (size (keyHash ([calmonths(1), calmonths(2)])), [1, 1])
%!assert_equal (isequal (keyHash (calmonths (1)), keyHash (calmonths (1))), true)
%!assert_equal (isequal (keyHash (calmonths (1)), keyHash (calmonths (2))), false)

################################################################################
##                          ** Query Operations **                            ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'iscolumn'         'isempty'          'isequal'          'isequaln'        ##
## 'isfinite'         'isinf'            'ismatrix'         'ismissing'       ##
## 'isnan'            'isreal'           'isrow'            'isscalar'        ##
## 'isvector'                                                                 ##
##                                                                            ##
################################################################################

%!assert_equal (iscolumn (calendarDuration (0, 0, 0)), true)
%!assert_equal (iscolumn (calendarDuration ([], [], [])), false)
%!assert_equal (iscolumn (calendarDuration (ones (1, 0), ones (1, 0), ones (1, 0))), false)
%!assert_equal (iscolumn (calendarDuration (ones (0, 1), ones (0, 1), ones (0, 1))), true)
%!assert_equal (iscolumn (calendarDuration ([1, 1], 1, 1)), false)
%!assert_equal (iscolumn (calendarDuration ([1; 1], 1, 1)), true)
%!assert_equal (iscolumn (calendarDuration (ones (2), 1, 1)), false)
%!assert_equal (iscolumn (calendarDuration (1, 1, zeros (2, 3, 4))), false)

%!assert_equal (isempty (calendarDuration (0, 0, 0)), false)
%!assert_equal (isempty (calendarDuration ([], [], [])), true)
%!assert_equal (isempty (calendarDuration (ones (1, 0), ones (1, 0), ones (1, 0))), true)
%!assert_equal (isempty (calendarDuration (ones (0, 1), ones (0, 1), ones (0, 1))), true)
%!test
%! x = ones ([2, 3, 0, 2]);
%! assert_equal (isempty (calendarDuration (x, x, x)), true);
%! assert_equal (isempty (calendarDuration (x, x, x, x, x, x)), true)
%! assert_equal (isempty (calendarDuration (x, x, x, hours (x))), true);
%!test
%! x = ones ([2, 3, 1, 2]);
%! assert_equal (isempty (calendarDuration (x, x, x)), false);
%! assert_equal (isempty (calendarDuration (x, x, x, x, x, x)), false)
%! assert_equal (isempty (calendarDuration (x, x, x, hours (x))), false);

%!assert_equal (isequal (caldays (5), caldays (5)), true)
%!assert_equal (isequal (caldays (NaN), caldays (NaN)), false)
%!assert_equal (isequal (caldays (5), caldays (6)), false)
%!assert_equal (isequal (caldays ([5, NaN]), caldays ([5, NaN])), false)
%!assert_equal (isequal (caldays (5), caldays (5), caldays (5)), true)
%!assert_equal (isequal (caldays (5), caldays (6), calweeks (1)), false)
%!assert_equal (isequal (caldays (7), caldays (7), calweeks (1)), true)
%!assert_equal (isequal (caldays ([1:3]), caldays ([1, 2, 3])), true)
%!assert_equal (isequal (caldays ([1:3]), caldays ([1, NaN, 3])), false)
%!assert_equal (isequal (calmonths (5), calmonths (5)), true)
%!assert_equal (isequal (calmonths (5), calmonths (6)), false)
%!assert_equal (isequal (calmonths (NaN), calmonths (NaN)), false)
%!assert_equal (isequal (calmonths (5), calmonths (5), calmonths (5)), true)
%!assert_equal (isequal (calmonths (5), calmonths (6), calquarters (2)), false)
%!assert_equal (isequal (calmonths (6), calmonths (6), calquarters (2)), true)
%!assert_equal (isequal (calmonths ([1:3]), calmonths ([1, 2, 3])), true)
%!assert_equal (isequal (calmonths ([1:3]), calmonths ([1, NaN, 3])), false)
%!assert_equal (isequal (calmonths ([1, NaN, 3]), calmonths ([1, NaN, 3])), false)
%!assert_equal (isequal (calendarDuration (0, 0, 0, hours (ones (3))), ...
%!                 calendarDuration (0, 0, 0, hours (ones (3)))), true)
%!assert_equal (isequal (calendarDuration (0, 0, 0, hours (nan (3))), ...
%!                 calendarDuration (0, 0, 0, hours (nan (3)))), false)
%!assert_equal (isequal (calendarDuration (0, 0, 0, hours (ones (3, 3, 2))), ...
%!                 calendarDuration (0, 0, 0, hours (ones (3, 3, 3)))), false)
%!error<calendarDuration.isequal: too few input arguments.> isequal (caldays (3))
%!assert_equal (isequal (caldays (3), 1), false)
%!assert_equal (isequal (caldays (3), caldays (3), hours (1)), false)
%!assert_equal (isequal (caldays (3), caldays (3), days (1)), false)

%!assert_equal (isequaln (caldays (5), caldays (5)), true)
%!assert_equal (isequaln (caldays (NaN), caldays (NaN)), true)
%!assert_equal (isequaln (caldays (5), caldays (6)), false)
%!assert_equal (isequaln (caldays ([5, NaN]), caldays ([5, NaN])), true)
%!assert_equal (isequaln (caldays (5), caldays (5), caldays (5)), true)
%!assert_equal (isequaln (caldays (5), caldays (6), calweeks (1)), false)
%!assert_equal (isequaln (caldays (7), caldays (7), calweeks (1)), true)
%!assert_equal (isequaln (caldays ([1:3]), caldays ([1, 2, 3])), true)
%!assert_equal (isequaln (caldays ([1:3]), caldays ([1, NaN, 3])), false)
%!assert_equal (isequaln (calmonths (5), calmonths (5)), true)
%!assert_equal (isequaln (calmonths (NaN), calmonths (NaN)), true)
%!assert_equal (isequaln (calmonths (5), calmonths (6)), false)
%!assert_equal (isequaln (calmonths (5), calmonths (5), calmonths (5)), true)
%!assert_equal (isequaln (calmonths (5), calmonths (6), calquarters (2)), false)
%!assert_equal (isequaln (calmonths (6), calmonths (6), calquarters (2)), true)
%!assert_equal (isequaln (calmonths ([1:3]), calmonths ([1, 2, 3])), true)
%!assert_equal (isequaln (calmonths ([1:3]), calmonths ([1, NaN, 3])), false)
%!assert_equal (isequaln (calmonths ([1, NaN, 3]), calmonths ([1, NaN, 3])), true)
%!assert_equal (isequaln (calendarDuration (0, 0, 0, hours (ones (3))), ...
%!                 calendarDuration (0, 0, 0, hours (ones (3)))), true)
%!assert_equal (isequaln (calendarDuration (0, 0, 0, hours (nan (3))), ...
%!                 calendarDuration (0, 0, 0, hours (nan (3)))), true)
%!assert_equal (isequaln (calendarDuration (0, 0, 0, hours (ones (3, 3, 2))), ...
%!                 calendarDuration (0, 0, 0, hours (ones (3, 3, 3)))), false)
%!assert_equal (isequaln (calendarDuration (0, NaN, 0, hours (ones (3))), ...
%!                 calendarDuration (0, NaN, 0, hours (ones (3)))), true)
%!assert_equal (isequaln (calendarDuration (0, 0, NaN, hours (nan (3, 3, 3))), ...
%!                 calendarDuration (0, 0, NaN, hours (nan (3, 3, 3)))), true)
%!error<calendarDuration.isequaln: too few input arguments.> isequaln (caldays (3))
%!assert_equal (isequaln (caldays (3), 1), false)
%!assert_equal (isequaln (caldays (3), caldays (3), hours (1)), false)
%!assert_equal (isequaln (caldays (3), caldays (3), days (1)), false)

%!assert_equal (isfinite (caldays (1)), true)
%!assert_equal (isfinite (caldays (NaN)), false)
%!assert_equal (isfinite (caldays (Inf)), false)
%!assert_equal (isfinite (caldays (-Inf)), false)
%!assert_equal (isfinite (calendarDuration ([1, 0, 1])), true)
%!assert_equal (isfinite (calendarDuration (nan (2), 1, 0)), false (2))
%!assert_equal (isfinite (calendarDuration (Inf (3), 0, 0)), false (3))
%!assert_equal (isfinite (calendarDuration (1, -Inf, [0; 0])), false (2, 1))
%!assert_equal (size (isfinite (caldays ([]))), [0, 0])
%!assert_equal (size (isfinite (caldays (ones (1, 2, 0, 2)))), [1, 2, 0, 2])

%!assert_equal (isinf (caldays (1)), false)
%!assert_equal (isinf (caldays (NaN)), false)
%!assert_equal (isinf (caldays (Inf)), true)
%!assert_equal (isinf (caldays (-Inf)), true)
%!assert_equal (isinf (calendarDuration ([1, 0, NaN])), false)
%!assert_equal (isinf (calendarDuration ([1, Inf, -Inf])), false)
%!assert_equal (isinf (calendarDuration (nan (2), 1, 0)), false (2))
%!assert_equal (isinf (calendarDuration (Inf (3), 0, 0)), true (3))
%!assert_equal (isinf (calendarDuration (1, -Inf, [0; 0])), true (2, 1))
%!assert_equal (size (isinf (caldays ([]))), [0, 0])
%!assert_equal (size (isinf (caldays (Inf (1, 2, 0, 2)))), [1, 2, 0, 2])

%!assert_equal (ismatrix (caldays (3)), true)
%!assert_equal (ismatrix (caldays ([3, 3; 2, 1])), true)
%!assert_equal (ismatrix (caldays ([3, 3, 2, 1])), true)
%!assert_equal (ismatrix (caldays (ones (3, 3, 2))), false)
%!assert_equal (ismatrix (calmonths (3)), true)
%!assert_equal (ismatrix (calmonths ([3, 3; 2, 1])), true)
%!assert_equal (ismatrix (calmonths ([3, 3, 2, 1])), true)
%!assert_equal (ismatrix (calmonths (ones (3, 3, 2))), false)
%!assert_equal (ismatrix (calendarDuration (0, 0, 3)), true)
%!assert_equal (ismatrix (calendarDuration ([3, 3; 2, 1], 0, 0)), true)
%!assert_equal (ismatrix (calendarDuration ([3, 3, 2, 1], 0, 1)), true)
%!assert_equal (ismatrix (calendarDuration (ones (3, 3, 2), 0, 0)), false)
%!assert_equal (ismatrix (calendarDuration (0, 0, 1, hours (ones (3, 3)))), true)
%!assert_equal (ismatrix (calendarDuration (0, 0, 1, hours (ones (3, 3, 2)))), false)

%!assert_equal (ismissing (caldays (1)), false)
%!assert_equal (ismissing (caldays (NaN)), true)
%!assert_equal (ismissing (caldays (Inf)), false)
%!assert_equal (ismissing (caldays (-Inf)), false)
%!assert_equal (ismissing (calendarDuration ([1, 0, NaN])), true)
%!assert_equal (ismissing (calendarDuration ([1, Inf, -Inf])), true)
%!assert_equal (ismissing (calendarDuration (nan (2), 1, 0)), true (2))
%!assert_equal (ismissing (calendarDuration (Inf (3), 0, 0)), false (3))
%!assert_equal (ismissing (calendarDuration (1, -Inf, [0; NaN])), [false; true])
%!assert_equal (size (ismissing (caldays ([]))), [0, 0])
%!assert_equal (ismissing (caldays (nan (1, 2, 0, 2))), true (1, 2, 0, 2))
%!assert_equal (size (ismissing (caldays (nan (1, 2, 0, 2)))), [1, 2, 0, 2])
%!error<calendarDuration.ismissing: Indicators are not supported.> ...
%! ismissing (caldays (3), 3)

%!assert_equal (isnan (caldays (1)), false)
%!assert_equal (isnan (caldays (NaN)), true)
%!assert_equal (isnan (caldays (Inf)), false)
%!assert_equal (isnan (caldays (-Inf)), false)
%!assert_equal (isnan (calendarDuration ([1, 0, NaN])), true)
%!assert_equal (isnan (calendarDuration ([1, Inf, -Inf])), true)
%!assert_equal (isnan (calendarDuration (nan (2), 1, 0)), true (2))
%!assert_equal (isnan (calendarDuration (Inf (3), 0, 0)), false (3))
%!assert_equal (isnan (calendarDuration (1, -Inf, [0; NaN])), [false; true])
%!assert_equal (size (isnan (caldays ([]))), [0, 0])
%!assert_equal (isnan (caldays (nan (1, 2, 0, 2))), true (1, 2, 0, 2))
%!assert_equal (size (isnan (caldays (nan (1, 2, 0, 2)))), [1, 2, 0, 2])

%!assert_equal (isscalar (isreal (caldays (ones (2, 2, 2)))), true)
%!assert_equal (isreal (calendarDuration ([1, 1, 1])), true)

%!assert_equal (isrow (calendarDuration (0, 0, 0)), true)
%!assert_equal (isrow (calendarDuration ([], [], [])), false)
%!assert_equal (isrow (calendarDuration (ones (1, 0), ones (1, 0), ones (1, 0))), true)
%!assert_equal (isrow (calendarDuration (ones (0, 1), ones (0, 1), ones (0, 1))), false)
%!assert_equal (isrow (calmonths (1)), true)
%!assert_equal (isrow (calmonths ([1, 2, 3])), true)
%!assert_equal (isrow (calyears ([1; 2; 3])), false)
%!assert_equal (isrow (calendarDuration (1, 1, 1, 1, 1, 1)), true)
%!assert_equal (isrow (calendarDuration (1, 1, 1, 1, 1, [1, 2, 3])), true)
%!assert_equal (isrow (calendarDuration (1, 1, 1, 1, 1, ones (2))), false)
%!assert_equal (isrow (calendarDuration (1, 1, ones (1, 1, 2))), false)
%!assert_equal (isrow (calendarDuration (1, 1, ones (1, 2, 1))), true)

%!assert_equal (isscalar (calmonths (1)), true)
%!assert_equal (isscalar (calmonths ([1, 2, 3])), false)
%!assert_equal (isscalar (calyears ([1; 2; 3])), false)
%!assert_equal (isscalar (calendarDuration (0, 0, 0)), true)
%!assert_equal (isscalar (calendarDuration ([], [], [])), false)
%!assert_equal (isscalar (calendarDuration (1, 1, 1, 1, 1, 1)), true)
%!assert_equal (isscalar (calendarDuration (1, 1, 1, 1, 1, [1, 2, 3])), false)
%!assert_equal (isscalar (calendarDuration (1, 1, 1, 1, 1, ones (2))), false)
%!assert_equal (isscalar (calendarDuration (1, 1, ones (1, 1, 2))), false)
%!assert_equal (isscalar (calendarDuration (1, 1, ones (1, 2, 1))), false)
%!assert_equal (isscalar (calendarDuration (1, 1, ones (1, 1, 1))), true)
%!assert_equal (isscalar (calendarDuration (1, 1, 1, hours (5))), true)

%!assert_equal (isvector (calendarDuration (0, 0, 0)), true)
%!assert_equal (isvector (calendarDuration ([], [], [])), false)
%!assert_equal (isvector (calendarDuration (ones (1, 0), ones (1, 0), ones (1, 0))), true)
%!assert_equal (isvector (calendarDuration (ones (0, 1), ones (0, 1), ones (0, 1))), true)
%!assert_equal (isvector (calmonths (1)), true)
%!assert_equal (isvector (calmonths ([1, 2, 3])), true)
%!assert_equal (isvector (calyears ([1; 2; 3])), true)
%!assert_equal (isvector (calendarDuration (1, 1, 1, 1, 1, 1)), true)
%!assert_equal (isvector (calendarDuration (1, 1, 1, 1, 1, [1, 2, 3])), true)
%!assert_equal (isvector (calendarDuration (1, 1, 1, 1, 1, ones (2))), false)
%!assert_equal (isvector (calendarDuration (1, 1, ones (1, 1, 2))), false)
%!assert_equal (isvector (calendarDuration (1, 1, ones (2, 1, 2))), false)
%!assert_equal (isvector (calendarDuration (1, 1, ones (1, 2, 1))), true)
%!assert_equal (isvector (calendarDuration (1, 1, ones (2, 1, 1))), true)

################################################################################
##                       ** Mathematical Operations **                        ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'minus'            'uminus'           'plus'             'uplus'           ##
## 'times'            'mtimes'                                                ##
##                                                                            ##
################################################################################

%!test
%! A = caldays ([1, 2, 3]);
%! B = caldays (1);
%! assert_equal (caldays (A - B), [0, 1, 2]);
%! assert_equal (caldays (B - A), [0, -1, -2]);
%!test
%! A = calweeks (1);
%! B = days ([2, 2]);
%! [c1, c2] = split (A - B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [-2, -2]);
%!test
%! A = calweeks (1);
%! B = [2, 2];
%! [c1, c2] = split (A - B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [-2, -2]);
%!test
%! A = days ([2, 2]);
%! B = calweeks (1);
%! [c1, c2] = split (A - B, {'days', 'time'});
%! assert_equal (c1, [-7, -7]);
%! assert_equal (days (c2), [2, 2]);
%!test
%! A = [2, 2];
%! B = calweeks (1);
%! [c1, c2] = split (A - B, {'days', 'time'});
%! assert_equal (c1, [-7, -7]);
%! assert_equal (days (c2), [2, 2]);
%!error <calendarDuration: subtraction is not defined between 'calendarDuration' and 'char' arrays.> ...
%! calweeks (1) - 'a'
%!error <calendarDuration: subtraction is not defined between 'calendarDuration' and 'cell' arrays.> ...
%! calweeks (1) - {1}
%!error <calendarDuration: subtraction is not defined between 'calendarDuration' and 'struct' arrays.> ...
%! calweeks (1) - struct ('a', 1)

%!test
%! A = caldays ([1, 2, 3]);
%! assert_equal (caldays (-A), [-1, -2, -3]);
%!test
%! A = randi (10, 3, 4, 5);
%! B = caldays (A);
%! C = - caldays (A);
%! assert_equal (caldays (B + C), zeros (3, 4, 5));

%!test
%! A = caldays ([1, 2, 3]);
%! B = caldays (1);
%! assert_equal (caldays (A + B), [2, 3, 4]);
%! assert_equal (caldays (B + A), [2, 3, 4]);
%!test
%! A = calweeks (1);
%! B = days ([2, 2]);
%! [c1, c2] = split (A + B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [2, 2]);
%!test
%! A = calweeks (1);
%! B = [2, 2];
%! [c1, c2] = split (A + B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [2, 2]);
%!test
%! A = days ([2, 2]);
%! B = calweeks (1);
%! [c1, c2] = split (A + B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [2, 2]);
%!test
%! A = [2, 2];
%! B = calweeks (1);
%! [c1, c2] = split (A + B, {'days', 'time'});
%! assert_equal (c1, [7, 7]);
%! assert_equal (days (c2), [2, 2]);
%!error <calendarDuration: addition is not defined between 'calendarDuration' and 'char' arrays.> ...
%! calweeks (1) + 'a'
%!error <calendarDuration: addition is not defined between 'calendarDuration' and 'cell' arrays.> ...
%! calweeks (1) + {1}
%!error <calendarDuration: addition is not defined between 'calendarDuration' and 'struct' arrays.> ...
%! calweeks (1) + struct ('a', 1)

%!test
%! A = calendarDuration ([1, 1, 5, 4, 30, 2]);
%! B = A .* [1, 3];
%! [y, m, d, t] = split (B, {'years', 'months', 'days', 'time'});
%! assert_equal (y, [1, 3]);
%! assert_equal (m, [1, 3]);
%! assert_equal (d, [5, 15]);
%! assert_equal (seconds (t), [16202, 48606], 4e-12);
%!test
%! A = calendarDuration ([1, 1, 5, 4, 30, 2]);
%! B = A .* [1; 3];
%! [y, m, d, t] = split (B, {'years', 'months', 'days', 'time'});
%! assert_equal (y, [1; 3]);
%! assert_equal (m, [1; 3]);
%! assert_equal (d, [5; 15]);
%! assert_equal (seconds (t), [16202; 48606], 4e-12);
%!test
%! A = calendarDuration ([1, 1, 5, 4, 30, 2]);
%! B = [1; 3] .* A;
%! [y, m, d, t] = split (B, {'years', 'months', 'days', 'time'});
%! assert_equal (y, [1; 3]);
%! assert_equal (m, [1; 3]);
%! assert_equal (d, [5; 15]);
%! assert_equal (seconds (t), [16202; 48606], 4e-12);
%!test
%! A = calendarDuration ([1, 1, 5, 4, 30, 2]);
%! B = 2 .* A;
%! [y, m, d, t] = split (B, {'years', 'months', 'days', 'time'});
%! assert_equal (y, 2);
%! assert_equal (m, 2);
%! assert_equal (d, 10);
%! assert_equal (seconds (t), 32404, 4e-12);
%!error <calendarDuration: multiplication is not defined between 'calendarDuration' and 'char' arrays.> ...
%! calweeks (1) .* 'a'
%!error <calendarDuration: multiplication is not defined between 'calendarDuration' and 'cell' arrays.> ...
%! calweeks (1) .* {1}
%!error <calendarDuration: multiplication is not defined between 'calendarDuration' and 'struct' arrays.> ...
%! calweeks (1) .* struct ('a', 1)

%!test
%! A = calendarDuration ([1, 1, 5, 4, 30, 2]);
%! B = 2 * A;
%! [y, m, d, t] = split (B, {'years', 'months', 'days', 'time'});
%! assert_equal (y, 2);
%! assert_equal (m, 2);
%! assert_equal (d, 10);
%! assert_equal (seconds (t), 32404, 4e-12);
%!test
%! A = calendarDuration ([1, 2], 0, [4, 5]);
%! B = A * [1; 2];
%! [y, d] = split (B, {'years', 'days'});
%! assert_equal (y, [1, 2] * [1; 2]);
%! assert_equal (d, [4, 5] * [1; 2]);
%!test
%! A = calendarDuration ([1, 2], 0, [4, 5]);
%! B = A * [4; 2];
%! [y, d] = split (B, {'years', 'days'});
%! assert_equal (y, [1, 2] * [4; 2]);
%! assert_equal (d, [4, 5] * [4; 2]);
%!test
%! A = calendarDuration ([1, 2], 0, [4, 5]);
%! B = [4; 2] * A;
%! [y, d] = split (B, {'years', 'days'});
%! assert_equal (y, [4; 2] * [1, 2]);
%! assert_equal (d, [4; 2] * [4, 5]);
%!error <calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'char' arrays.> ...
%! calweeks (1) * 'a'
%!error <calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'cell' arrays.> ...
%! calweeks (1) * {1}
%!error <calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'struct' arrays.> ...
%! calweeks (1) * struct ('a', 1)

## A zero component stays a positive zero through negation and through scaling
## by a negative number.  1/(+0) is Inf and 1/(-0) is -Inf, which is the only
## way an equality test can see the sign of a zero.
%!assert_equal (1 / seconds (time (-calendarDuration (1, 2, 3))), Inf)
%!assert_equal (1 / calmonths (-calendarDuration (0, 0, 3, 4, 5, 6.5)), Inf)
%!assert_equal (1 / caldays (-calendarDuration (0, 0, 0, 4, 5, 6.5)), Inf)
%!assert_equal (1 / seconds (time (calendarDuration (1, 2, 3) * -1)), Inf)
%!assert_equal (1 / seconds (time (-1 * calendarDuration (1, 2, 3))), Inf)
%!assert_equal (1 / seconds (time (calendarDuration (1, 2, 3) .* -1)), Inf)
%!assert_equal (1 / calmonths (calendarDuration (0, 0, 3) .* -1), Inf)

## Addition and subtraction are component-wise: months to months, days to days,
## time to time.  No component ever rolls into another -- a time part beyond 24
## hours stays in the time part, and days never become months.
%!test
%! C = calendarDuration (1, 2, 3) + calendarDuration (0, 1, 2);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal (mo, 15);
%! assert_equal (d, 5);
%! assert_equal (seconds (t), 0);
%!test
%! C = calendarDuration (1, 2, 3) - calendarDuration (0, 1, 2);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal (mo, 13);
%! assert_equal (d, 1);
%! assert_equal (seconds (t), 0);
%!test
%! C = calendarDuration (0, 2, -14) + calendarDuration (0, -1, 20);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal (mo, 1);
%! assert_equal (d, 6);
%!test
%! C = calendarDuration (0, 1, 2) - calendarDuration (1, 2, 3);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal (mo, -13);
%! assert_equal (d, -1);
%!test
%! C = calendarDuration (0, 0, 1, 5, 0, 0) + calendarDuration (0, 0, 1, 20, 0, 0);
%! [d, t] = split (C, {'days', 'time'});
%! assert_equal (d, 2);
%! assert_equal (seconds (t), 90000);
%! assert_equal (char (C), '2d 25h 0m 0s');
%!test
%! C = calendarDuration (0, 0, 0, 13, 0, 0) + calendarDuration (0, 0, 0, 13, 0, 0);
%! assert_equal (seconds (split (C, 'time')), 93600);
%! assert_equal (char (C), '26h 0m 0s');
%!assert_equal (char (calendarDuration (0, 0, 0, 25, 0, 0)), '25h 0m 0s')
%!assert_equal (char (caldays (1) + hours (25)), '1d 25h 0m 0s')
%!assert_equal (char (calendarDuration (0, 0, 1, -25, 0, 0)), '1d -25h 0m 0s')
%!assert_equal (char (calendarDuration (0, 0, 0, 0, 90, 0)), '1h 30m 0s')
%!assert_equal (char (calendarDuration (0, 0, 40)), '40d')
%!assert_equal (char (calendarDuration (0, 13, 0)), '1y 1mo')

## A duration addend goes to the time component whatever unit it was written
## in, and so does a bare numeric, which counts days.
%!test
%! C = calendarDuration (0, 1, 2) + hours (5);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d], [1, 2]);
%! assert_equal (seconds (t), 18000);
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) + hours (25), 'time')), 90000)
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) + days (3), 'time')), 259200)
%!assert_equal (seconds (split (hours (5) + calendarDuration (0, 1, 2), 'time')), 18000)
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) - hours (5), 'time')), -18000)
%!test
%! C = hours (5) - calendarDuration (0, 1, 2);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d], [-1, -2]);
%! assert_equal (seconds (t), 18000);
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) + 3, 'time')), 259200)
%!assert_equal (seconds (split (3 + calendarDuration (0, 1, 2), 'time')), 259200)
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) - 3, 'time')), -259200)
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) + (-3), 'time')), -259200)
%!assert_equal (seconds (split (calendarDuration (0, 1, 2) + 1.5, 'time')), 129600)
%!test
%! C = 3 - calendarDuration (0, 1, 2);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d], [-1, -2]);
%! assert_equal (seconds (t), 259200);
%!test
%! C = calendarDuration (0, 1, 2) + [1, 2];
%! assert_equal (size (C), [1, 2]);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal (mo, [1, 1]);
%! assert_equal (d, [2, 2]);
%! assert_equal (seconds (t), [86400, 172800]);
%!test
%! C = calendarDuration (0, [1, 2], 0) + calendarDuration (0, 0, [10; 20]);
%! assert_equal (size (C), [2, 2]);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal (mo, [1, 2; 1, 2]);
%! assert_equal (d, [10, 10; 20, 20]);
%!assert_equal (size (calendarDuration (0, 1, 2) + calendarDuration ([], [], [])), [0, 0])

## A logical operand counts as one, and no other numeric type is accepted.
%!assert_equal (seconds (split (calendarDuration (1, 2, 3) + true, 'time')), 86400)
%!error<calendarDuration: addition is not defined between 'calendarDuration' and 'single' arrays.> ...
%! calendarDuration (1, 2, 3) + single (2)
%!error<calendarDuration: addition is not defined between 'calendarDuration' and 'int8' arrays.> ...
%! calendarDuration (1, 2, 3) + int8 (2)
%!error<calendarDuration: addition is not defined between 'calendarDuration' and 'char' arrays.> ...
%! calendarDuration (1, 2, 3) + 'a'
%!error<calendarDuration: addition is not defined between 'calendarDuration' and 'cell' arrays.> ...
%! calendarDuration (1, 2, 3) + {1}

## A non-finite operand takes over every component of its element.
%!test
%! C = calendarDuration (0, 1, 2) + NaN;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%! assert_equal (char (C), 'NaN');
%!test
%! C = calendarDuration (0, 1, 2) + Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%! assert_equal (char (C), 'Inf');
%!test
%! C = calendarDuration (0, 1, 2) + calendarDuration (0, NaN, 0);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);

## Unary minus negates every component; unary plus copies.
%!test
%! C = -calendarDuration (1, 2, 3);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal ([mo, d], [-14, -3]);
%! assert_equal (char (C), '-1y -2mo -3d');
%!test
%! C = +calendarDuration (1, 2, 3);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal ([mo, d], [14, 3]);
%!test
%! C = -calendarDuration (0, 2, -14);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal ([mo, d], [-2, 14]);
%! assert_equal (char (C), '-2mo 14d');
%!assert_equal (char (-calendarDuration (0, 0, 0)), '0d')

## Scaling multiplies every component, and only by a whole number.
%!test
%! C = calendarDuration (1, 2, 3) * 2;
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal ([mo, d], [28, 6]);
%! assert_equal (char (C), '2y 4mo 6d');
%!test
%! C = 2 * calendarDuration (1, 2, 3);
%! assert_equal (split (C, 'months'), 28);
%!test
%! C = calendarDuration (1, 2, 3) * (-2);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal ([mo, d], [-28, -6]);
%!assert_equal (char (calendarDuration (1, 2, 3) * 0), '0d')
%!assert_equal (char (calendarDuration (1, 2, 3) * 2.0), '2y 4mo 6d')
%!assert_equal (char (calendarDuration (1, 2, 3) * true), '1y 2mo 3d')
%!test
%! C = calendarDuration (1, 2, 3) .* [1, 2, 3];
%! assert_equal (size (C), [1, 3]);
%! [mo, d] = split (C, {'months', 'days'});
%! assert_equal (mo, [14, 28, 42]);
%! assert_equal (d, [3, 6, 9]);
%!test
%! C = [1, 2, 3] .* calendarDuration (1, 2, 3);
%! assert_equal (split (C, 'months'), [14, 28, 42]);
%!test
%! C = calendarDuration (1, 2, 3) * [1; 2];
%! assert_equal (size (C), [2, 1]);
%! assert_equal (split (C, 'months'), [14; 28]);
%!assert_equal (split (calendarDuration (0, [1, 2], 0) * 2, 'months'), [2, 4])
%!assert_equal (size (calendarDuration (1, 2, 3) * []), [0, 0])
%!error<calendarDuration: matrix multiplication by non-integer values is not defined.> ...
%! calendarDuration (1, 2, 3) * 0.5
%!error<calendarDuration: matrix multiplication by non-integer values is not defined.> ...
%! calendarDuration (0, 1, 0) * 1.5
%!error<calendarDuration: matrix multiplication by non-integer values is not defined.> ...
%! calendarDuration (0, 0, 0, 1, 0, 0) * 0.5
%!error<calendarDuration: multiplication by non-integer values is not defined.> ...
%! calendarDuration (1, 2, 3) .* [1, 0.5]
%!error<calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'single' arrays.> ...
%! calendarDuration (1, 2, 3) * single (2)
%!error<calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'uint8' arrays.> ...
%! calendarDuration (1, 2, 3) * uint8 (2)
%!error<calendarDuration: multiplication is not defined between 'calendarDuration' and 'single' arrays.> ...
%! calendarDuration (1, 2, 3) .* single (2)
%!error<calendarDuration: matrix multiplication is not defined between 'calendarDuration' and 'calendarDuration' arrays.> ...
%! calendarDuration (1, 2, 3) * calendarDuration (1, 2, 3)

## A non-finite factor scales the span as a whole, by its sign: a zero
## component does not poison the result, but a span whose components disagree
## in sign, or that is entirely zero, has no sign and gives NaN.
%!test
%! C = calendarDuration (1, 2, 3) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%!test
%! C = calendarDuration (1, 2, 3) * (-Inf);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [-Inf, -Inf, -Inf]);
%!test
%! C = calendarDuration (-1, -2, -3) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [-Inf, -Inf, -Inf]);
%!test
%! C = calendarDuration (-1, -2, -3) * (-Inf);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%!test
%! C = calendarDuration (0, 0, 3) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%!test
%! C = calendarDuration (0, 0, 0, 5, 0, 0) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%!test
%! C = calendarDuration (0, 0, 0, -5, 0, 0) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [-Inf, -Inf, -Inf]);
%!test
%! C = calendarDuration (0, -2, 14) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%!test
%! C = calendarDuration (0, 2, -14) * (-Inf);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%!test
%! C = calendarDuration (0, 0, 0) * Inf;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%!test
%! C = calendarDuration (1, 2, 3) * NaN;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%!test
%! C = calendarDuration (0, Inf, 0) * 2;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [Inf, Inf, Inf]);
%!test
%! C = calendarDuration (0, Inf, 0) * (-2);
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [-Inf, -Inf, -Inf]);
%!test
%! C = calendarDuration (0, Inf, 0) * 0;
%! [mo, d, t] = split (C, {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [NaN, NaN, NaN]);
%!test
%! C = calendarDuration (0, [1, -2], [3, -4]) * Inf;
%! assert_equal (split (C, 'months'), [Inf, -Inf]);
%!test
%! C = calendarDuration (1, 2, 3) .* [Inf, -Inf];
%! assert_equal (split (C, 'months'), [Inf, -Inf]);
%!test
%! C = calendarDuration (1, 2, 3) .* [1, NaN];
%! assert_equal (split (C, 'months'), [14, NaN]);

## Adding or subtracting two spans displays either one's units: the format is
## the character union in 'yqmwdt' order, the same rule concatenation follows.
## A duration or numeric addend names no units, so the span keeps its own, and
## scaling and negation keep it too.
%!assert_equal ((calquarters (1) + calweeks (1)).Format, 'yqmwdt')
%!assert_equal ((calquarters (1) - calweeks (1)).Format, 'yqmwdt')
%!assert_equal ((calweeks (1) + calquarters (1)).Format, 'yqmwdt')
%!assert_equal ((calweeks (1) + calmonths (1)).Format, 'ymwdt')
%!assert_equal ((calquarters (1) + calquarters (2)).Format, 'qmdt')
%!assert_equal ((calweeks (1) + calendarDuration (0, 0, 0)).Format, 'ymwdt')
%!assert_equal ((calweeks (1) + hours (5)).Format, 'ymwdt')
%!assert_equal ((calweeks (1) + 5).Format, 'ymwdt')
%!assert_equal ((calquarters (1) * 2).Format, 'qmdt')
%!assert_equal ((-calweeks (1)).Format, 'ymwdt')
%!test
%! C = calquarters (1) + calweeks (1);
%! assert_equal (calmonths (C), 3);
%! assert_equal (split (C, 'days'), 7);
%!test
%! C = calquarters (1) - calweeks (1);
%! assert_equal (calmonths (C), 3);
%! assert_equal (split (C, 'days'), -7);

################################################################################
##                    ** Equality and Filter Operations **                    ##
################################################################################
##                             Available Methods                              ##
##                                                                            ##
## 'eq'               'ne'               'unique'                             ##
##                                                                            ##
################################################################################

%!assert_equal (caldays (3) == caldays (3), true)
%!assert_equal (caldays (3) == caldays (4), false)
%!assert_equal (caldays (14) == calweeks (2), true)
%!assert_equal (calmonths (15) == calquarters (5), true)
%!assert_equal (calmonths ([15, 12, NaN]) == calquarters ([5, 4, 3]), [true, true, false])
%!assert_equal (calyears (ones (2)) == calmonths ([12, 15]), logical ([1, 0; 1, 0]))
%!assert_equal (calyears (ones (2)) == calmonths ([12; 15]), logical ([1, 1; 0, 0]))
%!assert_equal (calmonths ([3, 4]) == calmonths ([3, 4]), [true, true])
%!assert_equal (calmonths ([3, NaN]) == calmonths ([3, 4]), [true, false])
%!assert_equal (calmonths ([3; NaN]) == calmonths ([3; NaN]), [true; false])
%!assert_equal (calendarDuration ([1; 2], 0, 0) == calyears ([1, 2]), logical (eye (2)))
%!error<calendarDuration.eq: equality is not defined between 'calendarDuration' and 'double' arrays.> ...
%! caldays (3) == NaN
%!error<calendarDuration.eq: equality is not defined between 'calendarDuration' and 'duration' arrays.> ...
%! calendarDuration (0, 0, 0, hours (5)) == hours (5)
%!error<calendarDuration.eq: equality is not defined between 'double' and 'calendarDuration' arrays.> ...
%! 5 == calyears (5)
%!error<calendarDuration.eq: equality is not defined between 'char' and 'calendarDuration' arrays.> ...
%! '1y' == calyears(1)

%!assert_equal (caldays (3) != caldays (3), false)
%!assert_equal (caldays (3) != caldays (4), true)
%!assert_equal (caldays (14) != calweeks (2), false)
%!assert_equal (calmonths (15) != calquarters (5), false)
%!assert_equal (calmonths ([15, 12, NaN]) != calquarters ([5, 4, 3]), [false, false, true])
%!assert_equal (calyears (ones (2)) != calmonths ([12, 15]), logical ([0, 1; 0, 1]))
%!assert_equal (calyears (ones (2)) != calmonths ([12; 15]), logical ([0, 0; 1, 1]))
%!assert_equal (calmonths ([3, 4]) != calmonths ([3, 4]), [false, false])
%!assert_equal (calmonths ([3, NaN]) != calmonths ([3, 4]), [false, true])
%!assert_equal (calmonths ([3; NaN]) != calmonths ([3; NaN]), [false; true])
%!assert_equal (calendarDuration ([1; 2], 0, 0) != calyears ([2, 1]), logical (eye (2)))
%!error<calendarDuration.ne: inequality is not defined between 'calendarDuration' and 'double' arrays.> ...
%! caldays (3) != NaN
%!error<calendarDuration.ne: inequality is not defined between 'calendarDuration' and 'duration' arrays.> ...
%! calendarDuration (0, 0, 0, hours (5)) != hours (5)
%!error<calendarDuration.ne: inequality is not defined between 'double' and 'calendarDuration' arrays.> ...
%! 5 != calyears (5)
%!error<calendarDuration.ne: inequality is not defined between 'char' and 'calendarDuration' arrays.> ...
%! '1y' != calyears(1)

%!test
%! U = unique (caldays ([1, 2, 2, 3]));
%! assert_equal (caldays (U), [1, 2, 3]);
%!test
%! U = unique (caldays ([1, 2; 2, 3; 1, 2]), 'rows');
%! assert_equal (caldays (U), [1, 2; 2, 3]);
%!test
%! U = unique (caldays ([1, 3, 2, 2]));
%! assert_equal (caldays (U), [1, 2, 3]);
%!test
%! U = unique (caldays ([1, 3, 2, 2]), 'sorted');
%! assert_equal (caldays (U), [1, 2, 3]);
%!test
%! U = unique (caldays ([1, 3, 2, 2]), 'stable');
%! assert_equal (caldays (U), [1, 3, 2]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'first');
%! assert_equal (caldays (U), [1, 2, 3]);
%! assert_equal (idx, [1; 3; 2]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'last');
%! assert_equal (caldays (U), [1, 2, 3]);
%! assert_equal (idx, [1; 4; 2]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'sorted', 'first');
%! assert_equal (caldays (U), [1, 2, 3]);
%! assert_equal (idx, [1; 3; 2]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'stable', 'first');
%! assert_equal (caldays (U), [1, 3, 2]);
%! assert_equal (idx, [1; 2; 3]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'sorted', 'last');
%! assert_equal (caldays (U), [1, 2, 3]);
%! assert_equal (idx, [1; 4; 2]);
%!test
%! [U, idx] = unique (caldays ([1, 3, 2, 2]), 'stable', 'last');
%! assert_equal (caldays (U), [1, 3, 2]);
%! assert_equal (idx, [1; 2; 4]);
%!test
%! [B, ixA, ixB] = unique (caldays ([1, 2; 2, 3; 1, 2]), 'rows', 'sorted', 'first');
%! assert_equal (caldays (B), [1, 2; 2, 3]);
%! assert_equal (ixA, [1; 2]);
%! assert_equal (ixB, [1; 2; 1]);
%!test
%! [B, ixA, ixB] = unique (caldays ([1, 2; 2, 3; 1, 2]), 'rows', 'stable', 'first');
%! assert_equal (caldays (B), [1, 2; 2, 3]);
%! assert_equal (ixA, [1; 2]);
%! assert_equal (ixB, [1; 2; 1]);
%!test
%! [B, ixA, ixB] = unique (caldays ([1, 2; 2, 3; 1, 2]), 'rows', 'sorted', 'last');
%! assert_equal (caldays (B), [1, 2; 2, 3]);
%! assert_equal (ixA, [3; 2]);
%! assert_equal (ixB, [1; 2; 1]);
%!test
%! [B, ixA, ixB] = unique (caldays ([1, 2; 2, 3; 1, 2]), 'rows', 'stable', 'last');
%! assert_equal (caldays (B), [2, 3; 1, 2]);
%! assert_equal (ixA, [2; 3]);
%! assert_equal (ixB, [2; 1; 2]);
%!error<calendarDuration.unique: 'legacy' option is not supported.> ...
%! unique (caldays ([1, 2; 2, 3; 1, 2]), 'legacy')

## MATLAB defines no equality for calendarDuration arrays -- '==' and '~='
## raise "Undefined function 'eq'" there -- so eq and ne are Octave extensions.
## They compare component by component, which is what MATLAB's own isequal
## does, so the two agree element for element.
%!assert_equal (calendarDuration (1, 2, 3) == calendarDuration (1, 2, 3), true)
%!assert_equal (calendarDuration (1, 2, 3) == calendarDuration (1, 2, 4), false)
%!assert_equal (calendarDuration (1, 0, 0) == calendarDuration (0, 12, 0), true)
%!assert_equal (calweeks (1) == caldays (7), true)
%!assert_equal (calmonths (1) == caldays (30), false)
%!assert_equal (calmonths (1) == caldays (31), false)
%!assert_equal (caldays (1) == calendarDuration (0, 0, 0, 24, 0, 0), false)
%!assert_equal (calendarDuration (0, 0, 0, 24, 0, 0) == calendarDuration (0, 0, 0, 0, 1440, 0), true)
%!assert_equal (calendarDuration (0, 0, 0) == calendarDuration (0, 0, 0, 0, 0, 0), true)
%!assert_equal (calendarDuration (NaN, 0, 0) == calendarDuration (NaN, 0, 0), false)
%!assert_equal (calendarDuration (Inf, 0, 0) == calendarDuration (Inf, 0, 0), true)
%!assert_equal (calendarDuration (Inf, 0, 0) == calendarDuration (-Inf, 0, 0), false)
%!assert_equal (calendarDuration (0, [1, 2, 3], 0) == calmonths (2), [false, true, false])
%!assert_equal (calendarDuration ([], [], []) == calmonths (1), logical ([]))
%!assert_equal (calendarDuration (1, 2, 3) != calendarDuration (1, 2, 3), false)
%!assert_equal (calendarDuration (NaN, 0, 0) != calendarDuration (NaN, 0, 0), true)
%!assert_equal (calendarDuration (0, [1, 2, 3], 0) != calmonths (2), [true, false, true])
%!error<calendarDuration.eq: equality is not defined between 'calendarDuration' and 'duration' arrays.> ...
%! caldays (1) == days (1)
%!error<calendarDuration.eq: equality is not defined between 'calendarDuration' and 'double' arrays.> ...
%! caldays (1) == 1
%!error<calendarDuration.ne: inequality is not defined between 'calendarDuration' and 'char' arrays.> ...
%! caldays (1) != 'a'

## isequal compares component by component and answers about any pair of
## values: an argument of another class is simply not equal, never an error.
## isequal holds a NaN unequal to itself where isequaln holds it equal.
%!assert_equal (isequal (calendarDuration (1, 0, 0), calendarDuration (0, 12, 0)), true)
%!assert_equal (isequal (caldays (1), calendarDuration (0, 0, 0, 24, 0, 0)), false)
%!assert_equal (isequal (calendarDuration (NaN, 0, 0), calendarDuration (NaN, 0, 0)), false)
%!assert_equal (isequaln (calendarDuration (NaN, 0, 0), calendarDuration (NaN, 0, 0)), true)
%!assert_equal (isequal (caldays (1), days (1)), false)
%!assert_equal (isequal (caldays (1), 1), false)
%!assert_equal (isequal (caldays (1), 'a'), false)
%!assert_equal (isequal (caldays (1), {1}), false)
%!assert_equal (isequaln (caldays (1), days (1)), false)
%!assert_equal (isequal (caldays (1), caldays (1), days (1)), false)
%!error<calendarDuration.isequal: too few input arguments.> isequal (caldays (1))
%!error<calendarDuration.isequaln: too few input arguments.> isequaln (caldays (1))

## unique sorts lexicographically by months, then days, then time, so a span
## of 30 days precedes one of 1 month and 24 hours precedes 1 day.  Every NaN
## is distinct from every other and sorts last.  B keeps the orientation of
## the input while ixA and ixB are always columns.
%!test
%! A = calmonths ([3, 1, 2, 1]);
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 3]);
%! assert_equal (calmonths (B), [1, 2, 3]);
%! assert_equal (ixA, [2; 3; 1]);
%! assert_equal (ixB, [3; 1; 2; 1]);
%!test
%! A = calmonths ([3, 1, 2, 1]);
%! [B, ixA, ixB] = unique (A, 'stable');
%! assert_equal (calmonths (B), [3, 1, 2]);
%! assert_equal (ixA, [1; 2; 3]);
%! assert_equal (ixB, [1; 2; 3; 2]);
%!test
%! A = calmonths ([3, 1, 2, 1]);
%! [B, ixA, ixB] = unique (A, 'last');
%! assert_equal (calmonths (B), [1, 2, 3]);
%! assert_equal (ixA, [4; 3; 1]);
%! assert_equal (ixB, [3; 1; 2; 1]);
%!test
%! A = [calmonths(1), caldays(30), calmonths(1)];
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 2]);
%! [mo, d] = split (B, {'months', 'days'});
%! assert_equal (mo, [0, 1]);
%! assert_equal (d, [30, 0]);
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, [2; 1; 2]);
%!test
%! A = [calmonths(1), caldays(30), calmonths(1)];
%! [B, ixA, ixB] = unique (A, 'stable');
%! [mo, d] = split (B, {'months', 'days'});
%! assert_equal (mo, [1, 0]);
%! assert_equal (d, [0, 30]);
%! assert_equal (ixA, [1; 2]);
%! assert_equal (ixB, [1; 2; 1]);
%!test
%! A = [calmonths(1), caldays(30), calmonths(1)];
%! [B, ixA, ixB] = unique (A, 'last');
%! assert_equal (ixA, [2; 3]);
%! assert_equal (ixB, [2; 1; 2]);
%!test
%! A = [calendarDuration(1, 0, 0), calendarDuration(0, 12, 0)];
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 1]);
%! assert_equal (calmonths (B), 12);
%! assert_equal (ixA, 1);
%! assert_equal (ixB, [1; 1]);
%!test
%! A = [calendarDuration(1, 0, 0), calendarDuration(0, 12, 0)];
%! [B, ixA] = unique (A, 'last');
%! assert_equal (calmonths (B), 12);
%! assert_equal (ixA, 2);
%!test
%! A = [calmonths(1), calendarDuration(NaN, 0, 0), calmonths(1), ...
%!      calendarDuration(NaN, 0, 0)];
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 3]);
%! assert_equal (calmonths (B), [1, NaN, NaN]);
%! assert_equal (ixA, [1; 2; 4]);
%! assert_equal (ixB, [1; 2; 1; 3]);
%!test
%! A = [calmonths(1), calendarDuration(NaN, 0, 0), calmonths(1), ...
%!      calendarDuration(NaN, 0, 0)];
%! [B, ixA, ixB] = unique (A, 'stable');
%! assert_equal (calmonths (B), [1, NaN, NaN]);
%! assert_equal (ixA, [1; 2; 4]);
%! assert_equal (ixB, [1; 2; 1; 3]);
%!test
%! A = [calmonths(1), calendarDuration(NaN, 0, 0), calmonths(1), ...
%!      calendarDuration(NaN, 0, 0)];
%! [~, ixA] = unique (A, 'last');
%! assert_equal (ixA, [3; 2; 4]);
%!test
%! A = [caldays(1), calendarDuration(0, 0, 0, 24, 0, 0), caldays(1)];
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 2]);
%! [d, t] = split (B, {'days', 'time'});
%! assert_equal (d, [0, 1]);
%! assert_equal (seconds (t), [86400, 0]);
%! assert_equal (ixA, [2; 1]);
%! assert_equal (ixB, [2; 1; 2]);
%!test
%! A = [caldays(1), calendarDuration(0, 0, 0, 24, 0, 0), caldays(1)];
%! [B, ixA] = unique (A, 'stable');
%! [d, t] = split (B, {'days', 'time'});
%! assert_equal (d, [1, 0]);
%! assert_equal (seconds (t), [0, 86400]);
%! assert_equal (ixA, [1; 2]);
%!test
%! A = calmonths ([3; 1; 2; 1]);
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [3, 1]);
%! assert_equal (calmonths (B), [1; 2; 3]);
%! assert_equal (ixA, [2; 3; 1]);
%! assert_equal (ixB, [3; 1; 2; 1]);
%!test
%! A = calmonths ([3; 1; 2; 1]);
%! [B, ixA, ixB] = unique (A, 'stable');
%! assert_equal (calmonths (B), [3; 1; 2]);
%! assert_equal (ixA, [1; 2; 3]);
%! assert_equal (ixB, [1; 2; 3; 2]);
%!test
%! A = [calendarDuration(0, 0, 0), calendarDuration(0, 0, 0)];
%! [B, ixA, ixB] = unique (A);
%! assert_equal (size (B), [1, 1]);
%! assert_equal (calmonths (B), 0);
%! assert_equal (ixA, 1);
%! assert_equal (ixB, [1; 1]);
%!test
%! A = [calendarDuration(0, 0, 0), calendarDuration(0, 0, 0)];
%! [~, ixA] = unique (A, 'last');
%! assert_equal (ixA, 2);

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

%!test
%! cD = cat (1, 2, caldays ([3; 4]));
%! assert_equal (caldays (cD), [0; 3; 4]);
%! assert_equal (days (time (cD)), [2; 0; 0]);
%!test
%! cD = cat (1, caldays ([3; 4]), 5);
%! assert_equal (caldays (cD), [3; 4; 0]);
%! assert_equal (days (time (cD)), [0; 0; 5]);
%!test
%! cD = cat (2, 2, caldays ([3, 4]));
%! assert_equal (caldays (cD), [0, 3, 4]);
%! assert_equal (days (time (cD)), [2, 0, 0]);
%!test
%! cD = cat (2, caldays ([3, 4]), 5);
%! assert_equal (caldays (cD), [3, 4, 0]);
%! assert_equal (days (time (cD)), [0, 0, 5]);
%!test
%! cD = cat (1, [0; 1; 2], caldays ([3; 4]));
%! assert_equal (caldays (cD), [0; 0; 0; 3; 4]);
%! assert_equal (days (time (cD)), [0; 1; 2; 0; 0]);
%!test
%! cD = cat (1, caldays ([3; 4]), [0; 2; 5]);
%! assert_equal (caldays (cD), [3; 4; 0; 0; 0]);
%! assert_equal (days (time (cD)), [0; 0; 0; 2; 5]);
%!test
%! cD = cat (2, [0, 1, 2], caldays ([3, 4]));
%! assert_equal (caldays (cD), [0, 0, 0, 3, 4]);
%! assert_equal (days (time (cD)), [0, 1, 2, 0, 0]);
%!test
%! cD = cat (2, caldays ([3, 4]), [0, 2, 5]);
%! assert_equal (caldays (cD), [3, 4, 0, 0, 0]);
%! assert_equal (days (time (cD)), [0, 0, 0, 2, 5]);
%!test
%! cD = cat (1, caldays ([1, 2]), caldays ([3, 4]));
%! assert_equal (caldays (cD), [1, 2; 3, 4]);
%!test
%! cD = cat (2, caldays ([1, 2]), caldays ([3, 4]));
%! assert_equal (caldays (cD), [1, 2, 3, 4]);
%!test
%! cD = cat (3, caldays ([1, 2]), caldays ([3, 4]));
%! assert_equal (caldays (cD), reshape ([1, 2, 3, 4], 1, 2, 2));
%!test
%! cD = cat (4, caldays ([1, 2]), caldays ([3, 4]));
%! assert_equal (caldays (cD), reshape ([1, 2, 3, 4], 1, 2, 1, 2));
%!error<cat: dimension mismatch> cat (1, caldays ([3, 4]), [0; 2; 5])
%!error<cat: dimension mismatch> cat (1, caldays ([3; 4]), [0, 2; 5, 2])
%!error<cat: dimension mismatch> cat (2, caldays ([3; 4]), [0; 2; 5])
%!error<cat: dimension mismatch> cat (2, caldays ([3, 4]), [0, 2; 5, 2])
%!error<calendarDuration: invalid input to constructor.> ...
%! cat (1, caldays ([3; 4]), {})
%!error<calendarDuration: invalid input to constructor.> ...
%! cat (1, caldays ([3; 4]), {'o'})
%!error<calendarDuration: invalid input to constructor.> ...
%! cat (1, caldays ([3; 4]), 'o')
%!error<calendarDuration: invalid input to constructor.> ...
%! cat (1, caldays ([3; 4]), string ('o'))

%!test
%! cD = [days(1), caldays(2)];
%! assert_equal (caldays (cD), [0, 2]);
%! assert_equal (days (time (cD)), [1, 0]);
%!test
%! cD = [days(1), 1, caldays(2)];
%! assert_equal (caldays (cD), [0, 0, 2]);
%! assert_equal (days (time (cD)), [1, 1, 0]);
%!test
%! cD = [caldays(1), days(2)];
%! assert_equal (caldays (cD), [1, 0]);
%! assert_equal (days (time (cD)), [0, 2]);
%!test
%! cD = [caldays(1), 1, days(2)];
%! assert_equal (caldays (cD), [1, 0, 0]);
%! assert_equal (days (time (cD)), [0, 1, 2]);
%!test
%! cD = [days(1), [], caldays(2)];
%! assert_equal (caldays (cD), [0, 2]);
%! assert_equal (days (time (cD)), [1, 0]);
%!test
%! cD = [caldays(1), [], days(2)];
%! assert_equal (caldays (cD), [1, 0]);
%! assert_equal (days (time (cD)), [0, 2]);
%!test
%! cD = [days(1), ones(0, 1), caldays(2)];
%! assert_equal (caldays (cD), [0, 2]);
%! assert_equal (days (time (cD)), [1, 0]);
%!test
%! cD = [caldays(1), ones(0, 1), days(2)];
%! assert_equal (caldays (cD), [1, 0]);
%! assert_equal (days (time (cD)), [0, 2]);
%!test
%! cD = [days(1), ones(0, 1), caldays(2), ones(1, 0)];
%! assert_equal (caldays (cD), [0, 2]);
%! assert_equal (days (time (cD)), [1, 0]);
%!test
%! cD = [caldays(1), ones(0, 1), days(2), ones(1, 0)];
%! assert_equal (caldays (cD), [1, 0]);
%! assert_equal (days (time (cD)), [0, 2]);
%!test
%! cD = [days(1), ones(2, 0, 1), caldays(2)];
%! assert_equal (caldays (cD), [0, 2]);
%! assert_equal (days (time (cD)), [1, 0]);
%!test
%! cD = [caldays(1), ones(2, 0, 1), days(2)];
%! assert_equal (caldays (cD), [1, 0]);
%! assert_equal (days (time (cD)), [0, 2]);

%!test
%! cD = [days(1); caldays(2)];
%! assert_equal (caldays (cD), [0; 2]);
%! assert_equal (days (time (cD)), [1; 0]);
%!test
%! cD = [days(1); 1; caldays(2)];
%! assert_equal (caldays (cD), [0; 0; 2]);
%! assert_equal (days (time (cD)), [1; 1; 0]);
%!test
%! cD = [caldays(1); days(2)];
%! assert_equal (caldays (cD), [1; 0]);
%! assert_equal (days (time (cD)), [0; 2]);
%!test
%! cD = [caldays(1); 1; days(2)];
%! assert_equal (caldays (cD), [1; 0; 0]);
%! assert_equal (days (time (cD)), [0; 1; 2]);
%!test
%! cD = [days(1); []; caldays(2)];
%! assert_equal (caldays (cD), [0; 2]);
%! assert_equal (days (time (cD)), [1; 0]);
%!test
%! cD = [caldays(1); []; days(2)];
%! assert_equal (caldays (cD), [1; 0]);
%! assert_equal (days (time (cD)), [0; 2]);
%!test
%! cD = [days(1); ones(0, 1); caldays(2)];
%! assert_equal (caldays (cD), [0; 2]);
%! assert_equal (days (time (cD)), [1; 0]);
%!test
%! cD = [caldays(1); ones(0, 1); days(2)];
%! assert_equal (caldays (cD), [1; 0]);
%! assert_equal (days (time (cD)), [0; 2]);
%!test
%! cD = [days(1); ones(0, 1); caldays(2); ones(1, 0)];
%! assert_equal (caldays (cD), [0; 2]);
%! assert_equal (days (time (cD)), [1; 0]);
%!test
%! cD = [caldays(1); ones(0, 1); days(2); ones(1, 0)];
%! assert_equal (caldays (cD), [1; 0]);
%! assert_equal (days (time (cD)), [0; 2]);
%!test
%! cD = [days(1); ones(2, 0, 1); caldays(2)];
%! assert_equal (caldays (cD), [0; 2]);
%! assert_equal (days (time (cD)), [1; 0]);
%!test
%! cD = [caldays(1); ones(2, 0, 1); days(2)];
%! assert_equal (caldays (cD), [1; 0]);
%! assert_equal (days (time (cD)), [0; 2]);

%!shared x1, x2
%! x1 = [2, 3];
%! x2 = magic (3);
%!assert_equal (caldays (repmat (caldays (x1), 3)), repmat (x1, 3))
%!assert_equal (caldays (repmat (caldays (x1), 2, 3)), repmat (x1, 2, 3))
%!assert_equal (caldays (repmat (caldays (x1), 1, 1, 3)), repmat (x1, 1, 1, 3))
%!assert_equal (caldays (repmat (caldays (x1), [1, 1, 3])), repmat (x1, [1, 1, 3]))
%!assert_equal (caldays (repmat (caldays (x1), 1, 0, 3)), repmat (x1, 1, 0, 3))
%!assert_equal (caldays (repmat (caldays (x1), [1, 0, 3])), repmat (x1, 1, 0, 3))
%!assert_equal (caldays (repelem (caldays (x1), 3)), repelem (x1, 3))
%!assert_equal (caldays (repelem (caldays (x1), 2, 3)), repelem (x1, 2, 3))
%!assert_equal (caldays (repelem (caldays (x1), [1, 3])), repelem (x1, [1, 3]))
%!assert_equal (caldays (repelem (caldays (x2), 1, [1, 1, 3])), repelem (x2, 1, [1, 1, 3]))
%!assert_equal (caldays (repelem (caldays (x2), [2, 3, 4], [1, 0, 3])), repelem (x2, [2, 3, 4], [1, 0, 3]))
%!assert_equal (caldays (repelem (caldays (x2), 2, [1, 0, 3])), repelem (x2, 2, [1, 0, 3]))
%!assert_equal (caldays (repelems (caldays (x2), [1, 3, 4; 4, 2, 5])), repelems (x2, [1, 3, 4; 4, 2, 5]))
%!assert_equal (isempty (repelems (caldays (x2), [1, 3, 4; 0, 0, 0])), true)
%!assert_equal (size (reshape (caldays (x2), 1, 9)), [1, 9])
%!assert_equal (caldays (reshape (caldays (x2), 1, 9)), reshape (x2, 1, 9))
%!assert_equal (caldays (reshape (caldays (x2), 1, 1, 9)), reshape (x2, 1, 1, 9))
%!assert_equal (caldays (reshape (caldays (x2), 1, 1, 3, 1, 3)), reshape (x2, 1, 1, 3, 1, 3))
%!assert_equal (caldays (circshift (caldays (x2), 1)), circshift (x2, 1))
%!assert_equal (caldays (circshift (caldays (x2), -2)), circshift (x2, -2))
%!assert_equal (caldays (circshift (caldays (x2), [1, -2])), circshift (x2, [1, -2]))
%!assert_equal (caldays (circshift (caldays (x2), -1, 2)), circshift (x2, -1, 2))
%!test
%! cD = permute (caldays (x2), [2, 1]);
%! assert_equal (caldays (cD), permute (x2, [2, 1]));
%!test
%! cD = ipermute (permute (caldays (x2), [2, 1]), [2, 1]);
%! assert_equal (caldays (cD), x2);
%!assert_equal (caldays (caldays (x2)'), x2');
%!assert_equal (caldays (caldays (x2).'), x2');
%!assert_equal (caldays (ctranspose (caldays (x2))), x2');
%!assert_equal (caldays (ctranspose (caldays (x2))), caldays (transpose (caldays (x2))));

## Concatenation takes the union of the operands' formats, so units that only
## one side displays survive.  A duration or a bare numeric may be concatenated
## and lands in the time component, the numeric counting days.
%!test
%! A = [calmonths(1), calendarDuration(0, 0, 2), calendarDuration(0, 0, 0, 3, 0, 0)];
%! B = [A, calmonths(9)];
%! assert_equal (size (B), [1, 4]);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1, 0, 0, 9]);
%! assert_equal (d, [0, 2, 0, 0]);
%! assert_equal (seconds (t), [0, 0, 10800, 0]);
%!test
%! B = [calmonths(1); calmonths(2)];
%! assert_equal (size (B), [2, 1]);
%! assert_equal (calmonths (B), [1; 2]);
%!assert_equal (size (cat (1, calmonths (1), calmonths (2))), [2, 1])
%!assert_equal (size (cat (2, calmonths (1), calmonths (2))), [1, 2])
%!test
%! B = cat (3, calmonths (1), calmonths (2));
%! assert_equal (size (B), [1, 1, 2]);
%! assert_equal (calmonths (B)(:).', [1, 2]);
%!test
%! B = [calweeks(1), calquarters(1)];
%! assert_equal (B.Format, 'yqmwdt');
%! assert_equal (calmonths (B), [0, 3]);
%! assert_equal (split (B, 'days'), [7, 0]);
%!test
%! B = [calquarters(1), calweeks(1)];
%! assert_equal (B.Format, 'yqmwdt');
%! assert_equal (calmonths (B), [3, 0]);
%! assert_equal (split (B, 'days'), [0, 7]);
%!assert_equal ([calmonths(1), calmonths(2)].Format, 'ymdt')
%!test
%! B = [calmonths(1), hours(5)];
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 18000]);
%!test
%! B = [calmonths(1), 5];
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 432000]);
%!test
%! B = [calmonths(1), calendarDuration([], [], [])];
%! assert_equal (size (B), [1, 1]);
%! assert_equal (calmonths (B), 1);
%!assert_equal (size ([calendarDuration([],[],[]), calendarDuration([],[],[])]), [0, 0])

## repmat and repelem tile and expand while keeping the format.
%!test
%! B = repmat (calmonths (1), 2);
%! assert_equal (size (B), [2, 2]);
%! assert_equal (calmonths (B), [1, 1; 1, 1]);
%!test
%! B = repmat (calmonths ([1, 2]), 2, 3);
%! assert_equal (size (B), [2, 6]);
%! assert_equal (calmonths (B), [1, 2, 1, 2, 1, 2; 1, 2, 1, 2, 1, 2]);
%!assert_equal (calmonths (repmat (calmonths ([1, 2]), [2, 3])), ...
%!              [1, 2, 1, 2, 1, 2; 1, 2, 1, 2, 1, 2])
%!assert_equal (size (repmat (calmonths (1), [2, 2, 2])), [2, 2, 2])
%!assert_equal (size (repmat (calmonths (1), 0)), [0, 0])
%!assert_equal (calmonths (repelem (calmonths ([1, 2]), 2)), [1, 1, 2, 2])
%!assert_equal (calmonths (repelem (calmonths ([1, 2, 3]), [1, 2, 3])), ...
%!              [1, 2, 2, 3, 3, 3])
%!test
%! B = repelem (calmonths ([1, 2]), 2, 2);
%! assert_equal (size (B), [2, 4]);
%! assert_equal (calmonths (B), [1, 1, 2, 2; 1, 1, 2, 2]);
%!assert_equal (size (repelem (calmonths ([1, 2]), 0)), [1, 0])
%!assert_equal (repmat (calweeks (1), 2).Format, 'ymwdt')

## reshape, circshift, permute and the transposes reorder elements without
## disturbing the format.
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! B = reshape (A, 3, 2);
%! assert_equal (size (B), [3, 2]);
%! assert_equal (calmonths (B), [1, 5; 4, 3; 2, 6]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! assert_equal (calmonths (reshape (A, [2, 3])), [1, 2, 3; 4, 5, 6]);
%! assert_equal (calmonths (reshape (A, 3, [])), [1, 5; 4, 3; 2, 6]);
%!assert_equal (size (reshape (repmat (calmonths (1), 2, 4), 2, 2, 2)), [2, 2, 2])
%!test
%! A = [calmonths(1), calendarDuration(0, 0, 2), calendarDuration(0, 0, 0, 3, 0, 0)];
%! B = circshift (A, 1);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [0, 1, 0]);
%! assert_equal (d, [0, 0, 2]);
%! assert_equal (seconds (t), [10800, 0, 0]);
%!test
%! A = [calmonths(1), calendarDuration(0, 0, 2), calendarDuration(0, 0, 0, 3, 0, 0)];
%! B = circshift (A, -1);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [0, 0, 1]);
%! assert_equal (d, [2, 0, 0]);
%! assert_equal (seconds (t), [0, 10800, 0]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! assert_equal (calmonths (circshift (A, 1, 2)), [3, 1, 2; 6, 4, 5]);
%!test
%! A = [calmonths(1), calendarDuration(0, 0, 2), calendarDuration(0, 0, 0, 3, 0, 0)];
%! assert_equal (calmonths (circshift (A, 0)), [1, 0, 0]);
%! assert_equal (split (circshift (A, 0), 'days'), [0, 2, 0]);
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! B = permute (A, [2, 1]);
%! assert_equal (size (B), [3, 2]);
%! assert_equal (calmonths (B), [1, 4; 2, 5; 3, 6]);
%! assert_equal (calmonths (ipermute (B, [2, 1])), [1, 2, 3; 4, 5, 6]);
%!assert_equal (size (permute (reshape (repmat (calmonths (1), 2, 4), 2, 2, 2), ...
%!                             [3, 1, 2])), [2, 2, 2])
%!test
%! A = [calmonths(1), calmonths(2), calmonths(3); ...
%!      calmonths(4), calmonths(5), calmonths(6)];
%! assert_equal (calmonths (A.'), [1, 4; 2, 5; 3, 6]);
%! assert_equal (calmonths (A'), [1, 4; 2, 5; 3, 6]);
%! assert_equal (A.'.Format, 'ymdt');
%!assert_equal (calmonths (circshift (calweeks ([1, 2]), 1)), [0, 0])
%!assert_equal (circshift (calweeks ([1, 2]), 1).Format, 'ymwdt')
%!assert_equal (calweeks (1).Format, 'ymwdt')
%!assert_equal (reshape (calweeks ([1, 2, 3, 4]), 2, 2).Format, 'ymwdt')
%!assert_equal (repelem (calweeks ([1, 2]), 2).Format, 'ymwdt')
%!assert_equal (calquarters ([1, 2]).'.Format, 'qmdt')

## repelems has no MATLAB counterpart and follows Octave's own function: the
## first row of the index matrix names elements, the second how many of each.
%!assert_equal (calmonths (repelems (calmonths ([1, 2]), [1, 2; 2, 1])), [1, 1, 2])
%!assert_equal (calmonths (repelems (calmonths ([1, 2, 3]), [1, 3; 1, 2])), [1, 3, 3])

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

%!test
%! cD = caldays (x2);
%! assert_equal (caldays (cD(end)), 2);
%! assert_equal (caldays (cD(1:end)), x2(:)');
%! assert_equal (caldays (cD(1:end)'), x2(:));
%!test
%! cD = caldays ([x2, x2]);
%! assert_equal (caldays (cD(1, 2:end-1)), [1, 6, 8, 1]);
%! assert_equal (caldays (cD(1:end-1, 2:end-1)), [1, 6, 8, 1; 5, 7, 3, 5]);
%! assert_equal (size (cD(1:end, 2:end-1)), [3, 4]);

%!test
%! cD = caldays ([x2, x2]);
%! assert_equal (class (cD(1)), 'calendarDuration');
%! assert_equal (length (cD(1)), 1);
%! assert_equal (class (cD(1,:)), 'calendarDuration');
%! assert_equal (length (cD(1,:)), 6);
%! assert_equal (length (cD(:,2)), 3);
%!test
%! cD = caldays (x1);
%! assert_equal (cD.proxyArray, [0, 2, 0, 0, 3, 0]);
%! assert_equal (cD.Format, 'ymdt');
%!test
%! cD = caldays (x1');
%! assert_equal (cD.proxyArray, [0, 2, 0; 0, 3, 0]);
%! assert_equal (cD.Format, 'ymdt');
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! assert_equal (cD.proxyArray, [16, 2, 0; 28, 4, 0]);
%! assert_equal (cD.Format, 'ymdt');
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4], hours ([8.5; 14.2]), 'Format', 'qmdt');
%! assert_equal (cD.proxyArray, [16, 2, 8.5; 28, 4, 14.2]);
%! assert_equal (cD.Format, 'qmdt');
%!error<calendarDuration.subsref: '{}' invalid indexing for referencing values. Use '\(\)' instead.> ...
%! caldays (3){1}
%!error<calendarDuration.subsref: '.' index argument must be a character vector.> ...
%! subsref (caldays (3), struct ('type', '.', 'subs', 123));
%!error<calendarDuration.subsref: unrecognized property: 'fdasd'> ...
%! caldays (3).fdasd

%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! cD(1) = calyears (2) + calmonths (1) + caldays (14);
%! assert_equal (calyears (cD), [2; 2]);
%! assert_equal (calmonths (cD), [25; 28]);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, [14; 4]);
%! assert_equal (days (time (cD)), [0; 0])
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! cD(end) = 180;
%! assert_equal (calyears (cD), [1; 0]);
%! assert_equal (calmonths (cD), [16; 0]);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, [2; 0]);
%! assert_equal (days (time (cD)), [0; 180])
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! cD(end) = hours (12);
%! assert_equal (calyears (cD), [1; 0]);
%! assert_equal (calmonths (cD), [16; 0]);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, [2; 0]);
%! assert_equal (days (time (cD)), [0; 0.5])
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! cD(1) = [];
%! assert_equal (calyears (cD), 2);
%! assert_equal (calmonths (cD), 28);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, 4);
%!test
%! cD = calendarDuration ([1, 3; 2, 2], 4, 0);
%! cD(1,:) = [];
%! assert_equal (calyears (cD), [2, 2]);
%! assert_equal (calmonths (cD), [28, 28]);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, [0, 0]);
%! cD(end+1, :) = caldays ([2; 3]);
%! assert_equal (caldays (cD(2,:)), [2, 3]);
%!test
%! cD = calendarDuration ([1, 3; 2, 2], 4, 0);
%! cD(:, 2) = [];
%! assert_equal (calyears (cD), [1; 2]);
%! assert_equal (calmonths (cD), [16; 28]);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, [0; 0]);
%! cD(:, end+1) = calmonths ([2; 3]);
%! assert_equal (calmonths (cD(:,2)), [2; 3]);
%! cD(:, end+1) = calmonths ([2, 3]);
%! assert_equal (calmonths (cD(2,:)), [28, 3, 3]);
%! assert_equal (calmonths (cD(:,[2,3])), [2, 2; 3, 3]);
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! cD(2) = [];
%! assert_equal (calyears (cD), 1);
%! assert_equal (calmonths (cD), 16);
%! [~, D_] = split (cD, {'months', 'days'});
%! assert_equal (D_, 2);
%!test
%! cD = calendarDuration ([1; 2], 4, [2; 4]);
%! assert_equal (cD.Format, 'ymdt');
%! cD.Format = 'qmdt';
%! assert_equal (cD.Format, 'qmdt');
%! cD.Format = 'qmwdt';
%! assert_equal (cD.Format, 'qmwdt');
%!error<calendarDuration.subsasgn: assignment value must be calendarDuration or duration array or a numeric array representing 24-hour days.> ...
%! cD = caldays([1:5]); cD(2) = {1}
%!error<calendarDuration.subsasgn: '{}' invalid indexing for assigning values. Use '\(\)' instead.> ...
%! cD = caldays([1:5]); cD{2} = 1
%!error<calendarDuration.subsasgn: '.' index argument must be a character vector.> ...
%! subsasgn (caldays (3), struct ('type', '.', 'subs', 123));
%!error<calendarDuration.subsasgn: 'Format' must be a character vector.> ...
%! cD = caldays([1:5]); cD.Format = ['asd';'ert']
%!error<calendarDuration.subsasgn: 'Format' contains duplicate characters.> ...
%! cD = caldays([1:5]); cD.Format = 'mmdt'
%!error<calendarDuration.subsasgn: 'Format' must contain 'm', 'd', and 't'.> ...
%! cD = caldays([1:5]); cD.Format = 'ymd'
%!error<calendarDuration.subsasgn: 'Format' has invalid order of characters.> ...
%! cD = caldays([1:5]); cD.Format = 'mydt'
%!error<calendarDuration.subsasgn: unrecognized property: 'fdasd'> ...
%! cD = caldays([1:5]); cD.fdasd = 1

## Test 'calendarDuration.empty' -- empty calendarDuration arrays
%!assert_equal (size (calendarDuration.empty), [0, 0])
%!assert_equal (class (calendarDuration.empty), 'calendarDuration')
%!assert_equal (isempty (calendarDuration.empty), true)
%!assert_equal (calendarDuration.empty.Format, 'ymdt')
%!assert_equal (size (calendarDuration.empty (2, 0)), [2, 0])
%!assert_equal (size (calendarDuration.empty (0, 3)), [0, 3])
%!error <calendarDuration.empty: at least one dimension must be zero for an empty array.> ...
%! calendarDuration.empty (3)

## Test 'calendarDuration.empty' -- size arguments follow 'zeros' rules: a
## negative dimension counts as zero, a size vector holding none names no size
%!assert_equal (size (calendarDuration.empty (0)), [0, 0])
%!assert_equal (size (calendarDuration.empty (-1)), [0, 0])
%!assert_equal (size (calendarDuration.empty (-2, -3)), [0, 0])
%!assert_equal (size (calendarDuration.empty ([])), [0, 0])
%!assert_equal (size (calendarDuration.empty (2, 0, 3)), [2, 0, 3])
%!assert_equal (size (calendarDuration.empty ([0 2 3])), [0, 2, 3])
%!error <calendarDuration.empty: dimensions must be integer values.> ...
%! calendarDuration.empty (1.5)

## A wholly zero element takes its unit from the rest of the array: the
## smallest unit any element uses, days when none does.  Values below are
## MATLAB R2024a.
%!assert_equal (cellstr ([calendarDuration(0,0,0), calyears(1), calmonths(2)]), ...
%!              {'0mo', '1y', '2mo'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calyears(1), caldays(3)]), ...
%!              {'0d', '1y', '3d'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calmonths(2), caldays(3)]), ...
%!              {'0d', '2mo', '3d'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calyears(1), ...
%!                        calendarDuration(0,0,0) + hours(5)]), ...
%!              {'0s', '1y', '5h 0m 0s'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calmonths(2), ...
%!                        calendarDuration(0,0,0) + hours(5)]), ...
%!              {'0s', '2mo', '5h 0m 0s'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), caldays(3), ...
%!                        calendarDuration(0,0,0) + hours(5)]), ...
%!              {'0s', '3d', '5h 0m 0s'})

## With nothing to take a unit from, a zero renders in days -- scalar or not.
%!assert_equal (cellstr ([calendarDuration(0,0,0), calendarDuration(0,0,0)]), ...
%!              {'0d', '0d'})
%!assert_equal (char (calendarDuration (0, 0, 0)), '0d')
%!assert_equal (char (calmonths (0)), '0d')
%!assert_equal (char (calyears (0)), '0d')

## A zero alongside an element that uses every unit reads in the smallest one.
%!assert_equal (cellstr ([calendarDuration(0,0,0), ...
%!                        calyears(1) + calmonths(2) + caldays(3)]){1}, '0d')
%!assert_equal (cellstr ([calendarDuration(0,0,0), ...
%!                        calmonths(2) + hours(5)]){1}, '0s')

## Concatenation displays in the union of the inputs' formats, so weeks and
## quarters survive rather than widening back to days and months.  Values
## below are MATLAB R2024a.
%!assert_equal (cellstr ([calendarDuration(0,0,0), calweeks(2)]), {'0w', '2w'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calquarters(3)]), ...
%!              {'0q', '3q'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calyears(1), ...
%!                        calquarters(3)]), {'0q', '1y', '3q'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calweeks(2), ...
%!                        calquarters(3)]), {'0w', '2w', '3q'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calmonths(2), ...
%!                        calquarters(3)]), {'0mo', '2mo', '3q'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), caldays(3), ...
%!                        calweeks(2)]), {'0d', '3d', '2w'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), calyears(1), ...
%!                        calweeks(2)]), {'0w', '1y', '2w'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), caldays(3), ...
%!                        calquarters(3)]), {'0d', '3d', '3q'})
%!assert_equal (cellstr ([calendarDuration(0,0,0), ...
%!                        calendarDuration(0,0,0) + hours(5), ...
%!                        calweeks(2)]), {'0s', '5h 0m 0s', '2w'})

## A format keeps its own units when nothing else is concatenated onto it.
%!assert_equal (char (calweeks (2)), '2w')
%!assert_equal (char (calquarters (3)), '3q')
%!assert_equal ([calweeks(1), calquarters(1)](1).Format, 'yqmwdt')
%!assert_equal ([calweeks(1); calquarters(1)].Format, 'yqmwdt')
%!assert_equal (calquarters (1).Format, 'qmdt')
%!assert_equal (calmonths (1).Format, 'ymdt')
%!assert_equal (calyears (1).Format, 'ymdt')
%!assert_equal (caldays (1).Format, 'ymdt')

## Indexing reads a subset without disturbing the Format.  A(:) returns a
## column whatever the input's shape, an empty index an empty array, and a
## trailing singleton index is allowed.
%!shared A, A2
%! A = [calmonths(1), calendarDuration(0, 0, 2), calendarDuration(0, 0, 0, 3, 0, 0)];
%! A2 = [calmonths(1), calmonths(2), calmonths(3); ...
%!       calmonths(4), calmonths(5), calmonths(6)];
%!test
%! [mo, d, t] = split (A(2), {'months', 'days', 'time'});
%! assert_equal ([mo, d, seconds(t)], [0, 2, 0]);
%!assert_equal (seconds (split (A(end), 'time')), 10800)
%!assert_equal (split (A(end-1), 'days'), 2)
%!test
%! B = A(:);
%! assert_equal (size (B), [3, 1]);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1; 0; 0]);
%! assert_equal (d, [0; 2; 0]);
%! assert_equal (seconds (t), [0; 0; 10800]);
%!test
%! B = A([1, 3]);
%! assert_equal (size (B), [1, 2]);
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 10800]);
%!test
%! B = A(logical ([1, 0, 1]));
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 10800]);
%!assert_equal (calmonths (A2(2,3)), 6)
%!assert_equal (calmonths (A2(end,end)), 6)
%!test
%! B = A2(:,2);
%! assert_equal (size (B), [2, 1]);
%! assert_equal (calmonths (B), [2; 5]);
%!test
%! B = A2(2,:);
%! assert_equal (size (B), [1, 3]);
%! assert_equal (calmonths (B), [4, 5, 6]);
%!assert_equal (size (A([])), [0, 0])
%!assert_equal (calmonths (A(1,1,1)), 1)
%!assert_equal (A.Format, 'ymdt')
%!assert_equal (A(2).Format, 'ymdt')

## Deleting elements.  A(:) = [] empties an array to 0x0 while deleting the
## only element of a scalar leaves 1x0, and a null assignment takes one
## non-colon index at most.
%!test
%! B = A;  B(2) = [];
%! assert_equal (size (B), [1, 2]);
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 10800]);
%!test
%! B = A;  B([1, 3]) = [];
%! assert_equal (size (B), [1, 1]);
%! assert_equal (split (B, 'days'), 2);
%!test
%! B = A;  B(end) = [];
%! assert_equal (calmonths (B), [1, 0]);
%! assert_equal (split (B, 'days'), [0, 2]);
%!test
%! B = A;  B(logical ([1, 0, 1])) = [];
%! assert_equal (size (B), [1, 1]);
%! assert_equal (split (B, 'days'), 2);
%!test
%! B = A;  B(:) = [];
%! assert_equal (size (B), [0, 0]);
%!test
%! B = A2;  B(:,1) = [];
%! assert_equal (size (B), [2, 2]);
%! assert_equal (calmonths (B), [2, 3; 5, 6]);
%!test
%! B = A2;  B(1,:) = [];
%! assert_equal (size (B), [1, 3]);
%! assert_equal (calmonths (B), [4, 5, 6]);
%!test
%! B = calmonths (1);  B(1) = [];
%! assert_equal (size (B), [1, 0]);

## Assignment.  Growing past the end fills the gap with zero spans, not with
## missing values.  A duration or a bare numeric may be assigned, the numeric
## counting days, and both land in the time component.
%!test
%! B = A;  B(2) = calmonths (9);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1, 9, 0]);
%! assert_equal (d, [0, 0, 0]);
%! assert_equal (seconds (t), [0, 0, 10800]);
%!test
%! B = A;  B(5) = calmonths (9);
%! assert_equal (size (B), [1, 5]);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1, 0, 0, 0, 9]);
%! assert_equal (d, [0, 2, 0, 0, 0]);
%! assert_equal (seconds (t), [0, 0, 10800, 0, 0]);
%!test
%! B = A2;  B(2,4) = calmonths (9);
%! assert_equal (size (B), [2, 4]);
%! assert_equal (calmonths (B), [1, 2, 3, 0; 4, 5, 6, 9]);
%!test
%! B = A;  B(:) = calmonths (9);
%! assert_equal (calmonths (B), [9, 9, 9]);
%!test
%! B = A;  B(end+1) = calmonths (9);
%! assert_equal (size (B), [1, 4]);
%! assert_equal (calmonths (B), [1, 0, 0, 9]);
%! assert_equal (seconds (split (B, 'time')), [0, 0, 10800, 0]);
%!test
%! B = A;  B(2) = hours (5);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1, 0, 0]);
%! assert_equal (d, [0, 0, 0]);
%! assert_equal (seconds (t), [0, 18000, 10800]);
%!test
%! B = A;  B(2) = 5;
%! assert_equal (seconds (split (B, 'time')), [0, 432000, 10800]);
%!test
%! B = A;  B(logical ([1, 0, 1])) = calmonths (9);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [9, 0, 9]);
%! assert_equal (d, [0, 2, 0]);
%! assert_equal (seconds (t), [0, 0, 0]);
%!test
%! B = A;  B(1:2) = [calmonths(8), calmonths(9)];
%! assert_equal (calmonths (B), [8, 9, 0]);
%! assert_equal (seconds (split (B, 'time')), [0, 0, 10800]);
%!test
%! B = calendarDuration ([], [], []);  B(2) = calmonths (9);
%! assert_equal (size (B), [1, 2]);
%! assert_equal (calmonths (B), [0, 9]);
%!test
%! B = A;  B(2) = calendarDuration (NaN, 0, 0);
%! [mo, d, t] = split (B, {'months', 'days', 'time'});
%! assert_equal (mo, [1, NaN, 0]);
%! assert_equal (d, [0, NaN, 0]);
%! assert_equal (seconds (t), [0, NaN, 10800]);
%!error<calendarDuration.subsasgn: assignment value must be calendarDuration or duration array or a numeric array representing 24-hour days.> ...
%! A(2) = 'a'

## A Format must name 'm', 'd' and 't' and use only the 'yqmwdt' characters.
## R2024a accepts a format missing them, warns, and falls back to 'ymdt', but
## it then ignores the format it accepted, so the documented contract is kept
## here instead.
%!error<calendarDuration.subsasgn: 'Format' must contain 'm', 'd', and 't'.> ...
%! A.Format = 'ymd'
%!error<calendarDuration.subsasgn: 'Format' must only contain characters from the 'yqmwdt' sequence.> ...
%! A.Format = 'zz'

