########################################################################
##
## Copyright (C) 2000-2020 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or .
##
## 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
## .
##
########################################################################
## -*- texinfo -*-
## @deftypefn {} {} uniquetol (@var{x}, @var{tol})
## @deftypefnx {} {} uniquetol (@var{x})
## @deftypefnx {} {} uniquetol (@var{x}, @var{tol}, "rows")
## @deftypefnx {} {} uniquetol (@dots{}, "prop1", "value1", @dots{})
## @deftypefnx {} {[@var{C}, @var{IA}, @var{IC}] =} uniquetol (@dots{})
## @deftypefnx {} {[@var{C}, @var{IA}, @var{IC}] =} uniquetol (@dots{}, "OutputAllIndices", true)
##
## Return the unique elements of @var{x} using tolerance tol.
## Two values, u and v, are within tolerance if abs(u-v) <= tol*max(abs(A(:)))
##
## If the tolerance is not provided:
##
## tol=1e-12 for double precision inputs
##
## tol=1e-6 for single precision inputs
##
## If double or single precision inputs are not provided unique is called
## without any additional options and a warning issued.
##
## If the input @var{x} is a column vector then return a column vector;
## Otherwise, return a row vector.
##
## If the optional argument @qcode{"rows"} is given then return the unique
## rows of @var{x}, @qcode{"ByRows"}, true does the same thing.
##
## If requested, return column index vectors @var{IA} and @var{IC} such that
## @code{@var{y} = @var{x}(@var{IA})} and @code{@var{x} = @var{y}(@var{IC})}.
##
## If the option "OutputAllIndices", true is provided @var{IA} is a cell array
## that contains the indices for all elements in A that are within tolerance of
## a value in C. That is, each cell in IA corresponds to a value in C, and the
## values in each cell correspond to locations in A.
##
## If the option "DataScale", DS, is provided the tolerance test is changed to
## abs(u-v) <= tol*DS where DS is a scalar unless the "ByRows" option is true,
## then it is either a scalar or a vector with a length equal to the number of
## rows.
##
## Example :
##
## @example
## @group
## x = (1:6)*pi;
## y = 10.^log10(x);
## C = uniquetol([x, y])
## @result{} [3.1416, 6.2832, 9.4248, 12.5664, 15.7080, 18.8496]
## D = unique([x, y])
## @result{} [3.1416, 6.2832, 9.4248, 12.5664, 12.5664, 15.7080,
## 18.8496, 18.8496]
## @end group
## @end example
##
## @seealso{unique, union, intersect, setdiff, setxor, ismember}
## @end deftypefn
function [C,IA,IC]=uniquetol (A,varargin)
if (nargin < 1)
print_usage ();
endif
if (isempty (A))
C = A;
IA = [];
IC = [];
return
endif
if (!isnumeric (A) || !(isa (A, "double") || isa (A, "single")))
warning(["uniquetol: input is not a numeric, double or single precision "...
"array calling unique without any additional options."]);
[C,IA,IC]=unique (A);
return
endif
maxabsA = max (abs (A(!isinf (A))(:)));
ByRows = false;
OutputAllIndices = false;
DataScale = maxabsA;
sizeA = size (A);
if (nargin ()==1 || !(isnumeric (varargin{1}) && isscalar (varargin{1})))
if (isa (A, "double"))
tol = 1e-12;
elseif (isa (A, "single"))
tol = 1e-6;
else
error ("uniquetol: Precision of A must be double or single.");
endif
else
tol = varargin{1};
endif
for k = 1:length(varargin)
if (isnumeric (varargin{k}))
continue
elseif (ischar (varargin{k}))
if strcmpi (varargin{k}, "rows")
ByRows = true;
elseif (strcmpi (varargin{k}, "byrows"))
ByRows = logical (varargin{k+1});
elseif (strcmpi (varargin{k}, "OutputAllIndices"))
OutputAllIndices = logical (varargin{k+1});
elseif (strcmpi (varargin{k}, "DataScale"))
DataScale = varargin{k+1}(:).';
columnsDataScale = columns (DataScale);
if (!(columnsDataScale == 1 || columnsDataScale == sizeA(2)))
error ("uniquetol: Incorrect size of data scale.");
endif
endif
endif
endfor
if (!ByRows)
A = A(:);
endif
points = rows (A);
d = columns (A);
Iall = zeros (points,1);
I = nan (d,1);
IA = {};
J = nan (d,1);
j=1;
ii=0;
tolDataScale = tol * DataScale;
for i=1:points
if (any (Iall == i))
continue
else
equ = all (abs (A - A(i,:)) <= tolDataScale,2);
equ(i,1) = equ(i,1) || any (isnan (A(i,:)) | isinf (A(i,:)),2);
sumeq=sum (equ);
ia = find (equ);
if (OutputAllIndices)
IA{end+1} = ia;
endif
Iall(ii+(1:sumeq)) = ia;
I(j) = ia(1);
J(equ) = j;
ii+= sumeq;
j+= 1;
endif
endfor
I(isnan (I)) = [];
J(isnan (J)) = [];
C = A(I,:);
if (sizeA(1) == 1 && sizeA(2) > 1 && !ByRows)
C = C.';
I = I.';
J = J.';
endif
if (!OutputAllIndices)
IA = I(1:j-1);
endif
IC = J;
endfunction
%!assert (uniquetol ([1 1 2; 1 2 1; 1 1 2]), [1;2])
%!assert (uniquetol ([1 1 2; 1 0 1; 1 1 2], 1e-12, "rows"), [1 1 2; 1 0 1])
%!assert (uniquetol ([]), [])
%!assert (uniquetol ([1]), [1])
%!assert (uniquetol ([1 2]), [1 2])
%!assert (uniquetol ([1;2]), [1;2])
%!assert (uniquetol ([1,NaN,Inf,NaN,Inf]), [1,NaN,Inf,NaN,Inf])
%!assert (uniquetol (zeros (1,0)), zeros (1,0))
%!assert (uniquetol (zeros (1,0), 1e-12, "rows"), zeros (1,0))
%!assert (uniquetol ([1,2,2,3,2,4], 1e-12, "rows"), [1,2,2,3,2,4])
%!assert (uniquetol ([1,2,2,3,2,4]), [1,2,3,4])
%!assert (uniquetol ([1,2,2,3,2,4]', 1e-12, "rows"), [1;2;3;4])
%!assert (uniquetol (sparse ([2,0;2,0])), sparse([2;0]))
%!assert (uniquetol (sparse ([1,2;2,3])), sparse([1;2;3]))
%!assert (uniquetol ([1,2,2,3,2,4]', 1e-12, "rows"), [1;2;3;4])
%!assert (uniquetol (single ([1,2,2,3,2,4]), 1e-12, "rows"), single ([1,2,2,3,2,4]))
%!assert (uniquetol (single ([1,2,2,3,2,4])), single ([1,2,3,4]))
%!assert (uniquetol (single ([1,2,2,3,2,4]'), 1e-12, "rows"), single ([1;2;3;4]))
%!assert (unique (uint8 ([1,2,2,3,2,4])), uint8 ([1,2,3,4]))
%!test
%! [y,i,j] = uniquetol ([1,1,2,3,3,3,4]);
%! assert (y, [1,2,3,4]);
%! assert (i, [1;3;4;7]);
%! assert (j, [1;1;2;3;3;3;4]);
%!test
%! A = [2, 3, 4; 2, 3, 4];
%! [y,i,j] = uniquetol (A, "rows");
%! assert (y, [2, 3, 4]);
%! assert (A(i,:), y);
%! assert (y(j,:), A);
%!test
%! x = (2:7)'*pi;
%! y = exp (1).^log (x);
%! C = uniquetol ([x;y]);
%! assert (C, x);
%!test
%! A = [0.06 0.21 0.38; 0.38 0.21 0.39; 0.54 0.56 0.41; 0.46 0.52 0.95];
%! B = log (exp (1).^A);
%! C = uniquetol ([A;B], 'rows');
%! assert (C, A);
%!test
%! A = [0.06 0.21 0.38; 0.38 0.21 0.39; 0.54 0.56 0.41; 0.46 0.52 0.95];
%! B = log (exp (1).^A);
%! C = uniquetol ([A;B], 'ByRows', true);
%! assert (C, A);
%!test
%! A = [.1 .2 .3 10];
%! [C,IA,IC] = uniquetol (A,.1,'OutputAllIndices',true);
%! assert (C, [.1, 10]);
%! assert (IA, {(1:3)',4});
%! assert (IC, [1;1;1;2]);
%!test
%! x = 10^11;
%! C = uniquetol ([x, exp(log(x))],1e-6,'DataScale',1);
%! assert (C, [x, exp(log(x))]);
%!error uniquetol ()