changeset 31887:96ed8d1d0c59

mode.m: Emtpy input produces Matlab compatible NaN output (bug #48690) * mode.m: Add isempty() block to return compatible forms of all outputs. Adjust dim input validation to ensure empty, Inf, NaN and negative inputs trigger expected error. Add BISTS for empty input handling and dim input validation. * NEWS.9.md: Note changes under Matlab compatibility.
author Nicholas R. Jankowski <jankowski.nicholas@gmail.com>
date Sat, 04 Mar 2023 00:59:48 -0500
parents c3858bef069a
children f1f11a50a402
files etc/NEWS.9.md scripts/statistics/mode.m
diffstat 2 files changed, 166 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS.9.md	Fri Mar 03 16:54:26 2023 -0500
+++ b/etc/NEWS.9.md	Sat Mar 04 00:59:48 2023 -0500
@@ -38,6 +38,8 @@
 limits of single precision by processing as doubles (bug #63848).  `median`
 has also adopted the 'outtype' option from `mean`.
 
+- `mode` now produces Matlab compatible outputs for empty inputs.
+
 ### Alphabetical list of new functions added in Octave 9
 
 * `tensorprod`
--- a/scripts/statistics/mode.m	Fri Mar 03 16:54:26 2023 -0500
+++ b/scripts/statistics/mode.m	Sat Mar 04 00:59:48 2023 -0500
@@ -57,15 +57,42 @@
   sz = size (x);
   if (nargin < 2)
     ## Find the first non-singleton dimension.
-    (dim = find (sz > 1, 1)) || (dim = 1);
+    (dim = find (sz != 1, 1)) || (dim = 1);
   else
-    if (! (isscalar (dim) && dim == fix (dim) && dim > 0))
+    if (! (isscalar (dim) && dim > 0 && rem (dim, 1) == 0))
       error ("mode: DIM must be an integer and a valid dimension");
     endif
   endif
 
-  if (dim > nd)
-    ## Special case of mode over non-existent dimension.
+  if (isempty (x))
+    ## Empty x produces NaN for m, 0 for f, , but m, f and c
+    ## shape depends on size of x.
+    if ((nargin == 1) && (nd == 2) && (sz == [0 0]))
+      f = 0; # f always a double even if x is single.
+      if (isa (x, "single"))
+        m = NaN ("single");
+        c = {(NaN (0, 1, "single"))};
+      else
+        m = NaN;
+        c = {(NaN (0, 1))};
+      endif
+    else
+      sz(dim) = 1;
+      f = zeros (sz); # f always a double even if x is single.
+      c = cell (sz);
+      if (isa (x, "single"))
+        m = (NaN (sz, "single"));
+        c(:) = NaN (1, 0, "single");
+      else
+        m = NaN (sz);
+        c(:) = NaN (1, 0);
+      endif
+    endif
+    return
+  endif
+
+  if (dim > nd || sz(dim) == 1)
+    ## Special case of mode over singleton dimension.
     m = x;
     f = ones (size (x));
     c = num2cell (x);
@@ -152,6 +179,15 @@
 %! assert (f, ones (3,3));
 %! assert (c, num2cell (x));
 
+%!test
+%! x = single (magic (3));
+%! [m, f, c] = mode (x, 3);
+%! assert (class (m), "single");
+%! assert (class (f), "double");
+%! assert (class (c), "cell");
+%! assert (class (c(1)), "cell");
+%! assert (class (c{1}), "single");
+
 %!shared x
 %! x(:,:,1) = toeplitz (1:3);
 %! x(:,:,2) = circshift (toeplitz (1:3), 1);
@@ -180,6 +216,124 @@
 %! assert (c{1}, [1; 2; 3]);
 %! assert (c{2}, [1; 2; 3]);
 %! assert (c{3}, [1; 2; 3]);
+%!shared   ## Clear shared to prevent variable echo for any later test failures
+
+## Test empty inputs
+%!test <*48690>
+%! [m, f, c] = mode ([]);
+%! assert (m, NaN);
+%! assert (f, 0);
+%! assert (c, {(NaN (0, 1))});
+%!test <*48690>
+%! [m, f, c] = mode (single ([]));
+%! assert (class (m), "single");
+%! assert (class (f), "double");
+%! assert (c, {(single (NaN (0, 1)))});
+%!test <*48690>
+%! [m, f, c] = mode ([], 1);
+%! assert (m, NaN (1, 0));
+%! assert (f, zeros (1, 0));
+%! assert (c, cell (1, 0));
+%!test <*48690>
+%! [m, f, c] = mode ([], 2);
+%! assert (m, NaN (0, 1));
+%! assert (f, zeros (0, 1));
+%! assert (c, cell(0, 1));
+%!test <*48690>
+%! [m, f, c] = mode ([], 3);
+%! assert (m, []);
+%! assert (f, []);
+%! assert (c, cell (0, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 1));
+%! assert (m, NaN);
+%! assert (f, 0);
+%! assert (c, {(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 1), 1);
+%! assert (m, NaN);
+%! assert (f, 0);
+%! assert (c, {(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 1), 2);
+%! assert (m, NaN (0, 1));
+%! assert (f, zeros (0, 1));
+%! assert (c, cell (0, 1));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 1), 3);
+%! assert (m, NaN (0, 1));
+%! assert (f, zeros (0, 1));
+%! assert (c, cell (0, 1));
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 0));
+%! assert (m, NaN);
+%! assert (f, 0);
+%! assert (c, {(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 0), 1);
+%! assert (m, NaN (1, 0));
+%! assert (f, zeros (1, 0));
+%! assert (c, cell (1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 0), 2);
+%! assert (m, NaN);
+%! assert (f, 0);
+%! assert (c, {(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 0), 3);
+%! assert (m, NaN (1, 0));
+%! assert (f, zeros (1, 0));
+%! assert (c, cell (1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 0, 0));
+%! assert (m, NaN (1, 0, 0));
+%! assert (f, zeros (1, 0, 0));
+%! assert (c, cell (1, 0, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 0, 0), 1);
+%! assert (m, NaN (1, 0, 0));
+%! assert (f, zeros (1, 0, 0));
+%! assert (c, cell (1, 0, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 0, 0), 2);
+%! assert (m, NaN (0, 1, 0));
+%! assert (f, zeros (0, 1, 0));
+%! assert (c, cell (0, 1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 0, 0), 3);
+%! assert (m, NaN (0, 0, 1));
+%! assert (f, zeros (0, 0, 1));
+%! assert (c, cell (0, 0, 1));
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 5, 0), 2);
+%! assert (m, NaN (1, 1, 0));
+%! assert (f, zeros (1, 1, 0));
+%! assert (c, cell (1, 1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (5, 1, 0), 2);
+%! assert (m, NaN (5, 1, 0));
+%! assert (f, zeros (5, 1, 0));
+%! assert (c, cell (5, 1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (2, 0));
+%! assert (m, NaN (1, 0));
+%! assert (f, zeros (1, 0));
+%! assert (c, cell (1, 0));
+%!test <*48690>
+%! [m, f, c] = mode (ones (0, 2));
+%! assert (m, NaN (1, 2));
+%! assert (f, zeros (1, 2));
+%! assert (c, {(NaN (1, 0)),(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 1, 1, 0));
+%! assert (m, NaN (1, 1));
+%! assert (f, zeros (1, 1));
+%! assert (c, {(NaN (1, 0))});
+%!test <*48690>
+%! [m, f, c] = mode (ones (1, 1, 1, 0), 1);
+%! assert (m, NaN (1, 1, 1, 0));
+%! assert (f, zeros (1, 1, 1, 0));
+%! assert (c, cell (1,1,1,0));
 
 ## Test input validation
 %!error <Invalid call> mode ()
@@ -187,3 +341,9 @@
 %!error <DIM must be an integer> mode (1, ones (2,2))
 %!error <DIM must be an integer> mode (1, 1.5)
 %!error <DIM must be .* a valid dimension> mode (1, 0)
+%!error <DIM must be .* a valid dimension> mode (1, -1)
+%!error <DIM must be .* a valid dimension> mode (1, -1.5)
+%!error <DIM must be .* a valid dimension> mode (1, Inf)
+%!error <DIM must be .* a valid dimension> mode (1, NaN)
+
+