# HG changeset patch # User Rik # Date 1562805172 25200 # Node ID 255f2681d224d46468fedb9beb585435a201fe16 # Parent c80681b4948d0992016c2a961e2771389a994b5b intersect.m: Accept a "legacy" flag for Matlab compatibility. * NEWS: Announce change. * intersect.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. Only use two-input form of sort or sortrows when number of outputs of intersect function is greater than 1. After calculation, change orientation of outputs as required based on optlegacy. Add BIST tests to test output orientation and values. * validsetargs.m: Modify to accept a variable number of option arguments as last input (varargin). Use switch statement to validate possible options including accepting "legacy". diff -r c80681b4948d -r 255f2681d224 NEWS --- a/NEWS Wed Jul 10 17:25:39 2019 -0700 +++ b/NEWS Wed Jul 10 17:32:52 2019 -0700 @@ -53,6 +53,10 @@ behavior, or Matlab behavior from releases prior to R2012b, can be obtained by using the `"legacy"` flag. +- The function `intersect` now accepts 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. + - Complex RESTful web services can now be accessed by the `webread` and `webwrite` functions alongside with the `weboptions` structure. One major feature is the support for cookies to enable RESTful diff -r c80681b4948d -r 255f2681d224 scripts/set/intersect.m --- a/scripts/set/intersect.m Wed Jul 10 17:25:39 2019 -0700 +++ b/scripts/set/intersect.m Wed Jul 10 17:32:52 2019 -0700 @@ -20,6 +20,7 @@ ## -*- texinfo -*- ## @deftypefn {} {@var{c} =} intersect (@var{a}, @var{b}) ## @deftypefnx {} {@var{c} =} intersect (@var{a}, @var{b}, "rows") +## @deftypefnx {} {@var{c} =} intersect (@dots{}, "legacy") ## @deftypefnx {} {[@var{c}, @var{ia}, @var{ib}] =} intersect (@dots{}) ## ## Return the unique elements common to both @var{a} and @var{b} sorted in @@ -32,15 +33,19 @@ ## If the optional input @qcode{"rows"} is given then return the common rows of ## @var{a} and @var{b}. The inputs must be 2-D matrices to use this option. ## -## If requested, return index vectors @var{ia} and @var{ib} such that +## If requested, return column index vectors @var{ia} and @var{ib} such that ## @code{@var{c} = @var{a}(@var{ia})} and @code{@var{c} = @var{b}(@var{ib})}. ## +## Programming Note: The input flag @qcode{"legacy"} changes the shape of the +## outputs (@var{c}, @var{i}, @var{j} to row vectors whenever at least one of +## the inputs is a row vector. +## ## @seealso{unique, union, setdiff, setxor, ismember} ## @end deftypefn function [c, ia, ib] = intersect (a, b, varargin) - if (nargin < 2 || nargin > 3) + if (nargin < 2 || nargin > 4) print_usage (); endif @@ -58,13 +63,21 @@ endif ia = ib = []; else - by_rows = nargin == 3; - isrowvec = isrow (a) && isrow (b); + by_rows = any (strcmp ("rows", varargin)); + optlegacy = any (strcmp ("legacy", varargin)); + + if (optlegacy) + isrowvec = ! iscolumn (a) || ! iscolumn (b); + else + isrowvec = isrow (a) && isrow (b); + endif ## Form A and B into sets if (nargout > 1) [a, ja] = unique (a, varargin{:}); + ja = ja(:); [b, jb] = unique (b, varargin{:}); + jb = jb(:); else a = unique (a, varargin{:}); b = unique (b, varargin{:}); @@ -72,13 +85,21 @@ if (by_rows) c = [a; b]; - [c, ic] = sortrows (c); + if (nargout > 1) + [c, ic] = sortrows (c); + else + c = sortrows (c); + endif ii = find (all (c(1:end-1,:) == c(2:end,:), 2)); c = c(ii,:); len_a = rows (a); else c = [a(:); b(:)]; - [c, ic] = sort (c); # [a(:);b(:)](ic) == c + if (nargout > 1) + [c, ic] = sort (c); # [a(:);b(:)](ic) == c + else + c = sort (c); + endif if (iscellstr (c)) ii = find (strcmp (c(1:end-1), c(2:end))); else @@ -88,15 +109,20 @@ len_a = length (a); endif + ## Adjust output orientation for Matlab compatibility + if (isrowvec) + c = c.'; + endif + if (nargout > 1) ia = ja(ic(ii)); # a(ia) == c ib = jb(ic(ii+1) - len_a); # b(ib) == c + if (optlegacy && isrowvec) + ia = ia.'; + ib = ib.'; + endif endif - ## Adjust output orientation for Matlab compatibility - if (! by_rows && isrowvec) - c = c.'; - endif endif endfunction @@ -107,10 +133,17 @@ %! a = 1:4; %! b = 2:5; -%!assert (size (intersect (a, b)), [1 3]) -%!assert (size (intersect (a', b)), [3 1]) -%!assert (size (intersect (a, b')), [3 1]) -%!assert (size (intersect (a', b')), [3 1]) +%!assert (size (intersect (a, b)), [1, 3]) +%!assert (size (intersect (a', b)), [3, 1]) +%!assert (size (intersect (a, b')), [3, 1]) +%!assert (size (intersect (a', b')), [3, 1]) +%!assert (size (intersect (a, b, "legacy")), [1, 3]) +%!assert (size (intersect (a', b, "legacy")), [1, 3]) +%!assert (size (intersect (a, b', "legacy")), [1, 3]) +%!assert (size (intersect (a', b', "legacy")), [3, 1]) + +## Clear shared variables +%!shared ## Test multi-dimensional arrays %!test @@ -150,6 +183,19 @@ %! assert (ia, [1:3]'); %! assert (ib, [1:3]'); +## Test "legacy" argument +%!test +%! a = [7 1 7 7 4]; +%! b = [7 0 4 4 0]; +%! [c, ia, ib] = intersect (a, b); +%! assert (c, [4, 7]); +%! assert (ia, [5; 1]); +%! assert (ib, [3; 1]); +%! [c, ia, ib] = intersect (a, b, "legacy"); +%! assert (c, [4, 7]); +%! assert (ia, [5, 4]); +%! assert (ib, [4, 1]); + ## Test return type of empty intersections %!assert (intersect (['a', 'b'], {}), {}) %!assert (intersect ([], {'a', 'b'}), {}) diff -r c80681b4948d -r 255f2681d224 scripts/set/private/validsetargs.m --- a/scripts/set/private/validsetargs.m Wed Jul 10 17:25:39 2019 -0700 +++ b/scripts/set/private/validsetargs.m Wed Jul 10 17:32:52 2019 -0700 @@ -19,7 +19,7 @@ ## Validate arguments for binary set operation. -function [x, y] = validsetargs (caller, x, y, byrows_arg) +function [x, y] = validsetargs (caller, x, y, varargin) isallowedarraytype = @(x) isnumeric (x) || ischar (x) || islogical (x); @@ -42,21 +42,29 @@ error ("%s: A and B must be arrays or cell arrays of strings", caller); endif elseif (nargin == 4) - if (! strcmpi (byrows_arg, "rows")) - error ("%s: invalid option: %s", caller, byrows_arg); - endif + for arg = varargin + switch (arg{1}) + case "legacy" + ## Accepted option, do nothing. - if (iscell (x) || iscell (y)) - error ('%s: cells not supported with "rows"', caller); - elseif (! (isallowedarraytype (x) && isallowedarraytype (y))) - error ("%s: A and B must be arrays or cell arrays of strings", caller); - else - if (ndims (x) > 2 || ndims (y) > 2) - error ('%s: A and B must be 2-dimensional matrices for "rows"', caller); - elseif (columns (x) != columns (y) && ! (isempty (x) || isempty (y))) - error ("%s: number of columns in A and B must match", caller); - endif - endif + case "rows" + if (iscell (x) || iscell (y)) + error ('%s: cells not supported with "rows"', caller); + elseif (! (isallowedarraytype (x) && isallowedarraytype (y))) + error ("%s: A and B must be arrays or cell arrays of strings", caller); + else + if (ndims (x) > 2 || ndims (y) > 2) + error ('%s: A and B must be 2-dimensional matrices for "rows"', caller); + elseif (columns (x) != columns (y) && ! (isempty (x) || isempty (y))) + error ("%s: number of columns in A and B must match", caller); + endif + endif + + otherwise + error ("%s: invalid option: %s", caller, byrows_arg); + + endswitch + endfor endif endfunction