/src/r-source/src/main/relop.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 1997--2025 The R Core Team |
4 | | * Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, a copy is available at |
18 | | * https://www.R-project.org/Licenses/ |
19 | | */ |
20 | | |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | #include <config.h> |
24 | | #endif |
25 | | |
26 | | #include <Defn.h> |
27 | | #include <Internal.h> |
28 | | #include <Rmath.h> |
29 | | #include <errno.h> |
30 | | #include <R_ext/Itermacros.h> |
31 | | |
32 | | /* interval at which to check interrupts, a guess */ |
33 | | #define NINTERRUPT 10000000 |
34 | | |
35 | | static SEXP numeric_relop(RELOP_TYPE code, SEXP s1, SEXP s2); |
36 | | static SEXP complex_relop(RELOP_TYPE code, SEXP s1, SEXP s2, SEXP call); |
37 | | static SEXP string_relop (RELOP_TYPE code, SEXP s1, SEXP s2); |
38 | | static SEXP raw_relop (RELOP_TYPE code, SEXP s1, SEXP s2); |
39 | | |
40 | 1.70k | #define DO_SCALAR_RELOP(oper, x, y) do { \ |
41 | 1.70k | switch (oper) { \ |
42 | 468 | case EQOP: return ScalarLogical((x) == (y)); \ |
43 | 0 | case NEOP: return ScalarLogical((x) != (y)); \ |
44 | 443 | case LTOP: return ScalarLogical((x) < (y)); \ |
45 | 477 | case GTOP: return ScalarLogical((x) > (y)); \ |
46 | 32 | case LEOP: return ScalarLogical((x) <= (y)); \ |
47 | 281 | case GEOP: return ScalarLogical((x) >= (y)); \ |
48 | 1.70k | } \ |
49 | 1.70k | } while (0) |
50 | | |
51 | | attribute_hidden SEXP do_relop(SEXP call, SEXP op, SEXP args, SEXP env) |
52 | 5.09k | { |
53 | 5.09k | SEXP ans, arg1, arg2; |
54 | 5.09k | int argc; |
55 | | |
56 | 5.09k | if (args != R_NilValue && |
57 | 5.09k | CDR(args) != R_NilValue && |
58 | 5.09k | CDDR(args) == R_NilValue) |
59 | 5.09k | argc = 2; |
60 | 0 | else |
61 | 0 | argc = length(args); |
62 | 5.09k | arg1 = CAR(args); |
63 | 5.09k | arg2 = CADR(args); |
64 | | |
65 | 5.09k | if (ATTRIB(arg1) != R_NilValue || ATTRIB(arg2) != R_NilValue) { |
66 | 0 | if (DispatchGroup("Ops", call, op, args, env, &ans)) |
67 | 0 | return ans; |
68 | 0 | } |
69 | | |
70 | 5.09k | if (argc != 2) |
71 | 0 | error("operator needs two arguments"); |
72 | | |
73 | 5.09k | return do_relop_dflt(call, op, arg1, arg2); |
74 | 5.09k | } |
75 | | |
76 | 0 | #define IS_SCALAR_STRING(x) (TYPEOF(x) == STRSXP && XLENGTH(x) == 1) |
77 | | #define SYMBOL_STRING_MATCH(x, y) \ |
78 | 0 | (isSymbol(x) && IS_SCALAR_STRING(y) && Seql(PRINTNAME(x), STRING_ELT(y, 0))) |
79 | | |
80 | | |
81 | | static R_INLINE bool compute_lang_equal(SEXP x, SEXP y) |
82 | 0 | { |
83 | 0 | if (isSymbol(x)) |
84 | 0 | return y == x || |
85 | 0 | (IS_SCALAR_STRING(y) && Seql(PRINTNAME(x), STRING_ELT(y, 0))); |
86 | 0 | else if (isSymbol(y)) |
87 | 0 | return x == y || |
88 | 0 | (IS_SCALAR_STRING(x) && Seql(STRING_ELT(x, 0), PRINTNAME(y))); |
89 | | |
90 | 0 | if (TYPEOF(x) == LANGSXP && ATTRIB(x) != R_NilValue) |
91 | 0 | x = LCONS(CAR(x), CDR(x)); |
92 | 0 | PROTECT(x); |
93 | 0 | if (TYPEOF(y) == LANGSXP && ATTRIB(y) != R_NilValue) |
94 | 0 | y = LCONS(CAR(y), CDR(y)); |
95 | 0 | PROTECT(y); |
96 | |
|
97 | 0 | bool val = R_compute_identical(x, y, 16); |
98 | 0 | UNPROTECT(2); |
99 | 0 | return val; |
100 | 0 | } |
101 | | |
102 | | static SEXP compute_language_relop(SEXP call, SEXP op, SEXP x, SEXP y) |
103 | 0 | { |
104 | 0 | static enum { |
105 | 0 | UNINITIALIZED, |
106 | 0 | EQONLY, |
107 | 0 | IDENTICAL_CALLS, |
108 | 0 | IDENTICAL_CALLS_ATTR, |
109 | 0 | IDENTICAL, |
110 | 0 | ERROR_CALLS, |
111 | 0 | ERROR |
112 | 0 | } option = UNINITIALIZED; |
113 | |
|
114 | 0 | if (option == UNINITIALIZED) { |
115 | 0 | option = EQONLY; |
116 | 0 | const char *val = getenv("_R_COMPARE_LANG_OBJECTS"); |
117 | 0 | if (val != NULL) { |
118 | 0 | if (strcmp(val, "eqonly") == 0) |
119 | 0 | option = EQONLY; |
120 | 0 | else if (strcmp(val, "identical_calls") == 0) |
121 | 0 | option = IDENTICAL_CALLS; |
122 | 0 | else if (strcmp(val, "identical_calls_attr") == 0) |
123 | 0 | option = IDENTICAL_CALLS_ATTR; |
124 | 0 | else if (strcmp(val, "identical") == 0) |
125 | 0 | option = IDENTICAL; |
126 | 0 | else if (strcmp(val, "error_calls") == 0) |
127 | 0 | option = ERROR_CALLS; |
128 | 0 | else if (strcmp(val, "error") == 0) |
129 | 0 | option = ERROR; |
130 | 0 | } |
131 | 0 | } |
132 | |
|
133 | 0 | switch(option) { |
134 | 0 | case EQONLY: |
135 | 0 | switch(PRIMVAL(op)) { |
136 | 0 | case EQOP: return NULL; |
137 | 0 | case NEOP: return NULL; |
138 | 0 | default: |
139 | 0 | errorcall(call, |
140 | 0 | _("comparison (%s) is not possible for language types"), |
141 | 0 | PRIMNAME(op)); |
142 | 0 | } |
143 | 0 | case IDENTICAL_CALLS: |
144 | | /* this should reproduce the current behavior of == and != for |
145 | | language objects, while signaling errors for <, <=, >, and |
146 | | >=. */ |
147 | 0 | switch(PRIMVAL(op)) { |
148 | 0 | case EQOP: |
149 | 0 | return compute_lang_equal(x, y) ? R_TrueValue : R_FalseValue; |
150 | 0 | case NEOP: |
151 | 0 | return compute_lang_equal(x, y) ? R_FalseValue : R_TrueValue; |
152 | 0 | default: |
153 | 0 | errorcall(call, |
154 | 0 | _("comparison (%s) is not possible for language types"), |
155 | 0 | PRIMNAME(op)); |
156 | 0 | } |
157 | 0 | case IDENTICAL_CALLS_ATTR: |
158 | 0 | if (isSymbol(x) && IS_SCALAR_STRING(y)) |
159 | 0 | y = Seql(STRING_ELT(y, 0), PRINTNAME(x)) ? x : R_NilValue; |
160 | 0 | else if (isSymbol(y) && IS_SCALAR_STRING(x)) |
161 | 0 | x = Seql(STRING_ELT(x, 0), PRINTNAME(y)) ? y : R_NilValue; |
162 | 0 | switch(PRIMVAL(op)) { |
163 | 0 | case EQOP: |
164 | 0 | return R_compute_identical(x, y, 16) ? R_TrueValue : R_FalseValue; |
165 | 0 | case NEOP: |
166 | 0 | return R_compute_identical(x, y, 16) ? R_FalseValue : R_TrueValue; |
167 | 0 | default: |
168 | 0 | errorcall(call, |
169 | 0 | _("comparison (%s) is not possible for language types"), |
170 | 0 | PRIMNAME(op)); |
171 | 0 | } |
172 | 0 | case IDENTICAL: |
173 | 0 | if (SYMBOL_STRING_MATCH(x, y) || SYMBOL_STRING_MATCH(y, x)) |
174 | | /* identical(x, y) and the default x == y implementation |
175 | | would disagree, so signal an error instead */ |
176 | 0 | errorcall(call, |
177 | 0 | _("comparing this symbol and string pair " |
178 | 0 | "is not supported")); |
179 | 0 | switch(PRIMVAL(op)) { |
180 | 0 | case EQOP: |
181 | 0 | return R_compute_identical(x, y, 16) ? R_TrueValue : R_FalseValue; |
182 | 0 | case NEOP: |
183 | 0 | return R_compute_identical(x, y, 16) ? R_FalseValue : R_TrueValue; |
184 | 0 | default: errorcall(call, |
185 | 0 | _("comparison (%s) is not possible for language types"), |
186 | 0 | PRIMNAME(op)); |
187 | 0 | } |
188 | 0 | case ERROR_CALLS: |
189 | 0 | if (TYPEOF(x) == LANGSXP || TYPEOF(y) == LANGSXP) |
190 | 0 | errorcall(call, _("comparison of call objects is not supported")); |
191 | 0 | return NULL; |
192 | 0 | case ERROR: |
193 | 0 | errorcall(call, _("comparison of language objects is not supported")); |
194 | 0 | default: return NULL; |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | | // also called from cmp_relop() in eval.c : |
199 | | attribute_hidden SEXP do_relop_dflt(SEXP call, SEXP op, SEXP x, SEXP y) |
200 | 1.71M | { |
201 | | /* handle the REALSXP/INTSXP simple scalar case quickly */ |
202 | 1.71M | if (IS_SIMPLE_SCALAR(x, INTSXP)) { |
203 | 980 | int ix = SCALAR_IVAL(x); |
204 | 980 | if (IS_SIMPLE_SCALAR(y, INTSXP)) { |
205 | 916 | int iy = SCALAR_IVAL(y); |
206 | 916 | if (ix == NA_INTEGER || iy == NA_INTEGER) |
207 | 0 | return ScalarLogical(NA_LOGICAL); |
208 | 916 | DO_SCALAR_RELOP(PRIMVAL(op), ix, iy); |
209 | 916 | } |
210 | 64 | else if (IS_SIMPLE_SCALAR(y, REALSXP)) { |
211 | 64 | double dy = SCALAR_DVAL(y); |
212 | 64 | if (ix == NA_INTEGER || ISNAN(dy)) |
213 | 0 | return ScalarLogical(NA_LOGICAL); |
214 | 64 | DO_SCALAR_RELOP(PRIMVAL(op), ix, dy); |
215 | 64 | } |
216 | 980 | } |
217 | 1.71M | else if (IS_SIMPLE_SCALAR(x, REALSXP)) { |
218 | 1.18k | double dx = SCALAR_DVAL(x); |
219 | 1.18k | if (IS_SIMPLE_SCALAR(y, INTSXP)) { |
220 | 436 | int iy = SCALAR_IVAL(y); |
221 | 436 | if (ISNAN(dx) || iy == NA_INTEGER) |
222 | 0 | return ScalarLogical(NA_LOGICAL); |
223 | 436 | DO_SCALAR_RELOP(PRIMVAL(op), dx, iy); |
224 | 436 | } |
225 | 744 | else if (IS_SIMPLE_SCALAR(y, REALSXP)) { |
226 | 308 | double dy = SCALAR_DVAL(y); |
227 | 308 | if (ISNAN(dx) || ISNAN(dy)) |
228 | 23 | return ScalarLogical(NA_LOGICAL); |
229 | 285 | DO_SCALAR_RELOP(PRIMVAL(op), dx, dy); |
230 | 285 | } |
231 | 1.18k | } |
232 | | |
233 | 1.71M | R_xlen_t |
234 | 1.71M | nx = xlength(x), |
235 | 1.71M | ny = xlength(y); |
236 | 1.71M | SEXPTYPE |
237 | 1.71M | typex = TYPEOF(x), |
238 | 1.71M | typey = TYPEOF(y); |
239 | | |
240 | | /* handle the REALSXP/INTSXP simple vector/scalar case quickly. */ |
241 | 1.71M | if (ATTRIB(x) == R_NilValue && ATTRIB(y) == R_NilValue && |
242 | 1.70M | (typex == REALSXP || typex == INTSXP) && |
243 | 231k | (typey == REALSXP || typey == INTSXP) && |
244 | 231k | nx > 0 && ny > 0 && (nx == 1 || ny == 1)) { |
245 | | |
246 | 177k | PROTECT(x); |
247 | 177k | PROTECT(y); |
248 | 177k | SEXP ans; |
249 | 177k | ans = numeric_relop(PRIMVAL(op), x, y); |
250 | 177k | UNPROTECT(2); |
251 | 177k | return ans; |
252 | 177k | } |
253 | | |
254 | | /* handle the general case */ |
255 | 1.53M | PROTECT_INDEX xpi, ypi; |
256 | 1.53M | PROTECT_WITH_INDEX(x, &xpi); |
257 | 1.53M | PROTECT_WITH_INDEX(y, &ypi); |
258 | | |
259 | 1.53M | if (isSymbol(x) || TYPEOF(x) == LANGSXP || |
260 | 1.53M | isSymbol(y) || TYPEOF(y) == LANGSXP) { |
261 | 0 | SEXP ans = compute_language_relop(call, op, x, y); |
262 | 0 | if (ans != NULL) { |
263 | 0 | UNPROTECT(2); |
264 | 0 | return ans; |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | 1.53M | bool iS; |
269 | | /* That symbols and calls were allowed was undocumented prior to |
270 | | R 2.5.0. We deparse them as deparse() would, minus attributes */ |
271 | 1.53M | if ((iS = isSymbol(x)) || TYPEOF(x) == LANGSXP) { |
272 | 0 | SEXP tmp = allocVector(STRSXP, 1); |
273 | 0 | PROTECT(tmp); |
274 | 0 | SET_STRING_ELT(tmp, 0, (iS) ? PRINTNAME(x) : |
275 | 0 | STRING_ELT(deparse1line_ex(x, false, |
276 | 0 | DEFAULTDEPARSE | DIGITS17), |
277 | 0 | 0)); |
278 | 0 | REPROTECT(x = tmp, xpi); |
279 | 0 | nx = xlength(x); |
280 | 0 | UNPROTECT(1); |
281 | 0 | } |
282 | 1.53M | if ((iS = isSymbol(y)) || TYPEOF(y) == LANGSXP) { |
283 | 0 | SEXP tmp = allocVector(STRSXP, 1); |
284 | 0 | PROTECT(tmp); |
285 | 0 | SET_STRING_ELT(tmp, 0, (iS) ? PRINTNAME(y) : |
286 | 0 | STRING_ELT(deparse1line_ex(y, false, |
287 | 0 | DEFAULTDEPARSE | DIGITS17), |
288 | 0 | 0)); |
289 | 0 | REPROTECT(y = tmp, ypi); |
290 | 0 | ny = xlength(y); |
291 | 0 | UNPROTECT(1); |
292 | 0 | } |
293 | | |
294 | 1.53M | if (isNull(x)) REPROTECT(x = allocVector(INTSXP,0), xpi); |
295 | 1.53M | if (isNull(y)) REPROTECT(y = allocVector(INTSXP,0), ypi); |
296 | 1.53M | if (!isVector(x) || !isVector(y)) |
297 | 0 | errorcall(call, |
298 | 0 | _("comparison (%s) is possible only for atomic and list types"), |
299 | 0 | PRIMNAME(op)); |
300 | | |
301 | | #ifdef previous_R_versions |
302 | | if (TYPEOF(x) == EXPRSXP || TYPEOF(y) == EXPRSXP) |
303 | | errorcall(call, _("comparison is not allowed for expressions")); |
304 | | #endif |
305 | | |
306 | | /* ELSE : x and y are both atomic or list */ |
307 | | |
308 | 1.53M | bool |
309 | 1.53M | xarray = isArray(x), |
310 | 1.53M | yarray = isArray(y), |
311 | 1.53M | xts = isTs(x), |
312 | 1.53M | yts = isTs(y); |
313 | 1.53M | SEXP dims, xnames, ynames; |
314 | 1.53M | if (xarray || yarray) { |
315 | 5.57k | if (xarray && yarray) { |
316 | 0 | if (!conformable(x, y)) |
317 | 0 | errorcall(call, _("non-conformable arrays")); |
318 | 0 | PROTECT(dims = getAttrib(x, R_DimSymbol)); |
319 | 0 | } |
320 | 5.57k | else if (xarray && (ny != 0 || nx == 0)) { |
321 | 5.57k | PROTECT(dims = getAttrib(x, R_DimSymbol)); |
322 | 5.57k | } |
323 | 0 | else if (yarray && (nx != 0 || ny == 0)) { |
324 | 0 | PROTECT(dims = getAttrib(y, R_DimSymbol)); |
325 | 0 | } else |
326 | 0 | PROTECT(dims = R_NilValue); |
327 | | |
328 | 5.57k | PROTECT(xnames = getAttrib(x, R_DimNamesSymbol)); |
329 | 5.57k | PROTECT(ynames = getAttrib(y, R_DimNamesSymbol)); |
330 | 5.57k | } |
331 | 1.53M | else { |
332 | 1.53M | PROTECT(dims = R_NilValue); |
333 | 1.53M | PROTECT(xnames = getAttrib(x, R_NamesSymbol)); |
334 | 1.53M | PROTECT(ynames = getAttrib(y, R_NamesSymbol)); |
335 | 1.53M | } |
336 | | |
337 | 1.53M | SEXP klass = NULL, tsp = NULL; // -Wall |
338 | 1.53M | if (xts || yts) { |
339 | 0 | if (xts && yts) { |
340 | | /* could check ts conformance here */ |
341 | 0 | PROTECT(tsp = getAttrib(x, R_TspSymbol)); |
342 | 0 | PROTECT(klass = getAttrib(x, R_ClassSymbol)); |
343 | 0 | } |
344 | 0 | else if (xts) { |
345 | 0 | if (xlength(x) < xlength(y)) |
346 | 0 | ErrorMessage(call, ERROR_TSVEC_MISMATCH); |
347 | 0 | PROTECT(tsp = getAttrib(x, R_TspSymbol)); |
348 | 0 | PROTECT(klass = getAttrib(x, R_ClassSymbol)); |
349 | 0 | } |
350 | 0 | else /*(yts)*/ { |
351 | 0 | if (xlength(y) < xlength(x)) |
352 | 0 | ErrorMessage(call, ERROR_TSVEC_MISMATCH); |
353 | 0 | PROTECT(tsp = getAttrib(y, R_TspSymbol)); |
354 | 0 | PROTECT(klass = getAttrib(y, R_ClassSymbol)); |
355 | 0 | } |
356 | 0 | } |
357 | | |
358 | 1.53M | if (nx > 0 && ny > 0) { |
359 | 1.46M | if(((nx > ny) ? nx % ny : ny % nx) != 0) // mismatch |
360 | 0 | warningcall(call, _( |
361 | 0 | "longer object length is not a multiple of shorter object length")); |
362 | | |
363 | 1.46M | if (isString(x) || isString(y)) { |
364 | 1.44M | REPROTECT(x = coerceVector(x, STRSXP), xpi); |
365 | 1.44M | REPROTECT(y = coerceVector(y, STRSXP), ypi); |
366 | 1.44M | x = string_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
367 | 1.44M | } |
368 | 15.4k | else if (isComplex(x) || isComplex(y)) { |
369 | 0 | REPROTECT(x = coerceVector(x, CPLXSXP), xpi); |
370 | 0 | REPROTECT(y = coerceVector(y, CPLXSXP), ypi); |
371 | 0 | x = complex_relop((RELOP_TYPE) PRIMVAL(op), x, y, call); |
372 | 0 | } |
373 | 15.4k | else if ((isNumeric(x) || isLogical(x)) && (isNumeric(y) || isLogical(y))) { |
374 | 15.4k | x = numeric_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
375 | 15.4k | } // rest of cases only apply when 'x' or 'y' is raw |
376 | 0 | else if (isReal(x) || isReal(y)) { |
377 | 0 | REPROTECT(x = coerceVector(x, REALSXP), xpi); |
378 | 0 | REPROTECT(y = coerceVector(y, REALSXP), ypi); |
379 | 0 | x = numeric_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
380 | 0 | } |
381 | 0 | else if (isInteger(x) || isInteger(y)) { |
382 | 0 | REPROTECT(x = coerceVector(x, INTSXP), xpi); |
383 | 0 | REPROTECT(y = coerceVector(y, INTSXP), ypi); |
384 | 0 | x = numeric_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
385 | 0 | } |
386 | 0 | else if (isLogical(x) || isLogical(y)) { |
387 | 0 | REPROTECT(x = coerceVector(x, LGLSXP), xpi); |
388 | 0 | REPROTECT(y = coerceVector(y, LGLSXP), ypi); |
389 | 0 | x = numeric_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
390 | 0 | } |
391 | 0 | else if (TYPEOF(x) == RAWSXP || TYPEOF(y) == RAWSXP) { |
392 | 0 | REPROTECT(x = coerceVector(x, RAWSXP), xpi); |
393 | 0 | REPROTECT(y = coerceVector(y, RAWSXP), ypi); |
394 | 0 | x = raw_relop((RELOP_TYPE) PRIMVAL(op), x, y); |
395 | 0 | } else errorcall(call, _("comparison of these types is not implemented")); |
396 | 1.46M | } else { // nx == 0 || ny == 0 |
397 | 75.9k | x = allocVector(LGLSXP, 0); |
398 | 75.9k | } |
399 | | |
400 | 1.53M | PROTECT(x); |
401 | 1.53M | if (dims != R_NilValue) { |
402 | 5.57k | setAttrib(x, R_DimSymbol, dims); |
403 | 5.57k | if (xnames != R_NilValue) |
404 | 5.57k | setAttrib(x, R_DimNamesSymbol, xnames); |
405 | 0 | else if (ynames != R_NilValue) |
406 | 0 | setAttrib(x, R_DimNamesSymbol, ynames); |
407 | 5.57k | } |
408 | 1.53M | else { |
409 | 1.53M | if (xnames != R_NilValue && xlength(x) == xlength(xnames)) |
410 | 2.98k | setAttrib(x, R_NamesSymbol, xnames); |
411 | 1.52M | else if (ynames != R_NilValue && xlength(x) == xlength(ynames)) |
412 | 0 | setAttrib(x, R_NamesSymbol, ynames); |
413 | 1.53M | } |
414 | 1.53M | if (xts || yts) { |
415 | 0 | setAttrib(x, R_TspSymbol, tsp); |
416 | 0 | setAttrib(x, R_ClassSymbol, klass); |
417 | 0 | UNPROTECT(2); |
418 | 0 | } |
419 | | |
420 | 1.53M | UNPROTECT(6); |
421 | 1.53M | return x; |
422 | 1.53M | } |
423 | | |
424 | | #define ISNA_INT(x) x == NA_INTEGER |
425 | | |
426 | 192k | #define NR_HELPER(OP, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2) do { \ |
427 | 192k | type1 x1, *px1 = ACCESSOR1(s1); \ |
428 | 192k | type2 x2, *px2 = ACCESSOR2(s2); \ |
429 | 192k | int *pa = LOGICAL(ans); \ |
430 | 192k | MOD_ITERATE2(n, n1, n2, i, i1, i2, { \ |
431 | 192k | x1 = px1[i1]; \ |
432 | 192k | x2 = px2[i2]; \ |
433 | 192k | if (ISNA1(x1) || ISNA2(x2)) \ |
434 | 192k | pa[i] = NA_LOGICAL; \ |
435 | 192k | else \ |
436 | 192k | pa[i] = (x1 OP x2); \ |
437 | 192k | }); \ |
438 | 192k | } while (0) |
439 | | |
440 | 192k | #define NUMERIC_RELOP(type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2) do { \ |
441 | 192k | switch (code) { \ |
442 | 56.0k | case EQOP: \ |
443 | 56.0k | NR_HELPER(==, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
444 | 56.0k | break; \ |
445 | 4.17k | case NEOP: \ |
446 | 4.17k | NR_HELPER(!=, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
447 | 4.17k | break; \ |
448 | 2.96k | case LTOP: \ |
449 | 2.96k | NR_HELPER(<, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
450 | 2.96k | break; \ |
451 | 122k | case GTOP: \ |
452 | 122k | NR_HELPER(>, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
453 | 122k | break; \ |
454 | 6.81k | case LEOP: \ |
455 | 6.81k | NR_HELPER(<=, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
456 | 6.81k | break; \ |
457 | 37 | case GEOP: \ |
458 | 37 | NR_HELPER(>=, type1, ACCESSOR1, ISNA1, type2, ACCESSOR2, ISNA2); \ |
459 | 37 | break; \ |
460 | 192k | } \ |
461 | 192k | } while(0) |
462 | | |
463 | | static SEXP numeric_relop(RELOP_TYPE code, SEXP s1, SEXP s2) |
464 | 192k | { |
465 | 192k | R_xlen_t i, i1, i2, n, n1, n2; |
466 | 192k | SEXP ans; |
467 | | |
468 | 192k | n1 = XLENGTH(s1); |
469 | 192k | n2 = XLENGTH(s2); |
470 | 192k | n = (n1 > n2) ? n1 : n2; |
471 | 192k | PROTECT(s1); |
472 | 192k | PROTECT(s2); |
473 | 192k | ans = allocVector(LGLSXP, n); |
474 | | |
475 | 192k | if (isInteger(s1) || isLogical(s1)) { |
476 | 184k | if (isInteger(s2) || isLogical(s2)) { |
477 | 179k | NUMERIC_RELOP(int, INTEGER, ISNA_INT, int, INTEGER, ISNA_INT); |
478 | 179k | } else { |
479 | 4.28k | NUMERIC_RELOP(int, INTEGER, ISNA_INT, double, REAL, ISNAN); |
480 | 4.28k | } |
481 | 184k | } else if (isInteger(s2) || isLogical(s2)) { |
482 | 3.68k | NUMERIC_RELOP(double, REAL, ISNAN, int, INTEGER, ISNA_INT); |
483 | 4.72k | } else { |
484 | 4.72k | NUMERIC_RELOP(double, REAL, ISNAN, double, REAL, ISNAN); |
485 | 4.72k | } |
486 | | |
487 | 192k | UNPROTECT(2); |
488 | 192k | return ans; |
489 | 192k | } |
490 | | |
491 | | static SEXP complex_relop(RELOP_TYPE code, SEXP s1, SEXP s2, SEXP call) |
492 | 0 | { |
493 | 0 | R_xlen_t i, i1, i2, n, n1, n2; |
494 | 0 | Rcomplex x1, x2; |
495 | 0 | SEXP ans; |
496 | |
|
497 | 0 | if (code != EQOP && code != NEOP) { |
498 | 0 | errorcall(call, _("invalid comparison with complex values")); |
499 | 0 | } |
500 | |
|
501 | 0 | n1 = XLENGTH(s1); |
502 | 0 | n2 = XLENGTH(s2); |
503 | 0 | n = (n1 > n2) ? n1 : n2; |
504 | 0 | PROTECT(s1); |
505 | 0 | PROTECT(s2); |
506 | 0 | ans = allocVector(LGLSXP, n); |
507 | |
|
508 | 0 | const Rcomplex *px1 = COMPLEX_RO(s1); |
509 | 0 | const Rcomplex *px2 = COMPLEX_RO(s2); |
510 | 0 | int *pa = LOGICAL(ans); |
511 | |
|
512 | 0 | switch (code) { |
513 | 0 | case EQOP: |
514 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
515 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
516 | 0 | x1 = px1[i1]; |
517 | 0 | x2 = px2[i2]; |
518 | 0 | if (ISNAN(x1.r) || ISNAN(x1.i) || |
519 | 0 | ISNAN(x2.r) || ISNAN(x2.i)) |
520 | 0 | pa[i] = NA_LOGICAL; |
521 | 0 | else |
522 | 0 | pa[i] = (x1.r == x2.r && x1.i == x2.i); |
523 | 0 | }); |
524 | 0 | break; |
525 | 0 | case NEOP: |
526 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
527 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
528 | 0 | x1 = px1[i1]; |
529 | 0 | x2 = px2[i2]; |
530 | 0 | if (ISNAN(x1.r) || ISNAN(x1.i) || |
531 | 0 | ISNAN(x2.r) || ISNAN(x2.i)) |
532 | 0 | pa[i] = NA_LOGICAL; |
533 | 0 | else |
534 | 0 | pa[i] = (x1.r != x2.r || x1.i != x2.i); |
535 | 0 | }); |
536 | 0 | break; |
537 | 0 | default: |
538 | | /* never happens (-Wall) */ |
539 | 0 | break; |
540 | 0 | } |
541 | 0 | UNPROTECT(2); |
542 | 0 | return ans; |
543 | 0 | } |
544 | | |
545 | | |
546 | | /* POSIX allows EINVAL when one of the strings contains characters |
547 | | outside the collation domain. */ |
548 | | static SEXP string_relop(RELOP_TYPE code, SEXP s1, SEXP s2) |
549 | 1.44M | { |
550 | 1.44M | R_xlen_t i, n, n1, n2, res, i1, i2; |
551 | 1.44M | SEXP ans, c1, c2; |
552 | 1.44M | const void *vmax = vmaxget(); // for Scollate |
553 | | |
554 | 1.44M | n1 = XLENGTH(s1); |
555 | 1.44M | n2 = XLENGTH(s2); |
556 | 1.44M | n = (n1 > n2) ? n1 : n2; |
557 | 1.44M | PROTECT(s1); |
558 | 1.44M | PROTECT(s2); |
559 | 1.44M | PROTECT(ans = allocVector(LGLSXP, n)); |
560 | 1.44M | int *pa = LOGICAL(ans); |
561 | | |
562 | 1.44M | switch (code) { |
563 | 1.25M | case EQOP: |
564 | 1.25M | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
565 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
566 | 1.25M | c1 = STRING_ELT(s1, i1); |
567 | 1.25M | c2 = STRING_ELT(s2, i2); |
568 | 1.25M | if (c1 == NA_STRING || c2 == NA_STRING) |
569 | 1.25M | pa[i] = NA_LOGICAL; |
570 | 1.25M | else |
571 | 1.25M | pa[i] = Seql(c1, c2) ? 1 : 0; |
572 | 1.25M | }); |
573 | 1.25M | break; |
574 | 188k | case NEOP: |
575 | 188k | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
576 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
577 | 188k | c1 = STRING_ELT(s1, i1); |
578 | 188k | c2 = STRING_ELT(s2, i2); |
579 | 188k | if (c1 == NA_STRING || c2 == NA_STRING) |
580 | 188k | pa[i] = NA_LOGICAL; |
581 | 188k | else |
582 | 188k | pa[i] = Seql(c1, c2) ? 0 : 1; |
583 | 188k | }); |
584 | 188k | break; |
585 | 0 | case LTOP: |
586 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
587 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
588 | 0 | c1 = STRING_ELT(s1, i1); |
589 | 0 | c2 = STRING_ELT(s2, i2); |
590 | 0 | if (c1 == NA_STRING || c2 == NA_STRING) |
591 | 0 | pa[i] = NA_LOGICAL; |
592 | 0 | else if (c1 == c2) |
593 | 0 | pa[i] = 0; |
594 | 0 | else { |
595 | 0 | errno = 0; |
596 | 0 | res = Scollate(c1, c2); |
597 | 0 | if(errno) |
598 | 0 | pa[i] = NA_LOGICAL; |
599 | 0 | else |
600 | 0 | pa[i] = (res < 0) ? 1 : 0; |
601 | 0 | } |
602 | 0 | }); |
603 | 0 | break; |
604 | 0 | case GTOP: |
605 | 0 | MOD_ITERATE2(n, n1, n2, i ,i1, i2, { |
606 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
607 | 0 | c1 = STRING_ELT(s1, i1); |
608 | 0 | c2 = STRING_ELT(s2, i2); |
609 | 0 | if (c1 == NA_STRING || c2 == NA_STRING) |
610 | 0 | pa[i] = NA_LOGICAL; |
611 | 0 | else if (c1 == c2) |
612 | 0 | pa[i] = 0; |
613 | 0 | else { |
614 | 0 | errno = 0; |
615 | 0 | res = Scollate(c1, c2); |
616 | 0 | if(errno) |
617 | 0 | pa[i] = NA_LOGICAL; |
618 | 0 | else |
619 | 0 | pa[i] = (res > 0) ? 1 : 0; |
620 | 0 | } |
621 | 0 | }); |
622 | 0 | break; |
623 | 0 | case LEOP: |
624 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
625 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
626 | 0 | c1 = STRING_ELT(s1, i1); |
627 | 0 | c2 = STRING_ELT(s2, i2); |
628 | 0 | if (c1 == NA_STRING || c2 == NA_STRING) |
629 | 0 | pa[i] = NA_LOGICAL; |
630 | 0 | else if (c1 == c2) |
631 | 0 | pa[i] = 1; |
632 | 0 | else { |
633 | 0 | errno = 0; |
634 | 0 | res = Scollate(c1, c2); |
635 | 0 | if(errno) |
636 | 0 | pa[i] = NA_LOGICAL; |
637 | 0 | else |
638 | 0 | pa[i] = (res <= 0) ? 1 : 0; |
639 | 0 | } |
640 | 0 | }); |
641 | 0 | break; |
642 | 0 | case GEOP: |
643 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
644 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
645 | 0 | c1 = STRING_ELT(s1, i1); |
646 | 0 | c2 = STRING_ELT(s2, i2); |
647 | 0 | if (c1 == NA_STRING || c2 == NA_STRING) |
648 | 0 | pa[i] = NA_LOGICAL; |
649 | 0 | else if (c1 == c2) |
650 | 0 | pa[i] = 1; |
651 | 0 | else { |
652 | 0 | errno = 0; |
653 | 0 | res = Scollate(c1, c2); |
654 | 0 | if(errno) |
655 | 0 | pa[i] = NA_LOGICAL; |
656 | 0 | else |
657 | 0 | pa[i] = (res >= 0) ? 1 : 0; |
658 | 0 | } |
659 | 0 | }); |
660 | 0 | break; |
661 | 1.44M | } |
662 | 1.44M | UNPROTECT(3); |
663 | 1.44M | vmaxset(vmax); |
664 | 1.44M | return ans; |
665 | 1.44M | } |
666 | | |
667 | | static SEXP raw_relop(RELOP_TYPE code, SEXP s1, SEXP s2) |
668 | 0 | { |
669 | 0 | R_xlen_t i, i1, i2, n, n1, n2; |
670 | 0 | Rbyte x1, x2; |
671 | 0 | SEXP ans; |
672 | |
|
673 | 0 | n1 = XLENGTH(s1); |
674 | 0 | n2 = XLENGTH(s2); |
675 | 0 | n = (n1 > n2) ? n1 : n2; |
676 | 0 | PROTECT(s1); |
677 | 0 | PROTECT(s2); |
678 | 0 | ans = allocVector(LGLSXP, n); |
679 | |
|
680 | 0 | const Rbyte *px1 = RAW_RO(s1); |
681 | 0 | const Rbyte *px2 = RAW_RO(s2); |
682 | 0 | int *pa = LOGICAL(ans); |
683 | |
|
684 | 0 | switch (code) { |
685 | 0 | case EQOP: |
686 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
687 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
688 | 0 | x1 = px1[i1]; |
689 | 0 | x2 = px2[i2]; |
690 | 0 | pa[i] = (x1 == x2); |
691 | 0 | }); |
692 | 0 | break; |
693 | 0 | case NEOP: |
694 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
695 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
696 | 0 | x1 = px1[i1]; |
697 | 0 | x2 = px2[i2]; |
698 | 0 | pa[i] = (x1 != x2); |
699 | 0 | }); |
700 | 0 | break; |
701 | 0 | case LTOP: |
702 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
703 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
704 | 0 | x1 = px1[i1]; |
705 | 0 | x2 = px2[i2]; |
706 | 0 | pa[i] = (x1 < x2); |
707 | 0 | }); |
708 | 0 | break; |
709 | 0 | case GTOP: |
710 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
711 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
712 | 0 | x1 = px1[i1]; |
713 | 0 | x2 = px2[i2]; |
714 | 0 | pa[i] = (x1 > x2); |
715 | 0 | }); |
716 | 0 | break; |
717 | 0 | case LEOP: |
718 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
719 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
720 | 0 | x1 = px1[i1]; |
721 | 0 | x2 = px2[i2]; |
722 | 0 | pa[i] = (x1 <= x2); |
723 | 0 | }); |
724 | 0 | break; |
725 | 0 | case GEOP: |
726 | 0 | MOD_ITERATE2(n, n1, n2, i, i1, i2, { |
727 | | // if ((i+1) % NINTERRUPT == 0) R_CheckUserInterrupt(); |
728 | 0 | x1 = px1[i1]; |
729 | 0 | x2 = px2[i2]; |
730 | 0 | pa[i] = (x1 >= x2); |
731 | 0 | }); |
732 | 0 | break; |
733 | 0 | } |
734 | 0 | UNPROTECT(2); |
735 | 0 | return ans; |
736 | 0 | } |
737 | | |
738 | | |
739 | | static SEXP bitwiseNot(SEXP a) |
740 | 0 | { |
741 | 0 | SEXP ans; |
742 | 0 | int np = 0; |
743 | 0 | if(isReal(a)) {a = PROTECT(coerceVector(a, INTSXP)); np++;} |
744 | |
|
745 | 0 | switch(TYPEOF(a)) { |
746 | 0 | case INTSXP: |
747 | 0 | { |
748 | 0 | R_xlen_t m = XLENGTH(a); |
749 | 0 | ans = allocVector(INTSXP, m); |
750 | 0 | int *pans = INTEGER(ans); |
751 | 0 | const int *pa = INTEGER_RO(a); |
752 | 0 | for(R_xlen_t i = 0; i < m; i++) { |
753 | 0 | int aa = pa[i]; |
754 | 0 | pans[i] = (aa == NA_INTEGER) ? aa : ~aa; |
755 | 0 | } |
756 | 0 | } |
757 | 0 | break; |
758 | 0 | default: |
759 | 0 | UNIMPLEMENTED_TYPE("bitwNot", a); |
760 | 0 | } |
761 | 0 | if(np) UNPROTECT(np); |
762 | 0 | return ans; |
763 | 0 | } |
764 | | |
765 | 0 | #define mymax(x, y) ((x >= y) ? x : y) |
766 | | |
767 | | #define BIT(op, name) \ |
768 | 0 | SEXP ans; \ |
769 | 0 | int np = 0; \ |
770 | 0 | if(isReal(a)) {a = PROTECT(coerceVector(a, INTSXP)); np++;} \ |
771 | 0 | if(isReal(b)) {b = PROTECT(coerceVector(b, INTSXP)); np++;} \ |
772 | 0 | if (TYPEOF(a) != TYPEOF(b)) \ |
773 | 0 | error(_("'a' and 'b' must have the same type")); \ |
774 | 0 | switch(TYPEOF(a)) { \ |
775 | 0 | case INTSXP: \ |
776 | 0 | { \ |
777 | 0 | R_xlen_t i, ia, ib; \ |
778 | 0 | R_xlen_t m = XLENGTH(a), n = XLENGTH(b), \ |
779 | 0 | mn = (m && n) ? mymax(m, n) : 0; \ |
780 | 0 | ans = allocVector(INTSXP, mn); \ |
781 | 0 | int *pans = INTEGER(ans); \ |
782 | 0 | const int *pa = INTEGER_RO(a), *pb = INTEGER_RO(b); \ |
783 | 0 | MOD_ITERATE2(mn, m, n, i, ia, ib, { \ |
784 | 0 | int aa = pa[ia]; int bb = pb[ib]; \ |
785 | 0 | pans[i] = (aa == NA_INTEGER || bb == NA_INTEGER) ? \ |
786 | 0 | NA_INTEGER : aa op bb; \ |
787 | 0 | }); \ |
788 | 0 | } \ |
789 | 0 | break; \ |
790 | 0 | default: \ |
791 | 0 | UNIMPLEMENTED_TYPE(name, a); \ |
792 | 0 | } \ |
793 | 0 | if(np) UNPROTECT(np); \ |
794 | 0 | return ans |
795 | | |
796 | | static SEXP bitwiseAnd(SEXP a, SEXP b) |
797 | 0 | { |
798 | 0 | BIT(&, "bitwAnd"); |
799 | 0 | } |
800 | | |
801 | | static SEXP bitwiseOr(SEXP a, SEXP b) |
802 | 0 | { |
803 | 0 | BIT(|, "bitwOr"); |
804 | 0 | } |
805 | | |
806 | | static SEXP bitwiseXor(SEXP a, SEXP b) |
807 | 0 | { |
808 | 0 | BIT(^, "bitwXor"); |
809 | 0 | } |
810 | | |
811 | | static SEXP bitwiseShiftL(SEXP a, SEXP b) |
812 | 0 | { |
813 | 0 | SEXP ans; |
814 | 0 | int np = 0; |
815 | 0 | if(isReal(a)) {a = PROTECT(coerceVector(a, INTSXP)); np++;} |
816 | 0 | if(!isInteger(b)) {b = PROTECT(coerceVector(b, INTSXP)); np++;} |
817 | 0 | if (TYPEOF(a) != TYPEOF(b)) |
818 | 0 | error(_("'a' and 'b' must have the same type")); |
819 | |
|
820 | 0 | switch(TYPEOF(a)) { |
821 | 0 | case INTSXP: |
822 | 0 | { |
823 | 0 | R_xlen_t i, ia, ib; |
824 | 0 | R_xlen_t m = XLENGTH(a), n = XLENGTH(b), |
825 | 0 | mn = (m && n) ? mymax(m, n) : 0; |
826 | 0 | ans = allocVector(INTSXP, mn); |
827 | 0 | int *pans = INTEGER(ans); |
828 | 0 | const int *pa = INTEGER_RO(a), *pb = INTEGER_RO(b); |
829 | 0 | MOD_ITERATE2(mn, m, n, i, ia, ib, { |
830 | 0 | int aa = pa[ia]; int bb = pb[ib]; |
831 | 0 | pans[i] = |
832 | 0 | (aa == NA_INTEGER || bb == NA_INTEGER || |
833 | 0 | bb < 0 || bb > 31) ? |
834 | 0 | NA_INTEGER : ((unsigned int)aa << bb); |
835 | 0 | }); |
836 | 0 | } |
837 | 0 | break; |
838 | 0 | default: |
839 | 0 | UNIMPLEMENTED_TYPE("bitShiftL", a); |
840 | 0 | } |
841 | 0 | if(np) UNPROTECT(np); |
842 | 0 | return ans; |
843 | 0 | } |
844 | | |
845 | | static SEXP bitwiseShiftR(SEXP a, SEXP b) |
846 | 0 | { |
847 | 0 | SEXP ans; |
848 | 0 | int np = 0; |
849 | 0 | if(isReal(a)) {a = PROTECT(coerceVector(a, INTSXP)); np++;} |
850 | 0 | if(!isInteger(b)) {b = PROTECT(coerceVector(b, INTSXP)); np++;} |
851 | 0 | if (TYPEOF(a) != TYPEOF(b)) |
852 | 0 | error(_("'a' and 'b' must have the same type")); |
853 | |
|
854 | 0 | switch(TYPEOF(a)) { |
855 | 0 | case INTSXP: |
856 | 0 | { |
857 | 0 | R_xlen_t i, ia, ib; |
858 | 0 | R_xlen_t m = XLENGTH(a), n = XLENGTH(b), |
859 | 0 | mn = (m && n) ? mymax(m, n) : 0; |
860 | 0 | ans = allocVector(TYPEOF(a), mn); |
861 | 0 | int *pans = INTEGER(ans); |
862 | 0 | const int *pa = INTEGER_RO(a), *pb = INTEGER_RO(b); |
863 | 0 | MOD_ITERATE2(mn, m, n, i, ia, ib, { |
864 | 0 | int aa = pa[ia]; int bb = pb[ib]; |
865 | 0 | pans[i] = |
866 | 0 | (aa == NA_INTEGER || bb == NA_INTEGER || |
867 | 0 | bb < 0 || bb > 31) ? |
868 | 0 | NA_INTEGER : ((unsigned int)aa >> bb); |
869 | 0 | }); |
870 | 0 | } |
871 | 0 | break; |
872 | 0 | default: |
873 | 0 | UNIMPLEMENTED_TYPE("bitShiftR", a); |
874 | 0 | } |
875 | 0 | if(np) UNPROTECT(np); |
876 | 0 | return ans; |
877 | 0 | } |
878 | | |
879 | | attribute_hidden SEXP do_bitwise(SEXP call, SEXP op, SEXP args, SEXP env) |
880 | 0 | { |
881 | 0 | checkArity(op, args); |
882 | 0 | SEXP ans = R_NilValue; /* -Wall */ |
883 | 0 | switch(PRIMVAL(op)) { |
884 | 0 | case 1: ans = bitwiseAnd(CAR(args), CADR(args)); break; |
885 | 0 | case 2: ans = bitwiseNot(CAR(args)); break; |
886 | 0 | case 3: ans = bitwiseOr(CAR(args), CADR(args)); break; |
887 | 0 | case 4: ans = bitwiseXor(CAR(args), CADR(args)); break; |
888 | 0 | case 5: ans = bitwiseShiftL(CAR(args), CADR(args)); break; |
889 | 0 | case 6: ans = bitwiseShiftR(CAR(args), CADR(args)); break; |
890 | 0 | } |
891 | 0 | return ans; |
892 | 0 | } |