changeset 33279:308060bc6b27 stable

cross: Improve input validation and expand BISTs. (bug #65544) * cross.m: Add input validation check for input dim to ensure it's a real integer valued numeric scalar. Change vector check from columns and rows count to iscolumn and isrow check to avoid passing 1-by-n-by-m and n-by-1-by-m arrays through to transpose operator. Move size and ndims check earlier to reduce repeated function calls. Reduce large BISTs tolerances on simple tests from 2e-8 to eps. Change docstring mention of matrices to arrays, add note about error if sz(dim) != 3, and add additional example. Add BISTs to verify dim input validation and to catch vector transpose check. Add BISTs for all error messages and warnings and remove FIXME note requesting the same. * NEWS.9.md: Note change to dim input validation.
author Nicholas R. Jankowski <jankowski.nicholas@gmail.com>
date Sun, 31 Mar 2024 22:48:01 -0400
parents 32669ebfd773
children c2ab6aba3cdb 2aea823a9973
files etc/NEWS.9.md scripts/linear-algebra/cross.m
diffstat 2 files changed, 72 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS.9.md	Sat Mar 30 11:01:24 2024 -0400
+++ b/etc/NEWS.9.md	Sun Mar 31 22:48:01 2024 -0400
@@ -8,6 +8,9 @@
 - `hist.m`: Add input validation for `Y` restricting it to 2-D array
   (bug #65478).
 
+- `cross.m`: Add input validation for 'dim' restricting it to a numeric
+  integer valued scalar.
+
 ### GUI
 
 - Use first word for options in right-click menu of command window widget
--- a/scripts/linear-algebra/cross.m	Sat Mar 30 11:01:24 2024 -0400
+++ b/scripts/linear-algebra/cross.m	Sun Mar 31 22:48:01 2024 -0400
@@ -29,11 +29,12 @@
 ## Compute the vector cross product of two 3-dimensional vectors @var{x} and
 ## @var{y}.
 ##
-## If @var{x} and @var{y} are matrices, the cross product is applied along the
+## If @var{x} and @var{y} are arrays, the cross product is applied along the
 ## first dimension with three elements.
 ##
 ## The optional argument  @var{dim} forces the cross product to be calculated
-## along the specified dimension.
+## along the specified dimension.  An error will be produced if the specified
+## dimension is not three elements in size.
 ##
 ## Example Code:
 ##
@@ -41,7 +42,17 @@
 ## @group
 ## cross ([1, 1, 0], [0, 1, 1])
 ##   @result{}
-##        1   -1   1
+##        1  -1   1
+## @end group
+## @end example
+##
+## @example
+## @group
+## cross (magic (3), eye (3), 2)
+##   @result{}
+##        0   6  -1
+##       -7   0   3
+##        9  -4   0
 ## @end group
 ## @end example
 ##
@@ -54,32 +65,39 @@
     print_usage ();
   endif
 
-  if (ndims (x) < 3 && ndims (y) < 3 && nargin < 3)
+  nd = ndims (x);
+
+  if (nd < 3 && ndims (y) < 3 && nargin < 3)
     ## COMPATIBILITY -- opposite behavior for cross(row,col)
     ## Swap x and y in the assignments below to get the matlab behavior.
     ## Better yet, fix the calling code so that it uses conformant vectors.
-    if (columns (x) == 1 && rows (y) == 1)
+    if (iscolumn (x) && isrow (y))
       warning ("cross: taking cross product of column by row");
       y = y.';
-    elseif (rows (x) == 1 && columns (y) == 1)
+    elseif (isrow (x) && iscolumn (y))
       warning ("cross: taking cross product of row by column");
       x = x.';
     endif
   endif
 
+  sz = size (x);
+
   if (nargin == 2)
-     dim = find (size (x) == 3, 1);
+     dim = find (sz == 3, 1);
      if (isempty (dim))
        error ("cross: must have at least one dimension with 3 elements");
      endif
-   else
-     if (size (x, dim) != 3)
-       error ("cross: dimension DIM must have 3 elements");
-     endif
+  else
+    if (! (isnumeric (dim) && dim > 0 && isreal (dim) && ...
+          isscalar (dim) && dim == fix (dim)))
+      error ("cross: DIM must be a positive scalar whole number");
+    endif
+
+    if (dim > nd || sz(dim) != 3)
+      error ("cross: must have three elements in dimension DIM");
+    endif
   endif
 
-  nd = ndims (x);
-  sz = size (x);
   idx2 = idx3 = idx1 = {':'}(ones (1, nd));
   idx1(dim) = 1;
   idx2(dim) = 2;
@@ -101,26 +119,48 @@
 
 
 %!test
-%! x = [1 0 0];
-%! y = [0 1 0];
-%! r = [0 0 1];
-%! assert (cross (x, y), r, 2e-8);
+%! x = [1, 0, 0];
+%! y = [0, 1, 0];
+%! r = [0, 0, 1];
+%! assert (cross (x, y), r, eps);
+
+%!test
+%! x = [1, 2, 3];
+%! y = [4, 5, 6];
+%! r = [(2*6-3*5), (3*4-1*6), (1*5-2*4)];
+%! assert (cross (x, y), r, eps);
 
 %!test
-%! x = [1 2 3];
-%! y = [4 5 6];
-%! r = [(2*6-3*5) (3*4-1*6) (1*5-2*4)];
-%! assert (cross (x, y), r, 2e-8);
+%! x = [1, 0, 0; 0, 1, 0; 0, 0, 1];
+%! y = [0, 1, 0; 0, 0, 1; 1, 0, 0];
+%! r = [0, 0, 1; 1, 0, 0; 0, 1, 0];
+%! assert (cross (x, y, 2), r, eps);
+%! assert (cross (x, y, 1), -r, eps);
 
-%!test
-%! x = [1 0 0; 0 1 0; 0 0 1];
-%! y = [0 1 0; 0 0 1; 1 0 0];
-%! r = [0 0 1; 1 0 0; 0 1 0];
-%! assert (cross (x, y, 2), r, 2e-8);
-%! assert (cross (x, y, 1), -r, 2e-8);
+%!test <*65527>
+%! x = cat (3, [1, 1, 1]', [1, 1, 1]');
+%! y = cat (3, [1, 0, 0], [1, 0, 0]);
+%! fail ("cross (x, y)", "X and Y must have the same dimensions");
+%! fail ("cross (y, x)", "X and Y must have the same dimensions");
 
 ## Test input validation
 %!error <Invalid call> cross ()
 %!error <Invalid call> cross (1)
-## FIXME: Need tests for other error() conditions and warning() calls.
-%!error <must have at least one dimension with 3 elements> cross (0,0)
+%!error <must have at least one dimension with 3 elements> cross (0, 0)
+%!error <must have at least one dimension with 3 elements> cross ([1, 2], [3, 4])
+%!error <must have at least one dimension with 3 elements> cross ([1, 2], [3, 4, 5])
+%!error <must have three elements in dimension DIM> cross (0, 0, 1)
+%!error <must have three elements in dimension DIM> cross ([1, 2, 3], [1, 2, 3], 1)
+%!error <must have three elements in dimension DIM> cross ([1, 2, 3], [1, 2, 3], 9)
+%!error <must have three elements in dimension DIM> cross (magic (3), magic (3), 4)
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], {1})
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], "a")
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], true)
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], [1, 2])
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 0)
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], -1)
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 1.5)
+%!error <DIM must be a positive scalar whole number> cross ([1, 2, 3], [4, 5, 6], 2i)
+%!error <X and Y must have the same dimensions> cross ([1, 2, 3], [3, 4])
+%!warning <taking cross product of column by row> cross ([1, 2, 3]', [4, 5, 6]);
+%!warning <taking cross product of row by column> cross ([1, 2, 3], [4, 5, 6]');