function [C, ia, ib] = setxor(A, B, varargin)
%SETXOR Set setxor for codistributed array data.
%   C = setxor(A,B)    
%   [C,IA,IB] = setxor(A,B)    
%   [C,IA,IB] = setxor(A,B,'rows')    
%   [C,IA,IB] = setxor(A,B,'stable') 
%   [C,IA,IB] = setxor(A,B,'stable','rows') 
%   [C,IA,IB] = setxor(A,B,'sorted') 
%   [C,IA,IB] = setxor(A,B,'sorted','rows') 
%   
%   Limitations:
%   The 'legacy' flag is not supported.
%   1. Table, timetable, categorical, datetime and duration inputs are not supported.
%   2. Char and string inputs are not supported when A or B is a cell array of char vectors. Convert cell array of char vectors input arguments to string instead.
%   
%   See also setxor, codistributed.


%   Copyright 2015-2023 The MathWorks, Inc.

% Allow 5 inputs so the right error can be thrown for the "legacy" flag.
narginchk(2, 5)
nargoutchk(0, 3)

% Default outputs
ia = [];
ib = [];

try
    [flags, A, B] = setfunCheckArgs(@setxor, A, B, varargin{:});
catch ME
    throwAsCaller(ME)
end

isRows = false;
if ~isempty(flags)
    isRows = any(contains(flags, "rows"));
end

% We only support 1D distributions right now. Convert rather than error.
A = pForce1DDistribution(A);
B = pForce1DDistribution(B);

% Get localia, a logical column vector true for elements (or rows) of
% localA that match B and a llocb, a logical array of size(numel(localA),
% numel(B)) or (numrows(localA), numrows(B)) with true for matches.
[localLia, ~, llocb] = ismemberImpl(A, B, isRows, "all");

[xorA, lia] = setfunExtractMatch(A, ~localLia, isRows);
if isempty(xorA)
    if isRows
        xorA = codistributed.zeros(0, size(A, 2));
    else
        xorA = codistributed.zeros(0, 1);
    end
    if isstring(A) || isstring(B)
        xorA = codistributed(string.empty(size(xorA)));
    elseif iscell(A) || iscell(B)
        xorA = cell.empty(1, 0);
    else
        xorA = cast(xorA, underlyingType(A));
    end
end

localLib = any(llocb, 1);  % True for element or row of B that has a matching
      % element or row in localA.
lib = spmdReduce(@or, localLib)';  % Logical column vector with
      % true where B matches A for all of A.

if isRows
    xorB = subsref(B, substruct('()', {~lib, ':'}));
else
    xorB = subsref(B, substruct('()', {~lib}));
    if isrow(xorA)
        xorA = xorA.';
    end
    if isrow(xorB)
        xorB = xorB.';
    end
end

if iscodistributed(xorA) && ~iscodistributed(xorB)
    xorB = codistributed(xorB, codistributor1d(1));
end

C = unique([xorA; xorB], flags{:});
if nargout > 1
    [~, xia] = unique(xorA, flags{:});
    [~, xib] = unique(xorB, flags{:});
    ia = setfunGetIndices(xia, lia);
    ib = setfunGetIndices(xib, ~lib);
end

% Output should be row if both inputs are rows.
if isrow(A) && isrow(B) && iscolumn(C) && ~isRows
    C = C.';
end

% Finally restore correct sparsity if needed.
if issparse(A) || issparse(B)
    C = sparse(C);
end
if issparse(ia)
    ia = full(ia);
end
if issparse(ib)
    ib = full(ib);
end


if ~iscodistributed(C)
    C = codistributed.pConstructFromReplicated(C); 
end
end
