changeset 27237:67e5e997a3bf

setdiff.m: Accept a "legacy" flag for Matlab compatibility. * NEWS: Announce change to setdiff return values when given "rows" argument. Announce "legacy" flag. * setdiff.m Add new calling form and explanation of "legacy" option to docstring. Allow up to 4 inputs in input validation. Check for "legacy" in input options and set variable optlegacy. Set variable isrowvec based on optlegacy and orientation of inputs. Add BIST tests for "rows" and "legacy" inputs.
author Rik <rik@octave.org>
date Thu, 11 Jul 2019 15:29:38 -0700
parents d29a12e8b6d9
children 177be3c01238
files NEWS scripts/set/setdiff.m
diffstat 2 files changed, 64 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Thu Jul 11 15:05:30 2019 -0700
+++ b/NEWS	Thu Jul 11 15:29:38 2019 -0700
@@ -53,9 +53,14 @@
   behavior, or Matlab behavior from releases prior to R2012b, can be
   obtained by using the `"legacy"` flag.
 
+- The function `setdiff` with the `"rows"` argument now returns Matlab
+  compatible results.  The previous Octave behavior, or Matlab behavior
+  from releases prior to R2012b, can be obtained by using the `"legacy"`
+  flag.
+
 - The functions `intersect`, `union` now accept a `"legacy"` flag which
   changes the index values (second and third outputs) as well as the
-  orientation of the outputs to match Matlab releases prior to R2012b.
+  orientation of all outputs to match Matlab releases prior to R2012b.
 
 - Complex RESTful web services can now be accessed by the `webread` and
   `webwrite` functions alongside with the `weboptions` structure.  One
--- a/scripts/set/setdiff.m	Thu Jul 11 15:05:30 2019 -0700
+++ b/scripts/set/setdiff.m	Thu Jul 11 15:29:38 2019 -0700
@@ -20,6 +20,7 @@
 ## -*- texinfo -*-
 ## @deftypefn  {} {@var{c} =} setdiff (@var{a}, @var{b})
 ## @deftypefnx {} {@var{c} =} setdiff (@var{a}, @var{b}, "rows")
+## @deftypefnx {} {@var{c} =} setdiff (@dots{}, "legacy")
 ## @deftypefnx {} {[@var{c}, @var{ia}] =} setdiff (@dots{})
 ## Return the unique elements in @var{a} that are not in @var{b} sorted in
 ## ascending order.
@@ -33,6 +34,10 @@
 ##
 ## If requested, return the index vector @var{ia} such that
 ## @code{@var{c} = @var{a}(@var{ia})}.
+##
+## Programming Note: The input flag @qcode{"legacy"} changes the algorithm
+## to be compatible with @sc{matlab} releases prior to R2012b.
+##
 ## @seealso{unique, union, intersect, setxor, ismember}
 ## @end deftypefn
 
@@ -41,24 +46,30 @@
 
 function [c, ia] = setdiff (a, b, varargin)
 
-  if (nargin < 2 || nargin > 3)
+  if (nargin < 2 || nargin > 4)
     print_usage ();
   endif
 
   [a, b] = validsetargs ("setdiff", a, b, varargin{:});
 
-  by_rows = nargin == 3;
-  isrowvec = isrow (a);
+  by_rows = any (strcmp ("rows", varargin));
+  optlegacy = any (strcmp ("legacy", varargin));
+
+  if (optlegacy)
+    isrowvec = ! iscolumn (a) || ! iscolumn (b);
+  else
+    isrowvec = isrow (a);
+  endif
 
   if (by_rows)
     if (nargout > 1)
-      [c, ia] = unique (a, "rows");
+      [c, ia] = unique (a, varargin{:});
     else
-      c = unique (a, "rows");
+      c = unique (a, varargin{:});
     endif
     if (! isempty (c) && ! isempty (b))
       ## Form A and B into combined set.
-      b = unique (b, "rows");
+      b = unique (b, varargin{:});
       [tmp, idx] = sortrows ([c; b]);
       ## Eliminate those elements of A that are the same as in B.
       dups = find (all (tmp(1:end-1,:) == tmp(2:end,:), 2));
@@ -69,9 +80,9 @@
     endif
   else
     if (nargout > 1)
-      [c, ia] = unique (a);
+      [c, ia] = unique (a, varargin{:});
     else
-      c = unique (a);
+      c = unique (a, varargin{:});
     endif
     if (! isempty (c) && ! isempty (b))
       ## Form a and b into combined set.
@@ -84,15 +95,18 @@
         dups = find (tmp(1:end-1) == tmp(2:end));
       endif
       c(idx(dups)) = [];
-      if (nargout > 1)
-        ia(idx(dups)) = [];
-      endif
       ## Reshape if necessary for Matlab compatibility.
       if (isrowvec)
         c = c(:).';
       else
         c = c(:);
       endif
+      if (nargout > 1)
+        ia(idx(dups)) = [];
+        if (optlegacy && isrowvec)
+          ia = ia(:).';
+        endif
+      endif
     endif
   endif
 
@@ -126,3 +140,36 @@
 %! a = rand (3,3,3);
 %! b = a(1);
 %! assert (setdiff (a, b), sort (a(2:end)'));
+
+## Test "rows" compatibility
+%!test
+%! a = [7 9 7; 0 0 0; 7 9 7; 5 5 5; 1 4 5];
+%! b = [0 0 0; 5 5 5];
+%! [c, ia] = setdiff (a, b, "rows");
+%! assert (c, [1, 4 ,5; 7, 9 7]);
+%! assert (ia, [5; 1]);
+
+## Test "legacy" option
+%!test
+%! a = [3, 6, 2, 1, 5, 1, 1];
+%! b = [2, 4, 6];
+%! [c, ia] = setdiff (a, b);
+%! assert (c, [1, 3, 5]);
+%! assert (ia, [4; 1; 5]);
+%! [c, ia] = setdiff (a, b, "legacy");
+%! assert (c, [1, 3, 5]);
+%! assert (ia, [7, 1, 5]);
+
+## "legacy" + "rows" compatibility
+%!test
+%! a = [7 9 7; 0 0 0; 7 9 7; 5 5 5; 1 4 5];
+%! b = [0 0 0; 5 5 5];
+%! [c, ia] = setdiff (a, b, "rows", "legacy");
+%! assert (c, [1, 4 ,5; 7, 9 7]);
+%! assert (ia, [5; 3]);
+
+## Output orientation with "legacy" option 
+%!assert (size (setdiff ([1:5], [2:3], "legacy")), [1, 3])
+%!assert (size (setdiff ([1:5]', [2:3], "legacy")), [1, 3])
+%!assert (size (setdiff ([1:5], [2:3]', "legacy")), [1, 3])
+%!assert (size (setdiff ([1:5]', [2:3]', "legacy")), [3, 1])