1######################################################################## 2## 3## Copyright (C) 2000-2021 The Octave Project Developers 4## 5## See the file COPYRIGHT.md in the top-level directory of this 6## distribution or <https://octave.org/copyright/>. 7## 8## This file is part of Octave. 9## 10## Octave is free software: you can redistribute it and/or modify it 11## under the terms of the GNU General Public License as published by 12## the Free Software Foundation, either version 3 of the License, or 13## (at your option) any later version. 14## 15## Octave is distributed in the hope that it will be useful, but 16## WITHOUT ANY WARRANTY; without even the implied warranty of 17## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18## GNU General Public License for more details. 19## 20## You should have received a copy of the GNU General Public License 21## along with Octave; see the file COPYING. If not, see 22## <https://www.gnu.org/licenses/>. 23## 24######################################################################## 25 26## -*- texinfo -*- 27## @deftypefn {} {} isequaln (@var{x1}, @var{x2}, @dots{}) 28## Return true if all of @var{x1}, @var{x2}, @dots{} are equal under the 29## additional assumption that NaN == NaN (no comparison of NaN placeholders 30## in dataset). 31## @seealso{isequal} 32## @end deftypefn 33 34## Algorithm: 35## 36## 1. Verify the class of x. 37## a. All objects are of the same class 38## b. All objects are of a generic "numeric" class which includes 39## numeric, logical, and character arrays 40## 2. Verify size of all objects match. 41## 3. Convert objects to struct, and then compare as stated below. 42## 4. For each argument after x, compare it for equality with x: 43## a. char compare each member with strcmp 44## b. numeric compare each member with '==', and assume NaN == NaN 45## c. struct compare number of fieldnames, value of fieldnames, 46## and then each field with isequaln (recursive) 47## d. cellstr compare each cellstr member with strcmp 48## e. cell compare each member with isequaln (recursive) 49## f. fcn_handle compare using overloaded "eq" operator 50 51function t = isequaln (x, varargin) 52 53 if (nargin < 2) 54 print_usage (); 55 endif 56 57 nvarargin = nargin - 1; 58 two_args = (nvarargin == 1); # Optimization for base case of just 2 args 59 60 if (two_args) 61 y = varargin{1}; # alias y to second input for comparison 62 endif 63 64 ############################################################ 65 ## Generic tests for equality 66 67 ## All arguments must either be of the same class, 68 ## or they must be "numeric" values. 69 if (two_args) 70 t = (strcmp (class (x), class (y)) 71 || ((isreal (x) || iscomplex (x)) && (isreal (y) || iscomplex (y)))); 72 else 73 t = (all (cellfun ("isclass", varargin, class (x))) 74 || ((isreal (x) || iscomplex (x)) 75 && all (cellfun ("isreal", varargin) 76 | cellfun ("isnumeric", varargin)))); 77 endif 78 79 ## Test that everything is the same size (which also tests dimensions) 80 if (t) 81 t = size_equal (x, varargin{:}); 82 endif 83 84 ## From here on, compare any objects as if they were structures. 85 if (t && isobject (x)) 86 ## Locally suppress class-to-struct warning. We know what we are doing. 87 warning ("off", "Octave:classdef-to-struct", "local"); 88 x = builtin ("struct", x); 89 if (two_args) 90 clear y; # break link to existing variable 91 varargin(1) = builtin ("struct", varargin{1}); 92 y = varargin{1}; # re-alias y to second input 93 else 94 for i = 1:nvarargin 95 varargin(i) = builtin ("struct", varargin{i}); 96 endfor 97 endif 98 endif 99 100 ############################################################ 101 ## Check individual classes. 102 103 if (t) 104 if (two_args) 105 106 if (ischar (x) && ischar (y)) 107 ## char type. Optimization, strcmp is ~35% faster than '==' operator. 108 t = strcmp (x, y); 109 110 elseif (isreal (x) || iscomplex (x)) 111 ## general "numeric" type. Use '==' operator. 112 m = (x == y); 113 t = all (m(:)); 114 115 if (! t && isfloat (x) && isfloat (y)) 116 t = isnan (x(! m)) && isnan (y(! m)); 117 endif 118 119 elseif (isstruct (x)) 120 ## struct type. Compare # of fields, fieldnames, then field values. 121 122 ## Test number of fields are equal. 123 t = (numfields (x) == numfields (y)); 124 125 ## Test that all the field names are equal. 126 if (t) 127 s_fnm_x = sort (fieldnames (x)); 128 t = all (strcmp (s_fnm_x, sort (fieldnames (y)))); 129 endif 130 131 ## Test that all field values are equal. Slow because of recursion. 132 if (t) 133 if (isscalar (x)) 134 for fldnm = s_fnm_x.' 135 t = isequaln (x.(fldnm{1}), y.(fldnm{1})); 136 if (! t) 137 break; 138 endif 139 endfor 140 else 141 ## struct arrays have to have the contents of each field wrapped 142 ## in a cell since it expands to a collection of values. 143 for fldnm = s_fnm_x.' 144 t = isequaln ({x.(fldnm{1})}, {y.(fldnm{1})}); 145 if (! t) 146 break; 147 endif 148 endfor 149 endif 150 endif 151 152 elseif (iscellstr (x) && iscellstr (y)) 153 ## cellstr type. Optimization over cell type by using strcmp. 154 ## FIXME: It would be faster to use strcmp on whole cellstr arrays, 155 ## but bug #51412 needs to be fixed. Instead, time/space trade-off. 156 ## Convert to char (space) for faster processing with strcmp (time). 157 t = strcmp (char (x), char (y)); 158 159 elseif (iscell (x)) 160 ## cell type. Check that each element of a cell is equal. Slow. 161 n = numel (x); 162 idx = 1; 163 while (t && idx <= n) 164 t = isequaln (x{idx}, y{idx}); 165 idx += 1; 166 endwhile 167 168 elseif (is_function_handle (x)) 169 ## function type. Use '==' operator which is overloaded. 170 t = (x == y); 171 172 else 173 error ("isequaln: Impossible to reach code. File a bug report."); 174 175 endif 176 177 else ## More than two args. This is going to be slower in general. 178 179 if (ischar (x) && all (cellfun ("isclass", varargin, "char"))) 180 ## char type. Optimization, strcmp is ~35% faster than '==' operator. 181 idx = 1; 182 while (t && idx <= nvarargin) 183 t = strcmp (x, varargin{idx}); 184 idx += 1; 185 endwhile 186 187 elseif (isreal (x) || iscomplex (x)) 188 ## general "numeric" type. Use '==' operator. 189 190 idx = 1; 191 while (t && idx <= nvarargin) 192 y = varargin{idx}; 193 m = (x == y); 194 t = all (m(:)); 195 196 if (! t && isfloat (x) && isfloat (y)) 197 t = isnan (x(! m)) && isnan (y(! m)); 198 endif 199 200 idx += 1; 201 endwhile 202 203 elseif (isstruct (x)) 204 ## struct type. Compare # of fields, fieldnames, then field values. 205 206 ## Test number of fields are equal. 207 fnm_x = fieldnames (x); 208 n = numel (fnm_x); 209 fnm_v = cellfun ("fieldnames", varargin, "uniformoutput", false); 210 t = all (n == cellfun ("numel", fnm_v)); 211 212 ## Test that all the field names are equal. 213 if (t) 214 fnm_x = sort (fnm_x); 215 idx = 1; 216 while (t && idx <= nvarargin) 217 ## Allow the fieldnames to be in a different order. 218 t = all (strcmp (fnm_x, sort (fnm_v{idx}))); 219 idx += 1; 220 endwhile 221 endif 222 223 ## Test that all field values are equal. Slow because of recursion. 224 if (t) 225 args = cell (1, 1 + nvarargin); 226 if (isscalar (x)) 227 for fldnm = fnm_x.' 228 args{1} = x.(fldnm{1}); 229 for argn = 1:nvarargin 230 args{argn+1} = varargin{argn}.(fldnm{1}); 231 endfor 232 233 t = isequaln (args{:}); 234 235 if (! t) 236 break; 237 endif 238 endfor 239 else 240 ## struct arrays have to have the contents of each field wrapped 241 ## in a cell since it expands to a collection of values. 242 for fldnm = fnm_x.' 243 args{1} = { x.(fldnm{1}) }; 244 for argn = 1:nvarargin 245 args{argn+1} = { varargin{argn}.(fldnm{1}) }; 246 endfor 247 248 t = isequaln (args{:}); 249 250 if (! t) 251 break; 252 endif 253 endfor 254 endif 255 endif 256 257 elseif (iscellstr (x) && all (cellfun (@iscellstr, varargin))) 258 ## cellstr type. Optimization over cell type by using strcmp. 259 ## FIXME: It would be faster to use strcmp on whole cellstr arrays, 260 ## but bug #51412 needs to be fixed. Instead, time/space trade-off. 261 ## Convert to char (space) for faster processing with strcmp (time). 262 idx = 1; 263 x = char (x); 264 while (t && idx <= nvarargin) 265 t = strcmp (x, char (varargin{idx})); 266 idx += 1; 267 endwhile 268 269 elseif (iscell (x)) 270 ## cell type. Check that each element of a cell is equal. Slow. 271 n = numel (x); 272 args = cell (1, 1 + nvarargin); 273 idx = 1; 274 while (t && idx <= n) 275 args(1) = x{idx}; 276 args(2:end) = [cellindexmat(varargin, idx){:}]; 277 278 t = isequaln (args{:}); 279 280 idx += 1; 281 endwhile 282 283 elseif (is_function_handle (x)) 284 ## function type. Use '==' operator which is overloaded. 285 t = all (cellfun ("eq", {x}, varargin)); 286 287 else 288 error ("isequaln: Impossible to reach code. File a bug report."); 289 290 endif 291 292 endif 293 endif 294 295 t = full (t); # Always return full logical value for Matlab compatibility. 296 297endfunction 298 299 300## test for equality 301%!assert (isequaln (1,1), true) 302%!assert (isequaln (1,1,1), true) 303%!assert (isequaln ({1,2,NaN,4}, {1,2,NaN,4}), true) 304%!assert (isequaln ({1,2,NaN,4}, {1,2,NaN,4}, {1,2,NaN,4}), true) 305%!assert (isequaln ([1,2,NaN,4], [1,2,NaN,4]), true) 306%!assert (isequaln ([1,2,NaN,4], [1,2,NaN,4], [1,2,NaN,4]), true) 307## test for inequality 308%!assert (isequaln (1,2), false) 309%!assert (isequaln (1,1,2), false) 310%!assert (isequaln ([1,2,NaN,4], [1,NaN,3,4]), false) 311%!assert (isequaln ([1,2,NaN,4], [1,2,NaN,4], [1,NaN,3,4]), false) 312%!assert (isequaln ([1,2,NaN,4], [1,2,3,4]), false) 313%!assert (isequaln ([1,2,NaN,4], [1,2,NaN,4], [1,2,3,4]), false) 314## test for equality (struct) 315%!shared st 316%! st = struct ("a",NaN,"b",2); 317%!assert (isequaln (st, st), true) 318%!assert (isequaln (st, st, st), true) 319 320## Input validation 321%!error isequaln () 322%!error isequaln (1) 323