view scripts/set/intersect.m @ 27226:6eb32f0aea87

Implement "legacy" flag for intersect function (bug #56592). * NEWS: Announce change to default intersect behavior. * intersect.m: Update documentation. Parse input options looking for "legacy" and set variable optlegacy if found. Record shape of input in variable isrowvec. When processing outputs 2,3 look for combination of optlegacy and isrowvec to decide if output should be transposed. Update BIST tests to pass with new behavior and add aditional test for "legacy" option. * union.m, unique.m: Update BIST tests to pass.
author Rik <rik@octave.org>
date Wed, 10 Jul 2019 13:48:01 -0700
parents 00f796120a6d
children 255f2681d224
line wrap: on
line source

## Copyright (C) 2000-2019 Paul Kienzle
## Copyright (C) 2008-2009 Jaroslav Hajek
##
## This file is part of Octave.
##
## Octave 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.
##
## Octave 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 Octave; see the file COPYING.  If not, see
## <https://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {} {@var{c} =} intersect (@var{a}, @var{b})
## @deftypefnx {} {@var{c} =} intersect (@var{a}, @var{b}, "rows")
## @deftypefnx {} {[@var{c}, @var{ia}, @var{ib}] =} intersect (@dots{})
##
## Return the unique elements common to both @var{a} and @var{b} sorted in
## ascending order.
##
## If @var{a} and @var{b} are both row vectors then return a row vector;
## Otherwise, return a column vector.  The inputs may also be cell arrays of
## strings.
##
## 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
## @code{@var{c} = @var{a}(@var{ia})} and @code{@var{c} = @var{b}(@var{ib})}.
##
## @seealso{unique, union, setdiff, setxor, ismember}
## @end deftypefn

function [c, ia, ib] = intersect (a, b, varargin)

  if (nargin < 2 || nargin > 3)
    print_usage ();
  endif

  [a, b] = validsetargs ("intersect", a, b, varargin{:});

  if (isempty (a) || isempty (b))
    ## Special case shortcuts algorithm.
    ## Lots of type checking required for Matlab compatibility.
    if (isnumeric (a) && isnumeric (b))
      c = [];
    elseif (iscell (b))
      c = {};
    else
      c = "";
    endif
    ia = ib = [];
  else
    by_rows = nargin == 3;
    isrowvec = isrow (a) && isrow (b);

    ## Form A and B into sets
    if (nargout > 1)
      [a, ja] = unique (a, varargin{:});
      [b, jb] = unique (b, varargin{:});
    else
      a = unique (a, varargin{:});
      b = unique (b, varargin{:});
    endif

    if (by_rows)
      c = [a; b];
      [c, ic] = sortrows (c);
      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 (iscellstr (c))
        ii = find (strcmp (c(1:end-1), c(2:end)));
      else
        ii = find (c(1:end-1) == c(2:end));
      endif
      c = c(ii);
      len_a = length (a);
    endif

    if (nargout > 1)
      ia = ja(ic(ii));            # a(ia) == c
      ib = jb(ic(ii+1) - len_a);  # b(ib) == c
    endif

    ## Adjust output orientation for Matlab compatibility
    if (! by_rows && isrowvec)
      c = c.';
    endif
  endif

endfunction


## Test orientation of output
%!shared a,b
%! 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])

## Test multi-dimensional arrays
%!test
%! a = rand (3,3,3);
%! b = a;
%! b(1,1,1) = 2;
%! assert (intersect (a, b), sort (a(2:end)'));

## Test the routine for index vectors ia and ib
%!test
%! a = [3 2 4 5 7 6 5 1 0 13 13];
%! b = [3 5 12 1 1 7];
%! [c,ia,ib] = intersect (a, b);
%! assert (c, [1, 3, 5, 7]);
%! assert (ia, [8; 1; 4; 5]);
%! assert (ib, [4; 1; 2; 6]);
%! assert (a(ia), c);
%! assert (b(ib), c);
%!test
%! a = [1,1,2;1,4,5;2,1,7];
%! b = [1,4,5;2,3,4;1,1,2;9,8,7];
%! [c,ia,ib] = intersect (a, b, "rows");
%! assert (c, [1,1,2;1,4,5]);
%! assert (ia, [1;2]);
%! assert (ib, [3;1]);
%! assert (a(ia,:), c);
%! assert (b(ib,:), c);
%!test
%! a = [1 1 1 2 2 2];
%! b = [1 2 3 4 5 6];
%! c = intersect (a, b);
%! assert(c, [1,2]);
%!test
%! a = [1 2 3 4; 5 6 7 8; 9 10 11 12];
%! [b, ia, ib] = intersect (a, a, "rows");
%! assert (b, a);
%! assert (ia, [1:3]');
%! assert (ib, [1:3]');

## Test return type of empty intersections
%!assert (intersect (['a', 'b'], {}), {})
%!assert (intersect ([], {'a', 'b'}), {})
%!assert (intersect ([], {}), {})
%!assert (intersect ({'a', 'b'}, []), {})
%!assert (intersect ([], ['a', 'b']), "")
%!assert (intersect ({}, []), {})
%!assert (intersect (['a', 'b'], []), "")