/src/r-source/src/main/radixsort.c
Line | Count | Source |
1 | | /* |
2 | | * R : A Computer Language for Statistical Data Analysis |
3 | | * Copyright (C) 2016-2025 The R Core Team |
4 | | * |
5 | | * Based on code donated from the data.table package |
6 | | * (C) 2006-2015 Matt Dowle and Arun Srinivasan. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU General Public License as published by |
10 | | * the Free Software Foundation; either version 2 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU General Public License |
19 | | * along with this program; if not, a copy is available at |
20 | | * https://www.R-project.org/Licenses/ |
21 | | */ |
22 | | |
23 | | #ifdef HAVE_CONFIG_H |
24 | | #include <config.h> |
25 | | #endif |
26 | | |
27 | | #include <Defn.h> |
28 | | #include <Internal.h> |
29 | | |
30 | | /* It would be better to find a way to avoid abusing TRUELENGTH, but |
31 | | in the meantime replace TRUELENGTH/SET_TRUELENGTH with |
32 | | TRLEN/SET_TRLEN that cast to int to avoid warnings. */ |
33 | 0 | #define TRLEN(x) ((int) TRUELENGTH(x)) |
34 | 12 | #define SET_TRLEN(x, v) SET_TRUELENGTH(x, ((int) (v))) |
35 | | |
36 | | // gs = groupsizes e.g.23, 12, 87, 2, 1, 34,... |
37 | | static int *gs[2] = { NULL }; |
38 | | //two vectors flip flopped:flip and 1 - flip |
39 | | static int flip = 0; |
40 | | //allocated stack size |
41 | | static int gsalloc[2] = { 0 }; |
42 | | static int gsngrp[2] = { 0 }; |
43 | | //max grpn so far |
44 | | static int gsmax[2] = { 0 }; |
45 | | //max size of stack, set by do_radixsort to nrows |
46 | | static int gsmaxalloc = 0; |
47 | | //switched off for last arg unless retGrp==TRUE |
48 | | static bool stackgrps = true; |
49 | | // TRUE for setkey, FALSE for by= |
50 | | static bool sortStr = true; |
51 | | // used by do_radixsort and [i|d|c]sort to reorder order. |
52 | | // not needed if narg==1 |
53 | | static int *newo = NULL; |
54 | | // =1, 0, -1 for TRUE, NA, FALSE respectively. |
55 | | // Value rewritten inside do_radixsort(). |
56 | | static int nalast = -1; |
57 | | // =1, -1 for ascending and descending order respectively |
58 | | static int order = 1; |
59 | | |
60 | | //replaced n < 200 with n < N_SMALL.Easier to change later |
61 | 0 | #define N_SMALL 200 |
62 | | // range limit for counting sort. Should be less than INT_MAX |
63 | | // (see setRange for details) |
64 | 0 | #define N_RANGE 100000 |
65 | | |
66 | | static SEXP *saveds = NULL; |
67 | | static R_len_t *savedtl = NULL, nalloc = 0, nsaved = 0; |
68 | | |
69 | | static void savetl_init(void) |
70 | 12 | { |
71 | 12 | if (nsaved || nalloc || saveds || savedtl) |
72 | 0 | error("Internal error: savetl_init checks failed (%d %d %p %p).", |
73 | 0 | nsaved, nalloc, (void *)saveds, (void *)savedtl); |
74 | 12 | nsaved = 0; |
75 | 12 | nalloc = 100; |
76 | 12 | saveds = (SEXP *) malloc(nalloc * sizeof(SEXP)); |
77 | 12 | if (saveds == NULL) |
78 | 0 | error("Could not allocate saveds in savetl_init"); |
79 | 12 | savedtl = (R_len_t *) malloc(nalloc * sizeof(R_len_t)); |
80 | 12 | if (savedtl == NULL) { |
81 | 0 | free(saveds); |
82 | 0 | error("Could not allocate saveds in savetl_init"); |
83 | 0 | } |
84 | 12 | } |
85 | | |
86 | | static void savetl_end(void) |
87 | 12 | { |
88 | | // Can get called if nothing has been saved yet (nsaved == 0), or |
89 | | // even if _init() has not been called yet (pointers NULL). Such as |
90 | | // to clear up before error. Also, it might be that nothing needed |
91 | | // to be saved anyway. |
92 | 12 | for (int i = 0; i < nsaved; i++) |
93 | 0 | SET_TRLEN(saveds[i], savedtl[i]); |
94 | 12 | free(saveds); // does nothing on NULL input |
95 | 12 | free(savedtl); |
96 | 12 | nsaved = nalloc = 0; |
97 | 12 | saveds = NULL; |
98 | 12 | savedtl = NULL; |
99 | 12 | } |
100 | | |
101 | | |
102 | | static void savetl(SEXP s) |
103 | 0 | { |
104 | 0 | if (nsaved >= nalloc) { |
105 | 0 | nalloc *= 2; |
106 | 0 | char *tmp; |
107 | 0 | tmp = (char *) realloc(saveds, nalloc * sizeof(SEXP)); |
108 | 0 | if (tmp == NULL) { |
109 | 0 | savetl_end(); |
110 | 0 | error("Could not realloc saveds in savetl"); |
111 | 0 | } |
112 | 0 | saveds = (SEXP *) tmp; |
113 | 0 | tmp = (char *) realloc(savedtl, nalloc * sizeof(R_len_t)); |
114 | 0 | if (tmp == NULL) { |
115 | 0 | savetl_end(); |
116 | 0 | error("Could not realloc savedtl in savetl"); |
117 | 0 | } |
118 | 0 | savedtl = (R_len_t *) tmp; |
119 | 0 | } |
120 | 0 | saveds[nsaved] = s; |
121 | 0 | savedtl[nsaved] = TRLEN(s); |
122 | 0 | nsaved++; |
123 | 0 | } |
124 | | |
125 | | // http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html#Swallowing-the-Semicolon |
126 | 0 | #define Error(...) do {savetl_end(); error(__VA_ARGS__);} while(0) |
127 | | #undef warning |
128 | | // since it can be turned to error via warn = 2 |
129 | | #define warning(...) Do not use warning in this file |
130 | | /* use malloc/realloc (not Calloc/Realloc) so we can trap errors |
131 | | and call savetl_end() before the error(). */ |
132 | | |
133 | | static void growstack(uint64_t newlen) |
134 | 0 | { |
135 | | // no link to icount range restriction, |
136 | | // just 100,000 seems a good minimum at 0.4MB |
137 | 0 | if (newlen == 0) newlen = 100000; |
138 | 0 | if (newlen > gsmaxalloc) newlen = gsmaxalloc; |
139 | 0 | gs[flip] = realloc(gs[flip], newlen * sizeof(int)); |
140 | 0 | if (gs[flip] == NULL) |
141 | 0 | Error("Failed to realloc working memory stack to %d*4bytes (flip=%d)", |
142 | 0 | (int)newlen /* no bigger than gsmaxalloc */, flip); |
143 | 0 | gsalloc[flip] = (int)newlen; |
144 | 0 | } |
145 | | |
146 | | static void push(int x) |
147 | 12 | { |
148 | 12 | if (!stackgrps || x == 0) |
149 | 12 | return; |
150 | 0 | if (gsalloc[flip] == gsngrp[flip]) |
151 | 0 | growstack((uint64_t)(gsngrp[flip]) * 2); |
152 | 0 | gs[flip][gsngrp[flip]++] = x; |
153 | 0 | if (x > gsmax[flip]) |
154 | 0 | gsmax[flip] = x; |
155 | 0 | } |
156 | | |
157 | | static void mpush(int x, int n) |
158 | 0 | { |
159 | 0 | if (!stackgrps || x == 0) |
160 | 0 | return; |
161 | 0 | if (gsalloc[flip] < gsngrp[flip] + n) |
162 | 0 | growstack(((uint64_t)(gsngrp[flip]) + n) * 2); |
163 | 0 | for (int i = 0; i < n; i++) |
164 | 0 | gs[flip][gsngrp[flip]++] = x; |
165 | 0 | if (x > gsmax[flip]) |
166 | 0 | gsmax[flip] = x; |
167 | 0 | } |
168 | | |
169 | | static void flipflop(void) |
170 | 0 | { |
171 | 0 | flip = 1 - flip; |
172 | 0 | gsngrp[flip] = 0; |
173 | 0 | gsmax[flip] = 0; |
174 | 0 | if (gsalloc[flip] < gsalloc[1 - flip]) |
175 | 0 | growstack((uint64_t)(gsalloc[1 - flip]) * 2); |
176 | 0 | } |
177 | | |
178 | | static void gsfree(void) |
179 | 12 | { |
180 | 12 | free(gs[0]); |
181 | 12 | free(gs[1]); |
182 | 12 | gs[0] = NULL; |
183 | 12 | gs[1] = NULL; |
184 | 12 | flip = 0; |
185 | 12 | gsalloc[0] = gsalloc[1] = 0; |
186 | 12 | gsngrp[0] = gsngrp[1] = 0; |
187 | 12 | gsmax[0] = gsmax[1] = 0; |
188 | 12 | gsmaxalloc = 0; |
189 | 12 | } |
190 | | |
191 | | #ifdef TIMING_ON |
192 | | // many calls to clock() can be expensive, |
193 | | // hence compiled out rather than switch(verbose) |
194 | | #include <time.h> |
195 | | #define NBLOCK 20 |
196 | | static clock_t tblock[NBLOCK], tstart; |
197 | | static int nblock[NBLOCK]; |
198 | | #define TBEG() tstart = clock(); |
199 | | #define TEND(i) tblock[i] += clock()-tstart; nblock[i]++; tstart = clock(); |
200 | | #else |
201 | | #define TBEG() |
202 | | #define TEND(i) |
203 | | #endif |
204 | | |
205 | | static int range, xmin; // used by both icount and do_radixsort |
206 | | static void setRange(int *x, int n) |
207 | 0 | { |
208 | 0 | xmin = NA_INTEGER; |
209 | 0 | int xmax = NA_INTEGER; |
210 | 0 | double overflow; |
211 | |
|
212 | 0 | int i = 0; |
213 | 0 | while(i < n && x[i] == NA_INTEGER) i++; |
214 | 0 | if (i < n) xmax = xmin = x[i]; |
215 | 0 | for (; i < n; i++) { |
216 | 0 | int tmp = x[i]; |
217 | 0 | if (tmp == NA_INTEGER) |
218 | 0 | continue; |
219 | 0 | if (tmp > xmax) |
220 | 0 | xmax = tmp; |
221 | 0 | else if (tmp < xmin) |
222 | 0 | xmin = tmp; |
223 | 0 | } |
224 | | // all NAs, nothing to do |
225 | 0 | if (xmin == NA_INTEGER) { |
226 | 0 | range = NA_INTEGER; |
227 | 0 | return; |
228 | 0 | } |
229 | | // ex: x=c(-2147483647L, NA_integer_, 1L) results in overflowing int range. |
230 | 0 | overflow = (double) xmax - (double) xmin + 1; |
231 | | // detect and force iradix here, since icount is out of the picture |
232 | 0 | if (overflow > INT_MAX) { |
233 | 0 | range = INT_MAX; |
234 | 0 | return; |
235 | 0 | } |
236 | | |
237 | 0 | range = xmax - xmin + 1; |
238 | |
|
239 | 0 | return; |
240 | 0 | } |
241 | | |
242 | | // x*order results in integer overflow when -1*NA, |
243 | | // so careful to avoid that here : |
244 | | static inline int icheck(int x) |
245 | 144 | { |
246 | | // if nalast == 1, NAs must go last. |
247 | 144 | return ((nalast != 1) ? ((x != NA_INTEGER) ? x*order : x) : |
248 | 144 | ((x != NA_INTEGER) ? (x*order) - 1 : INT_MAX)); |
249 | 144 | } |
250 | | |
251 | | |
252 | | static void icount(int *x, int *o, int n) |
253 | | /* Counting sort: |
254 | | 1. Places the ordering into o directly, overwriting whatever was there |
255 | | 2. Doesn't change x |
256 | | 3. Pushes group sizes onto stack |
257 | | */ |
258 | 0 | { |
259 | 0 | int napos = range; // NA's always counted in last bin |
260 | | // static is IMPORTANT, counting sort is called repetitively. |
261 | 0 | static unsigned int counts[N_RANGE + 1] = { 0 }; |
262 | | /* counts are set back to 0 at the end efficiently. 1e5 = 0.4MB i.e |
263 | | tiny. We'll only use the front part of it, as large as range. So it's |
264 | | just reserving space, not using it. Have defined N_RANGE to be 100000.*/ |
265 | 0 | if (range > N_RANGE) |
266 | 0 | Error("Internal error: range = %d; isorted cannot handle range > %d", |
267 | 0 | range, N_RANGE); |
268 | 0 | for (int i = 0; i < n; i++) { |
269 | | // For nalast=NA case, we won't remove/skip NAs, rather set 'o' indices |
270 | | // to 0. subset will skip them. We can't know how many NAs to skip |
271 | | // beforehand - i.e. while allocating "ans" vector |
272 | 0 | if (x[i] == NA_INTEGER) |
273 | 0 | counts[napos]++; |
274 | 0 | else |
275 | 0 | counts[x[i] - xmin]++; |
276 | 0 | } |
277 | | |
278 | 0 | int tmp = 0; |
279 | 0 | if (nalast != 1 && counts[napos]) { |
280 | 0 | push(counts[napos]); |
281 | 0 | tmp += counts[napos]; |
282 | 0 | } |
283 | 0 | int w = (order==1) ? 0 : range-1; |
284 | 0 | for (int i = 0; i < range; i++) |
285 | | /* no point in adding tmp < n && i <= range, since range includes max, |
286 | | need to go to max, unlike 256 loops elsewhere in radixsort.c */ |
287 | 0 | { |
288 | 0 | if (counts[w]) { |
289 | | // cumulate but not through 0's. |
290 | | // Helps resetting zeros when n < range, below. |
291 | 0 | push(counts[w]); |
292 | 0 | counts[w] = (tmp += counts[w]); |
293 | 0 | } |
294 | 0 | w += order; // order is +1 or -1 |
295 | 0 | } |
296 | 0 | if (nalast == 1 && counts[napos]) { |
297 | 0 | push(counts[napos]); |
298 | 0 | counts[napos] = (tmp += counts[napos]); |
299 | 0 | } |
300 | 0 | for (int i = n - 1; i >= 0; i--) { |
301 | | // This way na.last=TRUE/FALSE cases will have just a |
302 | | // single if-check overhead. |
303 | 0 | o[--counts[(x[i] == NA_INTEGER) ? napos : |
304 | 0 | x[i] - xmin]] = (int) (i + 1); |
305 | 0 | } |
306 | | // nalast = 1, -1 are both taken care already. |
307 | 0 | if (nalast == 0) |
308 | | // nalast = 0 is dealt with separately as it just sets o to 0 |
309 | 0 | for (int i = 0; i < n; i++) |
310 | 0 | o[i] = (x[o[i] - 1] == NA_INTEGER) ? 0 : o[i]; |
311 | | // at those indices where x is NA. x[o[i]-1] because x is not modifed here. |
312 | | |
313 | | /* counts were cumulated above so leaves non zero. |
314 | | Faster to clear up now ready for next time. */ |
315 | 0 | if (n < range) { |
316 | | /* Many zeros in counts already. Loop through n instead, |
317 | | doesn't matter if we set to 0 several times on any repeats */ |
318 | 0 | counts[napos] = 0; |
319 | 0 | for (int i = 0; i < n; i++) { |
320 | 0 | if (x[i] != NA_INTEGER) |
321 | 0 | counts[x[i] - xmin] = 0; |
322 | 0 | } |
323 | 0 | } else |
324 | 0 | if (range + 1) |
325 | 0 | memset(counts, 0, (range + 1) * sizeof(int)); |
326 | 0 | return; |
327 | 0 | } |
328 | | |
329 | | static void iinsert(int *x, int *o, int n) |
330 | | /* orders both x and o by reference in-place. Fast for small vectors, |
331 | | low overhead. don't be tempted to binsearch backwards here, have |
332 | | to shift anyway; many memmove would have overhead and do the same |
333 | | thing. */ |
334 | | /* when nalast == 0, iinsert will be called only from within iradix, |
335 | | where o[.] = 0 for x[.]=NA is already taken care of */ |
336 | 0 | { |
337 | 0 | for (int i = 1; i < n; i++) { |
338 | 0 | int xtmp = x[i]; |
339 | 0 | if (xtmp < x[i - 1]) { |
340 | 0 | int j = i - 1; |
341 | 0 | int otmp = o[i]; |
342 | 0 | while (j >= 0 && xtmp < x[j]) { |
343 | 0 | x[j + 1] = x[j]; |
344 | 0 | o[j + 1] = o[j]; |
345 | 0 | j--; |
346 | 0 | } |
347 | 0 | x[j + 1] = xtmp; |
348 | 0 | o[j + 1] = otmp; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | int tt = 0; |
352 | 0 | for (int i = 1; i < n; i++) |
353 | 0 | if (x[i] == x[i - 1]) |
354 | 0 | tt++; |
355 | 0 | else { |
356 | 0 | push(tt + 1); |
357 | 0 | tt = 0; |
358 | 0 | } |
359 | 0 | push(tt + 1); |
360 | 0 | } |
361 | | |
362 | | /* |
363 | | iradix is a counting sort performed forwards from MSB to LSB, with |
364 | | some tricks and short circuits building on Terdiman and Herf. |
365 | | http://codercorner.com/RadixSortRevisited.htm |
366 | | http://stereopsis.com/radix.html |
367 | | |
368 | | ~ Note they are LSD, but we do MSD here which is more complicated, |
369 | | for efficiency. |
370 | | ~ NAs need no special treatment as NA is the most negative integer |
371 | | in R (checked in init.c once, for efficiency) so NA naturally sort |
372 | | to the front. |
373 | | ~ Using 4-pass 1-byte radix for the following reasons : |
374 | | |
375 | | * 11-bit (Herf) reduces to 3-passes (3*11=33) yes, and LSD need |
376 | | random access to o vector in each pass 1:n so reduction in passes is |
377 | | good, but Terdiman's idea to skip a radix if all values are equal |
378 | | occurs less the wider the radix. A narrower radix benefits more from that. |
379 | | * That's detected here using a single 'if', an improvement on |
380 | | Terdiman's exposition of a single loop to find if any count==n |
381 | | * The pass through counts bites when radix is wider, |
382 | | because we repetitively call this iradix from fastorder forwards. |
383 | | * Herf's parallel histogramming is neat. In 4-pass 1-byte it needs |
384 | | 4*256 storage, that's tiny, and can be static. 4*256 << 3*2048. |
385 | | 4-pass 1-byte is simpler and tighter code than 3-pass 11-bit, |
386 | | giving modern optimizers and modern CPUs a better chance. |
387 | | We may get lucky anyway, if one or two of the 4-passes are skipped. |
388 | | |
389 | | Recall: there are no comparisons at all in counting and radix, |
390 | | there is wide random access in each LSD radix pass, though. |
391 | | */ |
392 | | |
393 | | // 4 are used for iradix, 8 for dradix and i64radix |
394 | | static unsigned int radixcounts[8][257] = { {0} }; |
395 | | |
396 | | static int skip[8]; |
397 | | /* global because iradix and iradix_r interact and are called repetitively. |
398 | | counts are set back to 0 after each use, to benefit from skipped radix. */ |
399 | | static void *radix_xsub = NULL; |
400 | | static size_t radix_xsuballoc = 0; |
401 | | |
402 | | static int *otmp = NULL, otmp_alloc = 0; |
403 | | static void alloc_otmp(int n) |
404 | 0 | { |
405 | 0 | if (otmp_alloc >= n) |
406 | 0 | return; |
407 | 0 | otmp = (int *) realloc(otmp, n * sizeof(int)); |
408 | 0 | if (otmp == NULL) |
409 | 0 | Error("Failed to allocate working memory for otmp. Requested %d * %d bytes", |
410 | 0 | n, (int)sizeof(int)); |
411 | 0 | otmp_alloc = n; |
412 | 0 | } |
413 | | |
414 | | // TO DO: save xtmp if possible, see allocs in do_radixsort |
415 | | static void *xtmp = NULL; |
416 | | static int xtmp_alloc = 0; |
417 | | // TO DO: currently always the largest type (double) but |
418 | | // could be int if that's all that's needed |
419 | | static void alloc_xtmp(int n) |
420 | 0 | { |
421 | 0 | if (xtmp_alloc >= n) |
422 | 0 | return; |
423 | 0 | xtmp = (double *) realloc(xtmp, n * sizeof(double)); |
424 | 0 | if (xtmp == NULL) |
425 | 0 | Error("Failed to allocate working memory for xtmp. Requested %d * %d bytes", |
426 | 0 | n, (int)sizeof(double)); |
427 | 0 | xtmp_alloc = n; |
428 | 0 | } |
429 | | |
430 | | static void iradix_r(int *xsub, int *osub, int n, int radix); |
431 | | |
432 | | static void iradix(int *x, int *o, int n) |
433 | | /* As icount : |
434 | | Places the ordering into o directly, overwriting whatever was there |
435 | | Doesn't change x |
436 | | Pushes group sizes onto stack */ |
437 | 0 | { |
438 | 0 | int nextradix, itmp, thisgrpn, maxgrpn; |
439 | 0 | unsigned int thisx = 0, shift, *thiscounts; |
440 | |
|
441 | 0 | for (int i = 0; i < n;i++) { |
442 | | /* parallel histogramming pass; i.e. count occurrences of |
443 | | 0:255 in each byte. Sequential so almost negligible. */ |
444 | | // relies on overflow behaviour. And shouldn't -INT_MIN be up in iradix? |
445 | 0 | thisx = (unsigned int) (icheck(x[i])) - INT_MIN; |
446 | | // unrolled since inside n-loop |
447 | 0 | radixcounts[0][thisx & 0xFF]++; |
448 | 0 | radixcounts[1][thisx >> 8 & 0xFF]++; |
449 | 0 | radixcounts[2][thisx >> 16 & 0xFF]++; |
450 | 0 | radixcounts[3][thisx >> 24 & 0xFF]++; |
451 | 0 | } |
452 | 0 | for (int radix = 0; radix < 4; radix++) { |
453 | | /* any(count == n) => all radix must have been that value => |
454 | | last x (still thisx) was that value */ |
455 | 0 | int i = thisx >> (radix*8) & 0xFF; |
456 | 0 | skip[radix] = radixcounts[radix][i] == n; |
457 | | // clear it now, the other counts must be 0 already |
458 | 0 | if (skip[radix]) |
459 | 0 | radixcounts[radix][i] = 0; |
460 | 0 | } |
461 | |
|
462 | 0 | int radix = 3; // MSD |
463 | 0 | while (radix >= 0 && skip[radix]) radix--; |
464 | 0 | if (radix == -1) { // All radix are skipped; one number repeated n times. |
465 | 0 | if (nalast == 0 && x[0] == NA_INTEGER) |
466 | | // all values are identical. return 0 if nalast=0 & all NA |
467 | | // because of 'return', have to take care of it here. |
468 | 0 | for (int i = 0; i < n; i++) |
469 | 0 | o[i] = 0; |
470 | 0 | else |
471 | 0 | for (int i = 0; i < n; i++) |
472 | 0 | o[i] = (i + 1); |
473 | 0 | push(n); |
474 | 0 | return; |
475 | 0 | } |
476 | 0 | for (int i = radix - 1; i >= 0; i--) { |
477 | 0 | if (!skip[i]) |
478 | 0 | memset(radixcounts[i], 0, 257 * sizeof(unsigned int)); |
479 | | /* clear the counts as we only needed the parallel pass for skip[] |
480 | | and we're going to use radixcounts again below. Can't use parallel |
481 | | lower counts in MSD radix, unlike LSD. */ |
482 | 0 | } |
483 | 0 | thiscounts = radixcounts[radix]; |
484 | 0 | shift = radix * 8; |
485 | |
|
486 | 0 | itmp = thiscounts[0]; |
487 | 0 | maxgrpn = itmp; |
488 | 0 | for (int i = 1; itmp < n && i < 256; i++) { |
489 | 0 | thisgrpn = thiscounts[i]; |
490 | 0 | if (thisgrpn) { |
491 | | // don't cummulate through 0s, important below. |
492 | 0 | if (thisgrpn > maxgrpn) |
493 | 0 | maxgrpn = thisgrpn; |
494 | 0 | thiscounts[i] = (itmp += thisgrpn); |
495 | 0 | } |
496 | 0 | } |
497 | 0 | for (int i = n - 1; i >= 0; i--) { |
498 | 0 | thisx = ((unsigned int) (icheck(x[i])) - INT_MIN) >> shift & 0xFF; |
499 | 0 | o[--thiscounts[thisx]] = i + 1; |
500 | 0 | } |
501 | |
|
502 | 0 | if (radix_xsuballoc < maxgrpn) { |
503 | | // The largest group according to the first non-skipped radix, |
504 | | // so could be big (if radix is needed on first arg) |
505 | | // TO DO: could include extra bits to divide the first radix |
506 | | // up more. Often the MSD has groups in just 0-4 out of 256. |
507 | | // free'd at the end of do_radixsort once we're done calling iradix |
508 | | // repetitively |
509 | 0 | radix_xsub = (int *) realloc(radix_xsub, maxgrpn * sizeof(double)); |
510 | 0 | if (!radix_xsub) |
511 | 0 | Error("Failed to realloc working memory %d*8bytes (xsub in iradix), radix=%d", |
512 | 0 | maxgrpn, radix); |
513 | 0 | radix_xsuballoc = maxgrpn; |
514 | 0 | } |
515 | | |
516 | | // TO DO: can we leave this to do_radixsort and remove these calls?? |
517 | 0 | alloc_otmp(maxgrpn); |
518 | | // TO DO: doesn't need to be sizeof(double) always, see inside |
519 | 0 | alloc_xtmp(maxgrpn); |
520 | |
|
521 | 0 | nextradix = radix - 1; |
522 | 0 | while (nextradix >= 0 && skip[nextradix]) nextradix--; |
523 | 0 | if (thiscounts[0] != 0) |
524 | 0 | Error("Internal error. thiscounts[0]=%d but should have been decremented to 0. dradix=%d", |
525 | 0 | thiscounts[0], radix); |
526 | 0 | thiscounts[256] = n; |
527 | 0 | itmp = 0; |
528 | 0 | for (int i = 1; itmp < n && i <= 256; i++) { |
529 | 0 | if (thiscounts[i] == 0) continue; |
530 | | // undo cumulate; i.e. diff |
531 | 0 | thisgrpn = thiscounts[i] - itmp; |
532 | 0 | if (thisgrpn == 1 || nextradix == -1) { |
533 | 0 | push(thisgrpn); |
534 | 0 | } else { |
535 | 0 | for (int j = 0; j < thisgrpn; j++) |
536 | | // this is why this xsub here can't be the same memory as |
537 | | // xsub in do_radixsort. |
538 | 0 | ((int *)radix_xsub)[j] = icheck(x[o[itmp+j]-1]); |
539 | | // changes xsub and o by reference recursively. |
540 | 0 | iradix_r(radix_xsub, o+itmp, thisgrpn, nextradix); |
541 | 0 | } |
542 | 0 | itmp = thiscounts[i]; |
543 | 0 | thiscounts[i] = 0; |
544 | 0 | } |
545 | 0 | if (nalast == 0) // nalast = 1, -1 are both taken care already. |
546 | | // nalast = 0 is dealt with separately as it just sets o to 0 |
547 | 0 | for (int i = 0; i < n; i++) |
548 | 0 | o[i] = (x[o[i] - 1] == NA_INTEGER) ? 0 : o[i]; |
549 | | // at those indices where x is NA. x[o[i]-1] because x is not |
550 | | // modified by reference unlike iinsert or iradix_r |
551 | 0 | } |
552 | | |
553 | | static void iradix_r(int *xsub, int *osub, int n, int radix) |
554 | | // xsub is a recursive offset into xsub working memory above in |
555 | | // iradix, reordered by reference. osub is a an offset into the main |
556 | | // answer o, reordered by reference. radix iterates 3,2,1,0 |
557 | 0 | { |
558 | 0 | int j, itmp, thisx, thisgrpn, nextradix, shift; |
559 | 0 | unsigned int *thiscounts; |
560 | | |
561 | | // N_SMALL=200 is guess based on limited testing. Needs |
562 | | // calibrate(). Was 50 based on sum(1:50)=1275 worst -vs- 256 |
563 | | // cummulate + 256 memset + allowance since reverse order is |
564 | | // unlikely. when nalast==0, iinsert will be called only from |
565 | | // within iradix. |
566 | 0 | if (n < N_SMALL) { |
567 | 0 | iinsert(xsub, osub, n); |
568 | 0 | return; |
569 | 0 | } |
570 | | |
571 | 0 | shift = radix * 8; |
572 | 0 | thiscounts = radixcounts[radix]; |
573 | |
|
574 | 0 | for (int i = 0; i < n; i++) { |
575 | 0 | thisx = (unsigned int) xsub[i] - INT_MIN; // sequential in xsub |
576 | 0 | thiscounts[thisx >> shift & 0xFF]++; |
577 | 0 | } |
578 | 0 | itmp = thiscounts[0]; |
579 | 0 | for (int i = 1; itmp < n && i < 256; i++) |
580 | | // don't cummulate through 0s, important below |
581 | 0 | if (thiscounts[i]) |
582 | 0 | thiscounts[i] = (itmp += thiscounts[i]); |
583 | 0 | for (int i = n - 1; i >= 0; i--) { |
584 | 0 | thisx = ((unsigned int) xsub[i] - INT_MIN) >> shift & 0xFF; |
585 | 0 | j = --thiscounts[thisx]; |
586 | 0 | otmp[j] = osub[i]; |
587 | 0 | ((int *) xtmp)[j] = xsub[i]; |
588 | 0 | } |
589 | 0 | memcpy(osub, otmp, n * sizeof(int)); |
590 | 0 | memcpy(xsub, xtmp, n * sizeof(int)); |
591 | |
|
592 | 0 | nextradix = radix - 1; |
593 | 0 | while (nextradix >= 0 && skip[nextradix]) nextradix--; |
594 | | /* TO DO: If nextradix == -1 AND no further args from do_radixsort AND |
595 | | !retGrp, we're done. We have o. Remember to memset thiscounts |
596 | | before returning. */ |
597 | |
|
598 | 0 | if (thiscounts[0] != 0) |
599 | 0 | Error("Logical error. thiscounts[0]=%d but should have been decremented to 0. radix=%d", |
600 | 0 | thiscounts[0], radix); |
601 | 0 | thiscounts[256] = n; |
602 | 0 | itmp = 0; |
603 | 0 | for (int i = 1; itmp < n && i <= 256; i++) { |
604 | 0 | if (thiscounts[i] == 0) |
605 | 0 | continue; |
606 | 0 | thisgrpn = thiscounts[i] - itmp; // undo cummulate; i.e. diff |
607 | 0 | if (thisgrpn == 1 || nextradix == -1) { |
608 | 0 | push(thisgrpn); |
609 | 0 | } else { |
610 | 0 | iradix_r(xsub+itmp, osub+itmp, thisgrpn, nextradix); |
611 | 0 | } |
612 | 0 | itmp = thiscounts[i]; |
613 | 0 | thiscounts[i] = 0; |
614 | 0 | } |
615 | 0 | } |
616 | | |
617 | | // dradix from Arun's fastradixdouble.c |
618 | | // + changed to MSD and hooked into do_radixsort framework here. |
619 | | // + replaced tolerance with rounding s.f. |
620 | | |
621 | | static unsigned long long dmask1; |
622 | | static unsigned long long dmask2; |
623 | | |
624 | | static void setNumericRounding(int dround) |
625 | 12 | { |
626 | 12 | dmask1 = dround ? 1 << (8 * dround - 1) : 0; |
627 | 12 | dmask2 = 0xffffffffffffffff << dround * 8; |
628 | 12 | } |
629 | | |
630 | | static union { |
631 | | double d; |
632 | | unsigned long long ull; |
633 | | } u; |
634 | | |
635 | | static |
636 | | unsigned long long dtwiddle(void *p, int i, int order) |
637 | 0 | { |
638 | 0 | u.d = order * ((double *)p)[i]; // take care of 'order' at the beginning |
639 | 0 | if (R_FINITE(u.d)) { |
640 | 0 | u.ull = (u.d != 0.0) ? u.ull + ((u.ull & dmask1) << 1) : 0; |
641 | 0 | } else if (ISNAN(u.d)) { |
642 | 0 | u.ull = 0; |
643 | 0 | return (nalast == 1 ? ~u.ull : u.ull); |
644 | 0 | } |
645 | 0 | unsigned long long mask = (u.ull & 0x8000000000000000) ? |
646 | | // always flip sign bit and if negative (sign bit was set) |
647 | | // flip other bits too |
648 | 0 | 0xffffffffffffffff : 0x8000000000000000; |
649 | 0 | return ((u.ull ^ mask) & dmask2); |
650 | 0 | } |
651 | | |
652 | | static bool dnan(void *p, int i) |
653 | 0 | { |
654 | 0 | u.d = ((double *) p)[i]; |
655 | 0 | return (ISNAN(u.d)); |
656 | 0 | } |
657 | | |
658 | | static unsigned long long (*twiddle) (void *, int, int); |
659 | | static bool(*is_nan) (void *, int); |
660 | | // the size of the arg type (4 or 8). Just 8 currently until iradix is |
661 | | // merged in. |
662 | | static size_t colSize = 8; |
663 | | |
664 | | static void dradix_r(unsigned char *xsub, int *osub, int n, int radix); |
665 | | |
666 | | #ifdef WORDS_BIGENDIAN |
667 | | #define RADIX_BYTE colSize - radix - 1 |
668 | | #else |
669 | 0 | #define RADIX_BYTE radix |
670 | | #endif |
671 | | |
672 | | static void dradix(unsigned char *x, int *o, int n) |
673 | 0 | { |
674 | 0 | int radix, nextradix, itmp, thisgrpn, maxgrpn; |
675 | 0 | unsigned int *thiscounts; |
676 | 0 | unsigned long long thisx = 0; |
677 | | // see comments in iradix for structure. This follows the same. |
678 | | // TO DO: merge iradix in here (almost ready) |
679 | 0 | for (int i = 0; i < n; i++) { |
680 | 0 | thisx = twiddle(x, i, order); |
681 | 0 | for (radix = 0; radix < colSize; radix++) |
682 | | // if dround == 2 then radix 0 and 1 will be all 0 here and skipped. |
683 | | /* on little endian, 0 is the least significant bits (the right) |
684 | | and 7 is the most including sign (the left); i.e. reversed. */ |
685 | 0 | radixcounts[radix][((unsigned char *)&thisx)[RADIX_BYTE]]++; |
686 | 0 | } |
687 | 0 | for (radix = 0; radix < colSize; radix++) { |
688 | | // thisx is the last x after loop above |
689 | 0 | int i = ((unsigned char *) &thisx)[RADIX_BYTE]; |
690 | 0 | skip[radix] = radixcounts[radix][i] == n; |
691 | | // clear it now, the other counts must be 0 already |
692 | 0 | if (skip[radix]) |
693 | 0 | radixcounts[radix][i] = 0; |
694 | 0 | } |
695 | 0 | radix = (int) colSize - 1; // MSD |
696 | 0 | while (radix >= 0 && skip[radix]) radix--; |
697 | 0 | if (radix == -1) { |
698 | | // All radix are skipped; i.e. one number repeated n times. |
699 | 0 | if (nalast == 0 && is_nan(x, 0)) |
700 | | // all values are identical. return 0 if nalast=0 & all NA |
701 | | // because of 'return', have to take care of it here. |
702 | 0 | for (int i = 0; i < n; i++) |
703 | 0 | o[i] = 0; |
704 | 0 | else |
705 | 0 | for (int i = 0; i < n; i++) |
706 | 0 | o[i] = (i + 1); |
707 | 0 | push(n); |
708 | 0 | return; |
709 | 0 | } |
710 | 0 | for (int i = radix - 1; i >= 0; i--) { |
711 | | // clear the lower radix counts, we only did them to know |
712 | | // skip. will be reused within each group |
713 | 0 | if (!skip[i]) |
714 | 0 | memset(radixcounts[i], 0, 257 * sizeof(unsigned int)); |
715 | 0 | } |
716 | 0 | thiscounts = radixcounts[radix]; |
717 | 0 | itmp = thiscounts[0]; |
718 | 0 | maxgrpn = itmp; |
719 | 0 | for (int i = 1; itmp < n && i < 256; i++) { |
720 | 0 | thisgrpn = thiscounts[i]; |
721 | 0 | if (thisgrpn) { // don't cummulate through 0s, important below |
722 | 0 | if (thisgrpn > maxgrpn) |
723 | 0 | maxgrpn = thisgrpn; |
724 | 0 | thiscounts[i] = (itmp += thisgrpn); |
725 | 0 | } |
726 | 0 | } |
727 | 0 | for (int i = n - 1; i >= 0; i--) { |
728 | 0 | thisx = twiddle(x, i, order); |
729 | 0 | o[ --thiscounts[((unsigned char *)&thisx)[RADIX_BYTE]] ] = i + 1; |
730 | 0 | } |
731 | |
|
732 | 0 | if (radix_xsuballoc < maxgrpn) { |
733 | | // TO DO: centralize this alloc |
734 | | // The largest group according to the first non-skipped radix, |
735 | | // so could be big (if radix is needed on first arg) TO DO: |
736 | | // could include extra bits to divide the first radix up |
737 | | // more. Often the MSD has groups in just 0-4 out of 256. |
738 | | // free'd at the end of do_radixsort once we're done calling iradix |
739 | | // repetitively |
740 | 0 | radix_xsub = (double *) realloc(radix_xsub, maxgrpn * sizeof(double)); |
741 | 0 | if (!radix_xsub) |
742 | 0 | Error("Failed to realloc working memory %d*8bytes (xsub in dradix), radix=%d", |
743 | 0 | maxgrpn, radix); |
744 | 0 | radix_xsuballoc = maxgrpn; |
745 | 0 | } |
746 | |
|
747 | 0 | alloc_otmp(maxgrpn); // TO DO: leave to do_radixsort and remove these? |
748 | 0 | alloc_xtmp(maxgrpn); |
749 | |
|
750 | 0 | nextradix = radix - 1; |
751 | 0 | while (nextradix >= 0 && skip[nextradix]) |
752 | 0 | nextradix--; |
753 | 0 | if (thiscounts[0] != 0) |
754 | 0 | Error("Logical error. thiscounts[0]=%d but should have been decremented to 0. dradix=%d", |
755 | 0 | thiscounts[0], radix); |
756 | 0 | thiscounts[256] = n; |
757 | 0 | itmp = 0; |
758 | 0 | for (int i = 1; itmp < n && i <= 256; i++) { |
759 | 0 | if (thiscounts[i] == 0) |
760 | 0 | continue; |
761 | 0 | thisgrpn = thiscounts[i] - itmp; // undo cummulate; i.e. diff |
762 | 0 | if (thisgrpn == 1 || nextradix == -1) { |
763 | 0 | push(thisgrpn); |
764 | 0 | } else { |
765 | 0 | if (colSize == 4) { // ready for merging in iradix ... |
766 | 0 | error("Not yet used, still using iradix instead"); |
767 | 0 | for (int j = 0; j < thisgrpn; j++) |
768 | 0 | ((int *)radix_xsub)[j] = (int)twiddle(x, o[itmp+j]-1, order); |
769 | | // this is why this xsub here can't be the same memory |
770 | | // as xsub in do_radixsort |
771 | 0 | } else |
772 | 0 | for (int j = 0; j < thisgrpn; j++) |
773 | 0 | ((unsigned long long *)radix_xsub)[j] = |
774 | 0 | twiddle(x, o[itmp+j]-1, order); |
775 | | // changes xsub and o by reference recursively. |
776 | 0 | dradix_r(radix_xsub, o+itmp, thisgrpn, nextradix); |
777 | 0 | } |
778 | 0 | itmp = thiscounts[i]; |
779 | 0 | thiscounts[i] = 0; |
780 | 0 | } |
781 | 0 | if (nalast == 0) // nalast = 1, -1 are both taken care already. |
782 | 0 | for (int i = 0; i < n; i++) |
783 | 0 | o[i] = is_nan(x, o[i] - 1) ? 0 : o[i]; |
784 | | // nalast = 0 is dealt with separately as it just sets o to 0 |
785 | | // at those indices where x is NA. x[o[i]-1] because x is not |
786 | | // modified by reference unlike iinsert or iradix_r |
787 | |
|
788 | 0 | } |
789 | | |
790 | | static void dinsert(unsigned long long *x, int *o, int n) |
791 | | // orders both x and o by reference in-place. Fast for small vectors, |
792 | | // low overhead. don't be tempted to binsearch backwards here, have |
793 | | // to shift anyway; many memmove would have overhead and do the same |
794 | | // thing 'dinsert' will not be called when nalast = 0 and o[0] = -1. |
795 | 0 | { |
796 | 0 | int otmp, tt; |
797 | 0 | unsigned long long xtmp; |
798 | 0 | for (int i = 1; i < n; i++) { |
799 | 0 | xtmp = x[i]; |
800 | 0 | if (xtmp < x[i - 1]) { |
801 | 0 | int j = i - 1; |
802 | 0 | otmp = o[i]; |
803 | 0 | while (j >= 0 && xtmp < x[j]) { |
804 | 0 | x[j + 1] = x[j]; |
805 | 0 | o[j + 1] = o[j]; |
806 | 0 | j--; |
807 | 0 | } |
808 | 0 | x[j + 1] = xtmp; |
809 | 0 | o[j + 1] = otmp; |
810 | 0 | } |
811 | 0 | } |
812 | 0 | tt = 0; |
813 | 0 | for (int i = 1; i < n; i++) |
814 | 0 | if (x[i] == x[i - 1]) |
815 | 0 | tt++; |
816 | 0 | else { |
817 | 0 | push(tt + 1); |
818 | 0 | tt = 0; |
819 | 0 | } |
820 | 0 | push(tt + 1); |
821 | 0 | } |
822 | | |
823 | | static void dradix_r(unsigned char *xsub, int *osub, int n, int radix) |
824 | | /* xsub is a recursive offset into xsub working memory above in |
825 | | dradix, reordered by reference. osub is a an offset into the main |
826 | | answer o, reordered by reference. dradix iterates |
827 | | 7,6,5,4,3,2,1,0 */ |
828 | 0 | { |
829 | 0 | int itmp, thisgrpn, nextradix; |
830 | 0 | unsigned int *thiscounts; |
831 | 0 | unsigned char *p; |
832 | 0 | if (n < 200) { |
833 | | /* 200 is guess based on limited testing. Needs calibrate(). Was 50 |
834 | | based on sum(1:50)=1275 worst -vs- 256 cummulate + 256 memset + |
835 | | allowance since reverse order is unlikely */ |
836 | | // order=1 here because it's already taken care of in iradix |
837 | 0 | dinsert((void *)xsub, osub, n); |
838 | |
|
839 | 0 | return; |
840 | 0 | } |
841 | 0 | thiscounts = radixcounts[radix]; |
842 | 0 | p = xsub + RADIX_BYTE; |
843 | 0 | for (int i = 0; i < n; i++) { |
844 | 0 | thiscounts[*p]++; |
845 | 0 | p += colSize; |
846 | 0 | } |
847 | 0 | itmp = thiscounts[0]; |
848 | 0 | for (int i = 1; itmp < n && i < 256; i++) |
849 | | // don't cummulate through 0s, important below |
850 | 0 | if (thiscounts[i]) |
851 | 0 | thiscounts[i] = (itmp += thiscounts[i]); |
852 | 0 | p = xsub + (n - 1) * colSize; |
853 | 0 | if (colSize == 4) { |
854 | 0 | error("Not yet used, still using iradix instead"); |
855 | 0 | for (int i = n - 1; i >= 0; i--) { |
856 | 0 | int j = --thiscounts[*(p + RADIX_BYTE)]; |
857 | 0 | otmp[j] = osub[i]; |
858 | 0 | ((int *) xtmp)[j] = *(int *) p; |
859 | 0 | p -= colSize; |
860 | 0 | } |
861 | 0 | } else { |
862 | 0 | for (int i = n - 1; i >= 0; i--) { |
863 | 0 | int j = --thiscounts[*(p + RADIX_BYTE)]; |
864 | 0 | otmp[j] = osub[i]; |
865 | 0 | ((unsigned long long *) xtmp)[j] = *(unsigned long long *) p; |
866 | 0 | p -= colSize; |
867 | 0 | } |
868 | 0 | } |
869 | 0 | memcpy(osub, otmp, n * sizeof(int)); |
870 | 0 | memcpy(xsub, xtmp, n * colSize); |
871 | |
|
872 | 0 | nextradix = radix - 1; |
873 | 0 | while (nextradix >= 0 && skip[nextradix]) |
874 | 0 | nextradix--; |
875 | | // TO DO: If nextradix==-1 and no further args from do_radixsort, |
876 | | // we're done. We have o. Remember to memset thiscounts before |
877 | | // returning. |
878 | |
|
879 | 0 | if (thiscounts[0] != 0) |
880 | 0 | Error("Logical error. thiscounts[0]=%d but should have been decremented to 0. radix=%d", |
881 | 0 | thiscounts[0], radix); |
882 | 0 | thiscounts[256] = n; |
883 | 0 | itmp = 0; |
884 | 0 | for (int i = 1; itmp < n && i <= 256; i++) { |
885 | 0 | if (thiscounts[i] == 0) |
886 | 0 | continue; |
887 | 0 | thisgrpn = thiscounts[i] - itmp; // undo cummulate; i.e. diff |
888 | 0 | if (thisgrpn == 1 || nextradix == -1) |
889 | 0 | push(thisgrpn); |
890 | 0 | else |
891 | 0 | dradix_r(xsub + itmp * colSize, osub + itmp, thisgrpn, |
892 | 0 | nextradix); |
893 | 0 | itmp = thiscounts[i]; |
894 | 0 | thiscounts[i] = 0; |
895 | 0 | } |
896 | 0 | } |
897 | | |
898 | | // TO DO?: dcount. Find step size, then range = (max-min)/step and |
899 | | // proceed as icount. Many fixed precision floats (such as prices) may |
900 | | // be suitable. Fixed precision such as 1.10, 1.15, 1.20, 1.25, 1.30 |
901 | | // ... do use all bits so dradix skipping may not help. |
902 | | |
903 | | static int *cradix_counts = NULL; |
904 | | static int cradix_counts_alloc = 0; |
905 | | static int maxlen = 1; |
906 | | static SEXP *cradix_xtmp = NULL; |
907 | | static int cradix_xtmp_alloc = 0; |
908 | | |
909 | | // same as StrCmp but also takes into account 'decreasing' and 'na.last' args. |
910 | | static int StrCmp2(SEXP x, SEXP y) |
911 | 0 | { |
912 | | // same cached pointer (including NA_STRING == NA_STRING) |
913 | 0 | if (x == y) return 0; |
914 | | // if x=NA, nalast=1 ? then x > y else x < y (Note: nalast == 0 is |
915 | | // already taken care of in 'csorted', won't be 0 here) |
916 | 0 | if (x == NA_STRING) return nalast; |
917 | 0 | if (y == NA_STRING) return -nalast; // if y=NA, nalast=1 ? then y > x |
918 | 0 | return order*strcmp(CHAR(x), CHAR(y)); // same as explanation in StrCmp |
919 | 0 | } |
920 | | |
921 | | static int StrCmp(SEXP x, SEXP y) // also used by bmerge and chmatch |
922 | 0 | { |
923 | | // same cached pointer (including NA_STRING == NA_STRING) |
924 | 0 | if (x == y) return 0; |
925 | 0 | if (x == NA_STRING) return -1; // x < y |
926 | 0 | if (y == NA_STRING) return 1; // x > y |
927 | | // assumes strings are in same encoding |
928 | 0 | return strcmp(CHAR(x), CHAR(y)); |
929 | 0 | } |
930 | | |
931 | 0 | #define CHAR_ENCODING(x) (IS_ASCII(x) ? CE_UTF8 : getCharCE(x)) |
932 | | |
933 | | static void checkEncodings(SEXP x) |
934 | 0 | { |
935 | 0 | cetype_t ce; |
936 | |
|
937 | 0 | int i; |
938 | 0 | for (i = 0; i < length(x) && STRING_ELT(x, i) == NA_STRING; i++); |
939 | |
|
940 | 0 | if (i < length(x)) { |
941 | 0 | ce = CHAR_ENCODING(STRING_ELT(x, i)); |
942 | 0 | if (ce == CE_NATIVE) { |
943 | 0 | error(_("Character encoding must be UTF-8, Latin-1 or bytes")); |
944 | 0 | } |
945 | 0 | } |
946 | | |
947 | | /* Disabled for now -- doubles the time (for already sorted vectors): why? |
948 | | for (int i = 1; i < length(x); i++) { |
949 | | if (ce != CHAR_ENCODING(STRING_ELT(x, i))) { |
950 | | error(_("Mixed character encodings are not supported")); |
951 | | } |
952 | | } |
953 | | */ |
954 | 0 | } |
955 | | |
956 | | static void cradix_r(SEXP * xsub, int n, int radix) |
957 | | // xsub is a unique set of CHARSXP, to be ordered by reference |
958 | | |
959 | | // First time, radix == 0, and xsub == x. Then recursively moves SEXP together |
960 | | // for L1 cache efficiency. |
961 | | |
962 | | // Quite different to iradix because |
963 | | // 1) x is known to be unique so fits in cache |
964 | | // (wide random access not an issue) |
965 | | // 2) they're variable length character strings |
966 | | // 3) no need to maintain o. Just simply reorder x. No grps or push. |
967 | | |
968 | | // Fortunately, UTF sorts in the same order if treated as ASCII, so we |
969 | | // can simplify by doing it by bytes. |
970 | | |
971 | | // TO DO: confirm a forwards (MSD) radix for efficiency, although more |
972 | | // complicated. |
973 | | |
974 | | // This part has nothing to do with truelength. The |
975 | | // truelength stuff is to do with finding the unique strings. We may |
976 | | // be able to improve CHARSXP derefencing by submitting patch to R to |
977 | | // make R's string cache contiguous but would likely be difficult. If |
978 | | // we strxfrm, then it'll then be contiguous and compact then anyway. |
979 | 0 | { |
980 | 0 | int itmp, *thiscounts, thisgrpn=0, thisx=0; |
981 | 0 | SEXP stmp; |
982 | | |
983 | | // TO DO?: chmatch to existing sorted vector, then grow it. |
984 | | // TO DO?: if (n<N_SMALL = 200) insert sort, then loop through groups via == |
985 | 0 | if (n <= 1) return; |
986 | 0 | if (n == 2) { |
987 | 0 | if (StrCmp(xsub[1], xsub[0]) < 0) { |
988 | 0 | stmp = xsub[0]; |
989 | 0 | xsub[0] = xsub[1]; |
990 | 0 | xsub[1] = stmp; |
991 | 0 | } |
992 | 0 | return; |
993 | 0 | } |
994 | | // TO DO: if (n < 50) cinsert (continuing from radix offset into |
995 | | // CHAR) or using StrCmp. But 256 is narrow, so quick and not too |
996 | | // much an issue. |
997 | | |
998 | 0 | thiscounts = cradix_counts + radix * 256; |
999 | 0 | for (int i = 0; i < n; i++) { |
1000 | 0 | thisx = xsub[i] == NA_STRING ? |
1001 | 0 | 0 : (radix < LENGTH(xsub[i]) ? |
1002 | 0 | (unsigned char) (CHAR(xsub[i])[radix]) : 1); |
1003 | 0 | thiscounts[ thisx ]++; // 0 for NA, 1 for "" |
1004 | 0 | } |
1005 | | // this also catches when subx has shorter strings than the rest, |
1006 | | // thiscounts[0] == n and we'll recurse very quickly through to the |
1007 | | // overall maxlen with no 256 overhead each time |
1008 | 0 | if (thiscounts[thisx] == n && radix < maxlen - 1) { |
1009 | 0 | cradix_r(xsub, n, radix + 1); |
1010 | 0 | thiscounts[thisx] = 0; // the rest must be 0 already, save the memset |
1011 | 0 | return; |
1012 | 0 | } |
1013 | 0 | itmp = thiscounts[0]; |
1014 | 0 | for (int i = 1; i < 256; i++) |
1015 | | // don't cummulate through 0s, important below |
1016 | 0 | if (thiscounts[i]) |
1017 | 0 | thiscounts[i] = (itmp += thiscounts[i]); |
1018 | 0 | for (int i = n - 1; i >= 0; i--) { |
1019 | 0 | thisx = xsub[i] == NA_STRING ? |
1020 | 0 | 0 : (radix < LENGTH(xsub[i]) ? |
1021 | 0 | (unsigned char) (CHAR(xsub[i])[radix]) : 1); |
1022 | 0 | int j = --thiscounts[thisx]; |
1023 | 0 | cradix_xtmp[j] = xsub[i]; |
1024 | 0 | } |
1025 | 0 | memcpy(xsub, cradix_xtmp, n * sizeof(SEXP)); |
1026 | 0 | if (radix == maxlen - 1) { |
1027 | 0 | memset(thiscounts, 0, 256 * sizeof(int)); |
1028 | 0 | return; |
1029 | 0 | } |
1030 | 0 | if (thiscounts[0] != 0) |
1031 | 0 | Error("Logical error. counts[0]=%d in cradix but should have been decremented to 0. radix=%d", |
1032 | 0 | thiscounts[0], radix); |
1033 | 0 | itmp = 0; |
1034 | 0 | for (int i = 1; i < 256; i++) { |
1035 | 0 | if (thiscounts[i] == 0) |
1036 | 0 | continue; |
1037 | 0 | thisgrpn = thiscounts[i] - itmp; // undo cummulate; i.e. diff |
1038 | 0 | cradix_r(xsub + itmp, thisgrpn, radix + 1); |
1039 | 0 | itmp = thiscounts[i]; |
1040 | | // set to 0 now since we're here, saves memset |
1041 | | // afterwards. Important to clear! Also more portable for |
1042 | | // machines where 0 isn't all bits 0 (?!) |
1043 | 0 | thiscounts[i] = 0; |
1044 | 0 | } |
1045 | 0 | if (itmp < n - 1) |
1046 | 0 | cradix_r(xsub + itmp, n - itmp, radix + 1); // final group |
1047 | 0 | } |
1048 | | |
1049 | | static SEXP *ustr = NULL; |
1050 | | static int ustr_alloc = 0, ustr_n = 0; |
1051 | | |
1052 | | static void cgroup(SEXP * x, int *o, int n) |
1053 | | // As icount : |
1054 | | // Places the ordering into o directly, overwriting whatever was there |
1055 | | // Doesn't change x |
1056 | | // Pushes group sizes onto stack |
1057 | | |
1058 | | // Only run when sortStr == FALSE. Basically a counting sort, in first |
1059 | | // appearance order, directly. Since it doesn't sort the strings, the |
1060 | | // name is cgroup. there is no _pre for this. ustr created and |
1061 | | // cleared each time. |
1062 | 0 | { |
1063 | | // savetl_init() is called once at the start of do_radixsort |
1064 | 0 | if (ustr_n != 0) |
1065 | 0 | Error |
1066 | 0 | ("Internal error. ustr isn't empty when starting cgroup: ustr_n=%d, ustr_alloc=%d", |
1067 | 0 | ustr_n, ustr_alloc); |
1068 | 0 | for (int i = 0; i < n; i++) { |
1069 | 0 | SEXP s = x[i]; |
1070 | 0 | if (TRLEN(s) < 0) { // this case first as it's the most frequent |
1071 | 0 | SET_TRLEN(s, TRLEN(s) - 1); |
1072 | | // use negative counts so as to detect R's own (positive) |
1073 | | // usage of tl on CHARSXP |
1074 | 0 | continue; |
1075 | 0 | } |
1076 | 0 | if (TRLEN(s) > 0) { |
1077 | | // Save any of R's own usage of tl (assumed positive, so |
1078 | | // we can both count and save in one scan), to restore |
1079 | | // afterwards. From R 2.14.0, tl is initialized to 0, |
1080 | | // prior to that it was random so this step saved too much. |
1081 | 0 | savetl(s); |
1082 | 0 | SET_TRLEN(s, 0); |
1083 | 0 | } |
1084 | 0 | if (ustr_alloc <= ustr_n) { |
1085 | | // 10000 = 78k of 8byte pointers. Small initial guess, |
1086 | | // negligible time to alloc. |
1087 | 0 | ustr_alloc = (ustr_alloc == 0) ? 10000 : ustr_alloc*2; |
1088 | 0 | if (ustr_alloc > n) |
1089 | 0 | ustr_alloc = n; |
1090 | 0 | ustr = realloc(ustr, ustr_alloc * sizeof(SEXP)); |
1091 | 0 | if (ustr == NULL) |
1092 | 0 | Error("Unable to realloc %d * %d bytes in cgroup", ustr_alloc, |
1093 | 0 | (int)sizeof(SEXP)); |
1094 | 0 | } |
1095 | 0 | SET_TRLEN(s, -1); |
1096 | 0 | ustr[ustr_n++] = s; |
1097 | 0 | } |
1098 | | // TO DO: the same string in different encodings will be |
1099 | | // considered different here. Sweep through ustr and merge counts |
1100 | | // where equal (sort needed therefore, unfortunately?, only if |
1101 | | // there are any marked encodings present) |
1102 | 0 | int cumsum = 0; |
1103 | 0 | for (int i = 0; i < ustr_n; i++) { // 0.000 |
1104 | 0 | push(-TRLEN(ustr[i])); |
1105 | 0 | SET_TRLEN(ustr[i], cumsum += -TRLEN(ustr[i])); |
1106 | 0 | } |
1107 | 0 | int *target = (o[0] != -1) ? newo : o; |
1108 | 0 | for (int i = n - 1; i >= 0; i--) { |
1109 | 0 | SEXP s = x[i]; // 0.400 (page fetches on string cache) |
1110 | 0 | int k = TRLEN(s) - 1; |
1111 | 0 | SET_TRLEN(s, k); |
1112 | 0 | target[k] = i + 1; // 0.800 (random access to o) |
1113 | 0 | } |
1114 | | // The cummulate meant counts are left non zero, so reset for next |
1115 | | // time (0.00s). |
1116 | 0 | for (int i = 0; i < ustr_n; i++) |
1117 | 0 | SET_TRLEN(ustr[i], 0); |
1118 | 0 | ustr_n = 0; |
1119 | 0 | } |
1120 | | |
1121 | | static int *csort_otmp = NULL, csort_otmp_alloc = 0; |
1122 | | static void alloc_csort_otmp(int n) |
1123 | 0 | { |
1124 | 0 | if (csort_otmp_alloc >= n) |
1125 | 0 | return; |
1126 | 0 | csort_otmp = (int *) realloc(csort_otmp, n * sizeof(int)); |
1127 | 0 | if (csort_otmp == NULL) |
1128 | 0 | Error |
1129 | 0 | ("Failed to allocate working memory for csort_otmp. Requested %d * %d bytes", |
1130 | 0 | n, (int)sizeof(int)); |
1131 | 0 | csort_otmp_alloc = n; |
1132 | 0 | } |
1133 | | |
1134 | | static void csort(SEXP * x, int *o, int n) |
1135 | | /* |
1136 | | As icount : |
1137 | | Places the ordering into o directly, overwriting whatever was there |
1138 | | Doesn't change x |
1139 | | Pushes group sizes onto stack |
1140 | | Requires csort_pre() to have created and sorted ustr already |
1141 | | */ |
1142 | 0 | { |
1143 | | /* can't use otmp, since iradix might be called here and that uses |
1144 | | otmp (and xtmp). alloc_csort_otmp(n) is called from do_radixsort for |
1145 | | either n=nrow if 1st arg, or n=maxgrpn if onwards args */ |
1146 | 0 | for (int i = 0; i < n; i++) |
1147 | 0 | csort_otmp[i] = (x[i] == NA_STRING) ? NA_INTEGER : -TRLEN(x[i]); |
1148 | 0 | if (nalast == 0 && n == 2) { |
1149 | | // special case for nalast == 0. n == 1 is handled inside |
1150 | | // do_radixsort. at least 1 will be NA here else use o from caller |
1151 | | // directly (not 1st arg) |
1152 | 0 | if (o[0] == -1) |
1153 | 0 | for (int i = 0; i < n; i++) |
1154 | 0 | o[i] = i + 1; |
1155 | 0 | for (int i = 0; i < n; i++) |
1156 | 0 | if (csort_otmp[i] == NA_INTEGER) |
1157 | 0 | o[i] = 0; |
1158 | 0 | push(1); push(1); |
1159 | 0 | return; |
1160 | 0 | } |
1161 | 0 | if (n < N_SMALL && nalast != 0) { // TO DO: calibrate() N_SMALL=200 |
1162 | 0 | if (o[0] == -1) |
1163 | 0 | for (int i = 0; i < n; i++) |
1164 | 0 | o[i] = i + 1; |
1165 | | // else use o from caller directly (not 1st arg) |
1166 | 0 | for (int i = 0; i < n; i++) |
1167 | 0 | csort_otmp[i] = icheck(csort_otmp[i]); |
1168 | 0 | iinsert(csort_otmp, o, n); |
1169 | 0 | } else { |
1170 | 0 | setRange(csort_otmp, n); |
1171 | 0 | if (range == NA_INTEGER) |
1172 | 0 | Error("Internal error. csort's otmp contains all-NA"); |
1173 | 0 | int *target = (o[0] != -1) ? newo : o; |
1174 | 0 | if (range <= N_RANGE) |
1175 | | // TO DO: calibrate(). radix was faster (9.2s |
1176 | | // "range<=10000" instead of 11.6s "range<=N_RANGE && |
1177 | | // range<n") for run(7) where range=N_RANGE n=10000000 |
1178 | 0 | icount(csort_otmp, target, n); |
1179 | 0 | else |
1180 | 0 | iradix(csort_otmp, target, n); |
1181 | 0 | } |
1182 | | // all i* push onto stack. Using their counts may be faster here |
1183 | | // than thrashing SEXP fetches over several passes as cgroup does |
1184 | | // (but cgroup needs that to keep original order, and cgroup saves |
1185 | | // the sort in csort_pre). |
1186 | 0 | } |
1187 | | |
1188 | | static void csort_pre(SEXP * x, int n) |
1189 | | // Finds ustr and sorts it. Runs once for each arg (if |
1190 | | // sortStr == TRUE), then ustr is used by csort within each group ustr |
1191 | | // is grown on each character arg, to save sorting the same strings |
1192 | | // again if several args contain the same strings |
1193 | 0 | { |
1194 | 0 | SEXP s; |
1195 | 0 | int old_un, new_un; |
1196 | | // savetl_init() is called once at the start of do_radixsort |
1197 | 0 | old_un = ustr_n; |
1198 | 0 | for (int i = 0; i < n; i++) { |
1199 | 0 | s = x[i]; |
1200 | | // this case first as it's the most frequent. Already in ustr, |
1201 | | // this negative is its ordering. |
1202 | 0 | if (TRLEN(s) < 0) |
1203 | 0 | continue; |
1204 | | // Save any of R's own usage of tl (assumed positive, so we |
1205 | | // can both count and save in one scan), to restore |
1206 | | // afterwards. From R 2.14.0, tl is initialized to 0, prior to |
1207 | | // that it was random so this step saved too much. |
1208 | 0 | if (TRLEN(s) > 0) { |
1209 | 0 | savetl(s); |
1210 | 0 | SET_TRLEN(s, 0); |
1211 | 0 | } |
1212 | 0 | if (ustr_alloc <= ustr_n) { |
1213 | | // 10000 = 78k of 8byte pointers. Small initial guess, |
1214 | | // negligible time to alloc. |
1215 | 0 | ustr_alloc = (ustr_alloc == 0) ? 10000 : ustr_alloc*2; |
1216 | 0 | if (ustr_alloc > old_un+n) |
1217 | 0 | ustr_alloc = old_un + n; |
1218 | 0 | ustr = realloc(ustr, ustr_alloc * sizeof(SEXP)); |
1219 | 0 | if (ustr == NULL) |
1220 | 0 | Error("Failed to realloc ustr. Requested %d * %d bytes", |
1221 | 0 | ustr_alloc, (int)sizeof(SEXP)); |
1222 | 0 | } |
1223 | 0 | SET_TRLEN(s, -1); // this -1 will become its ordering later below |
1224 | 0 | ustr[ustr_n++] = s; |
1225 | | // length on CHARSXP is the nchar of char * (excluding \0), |
1226 | | // and treats marked encodings as if ascii. |
1227 | 0 | if (s != NA_STRING && LENGTH(s) > maxlen) |
1228 | 0 | maxlen = LENGTH(s); |
1229 | 0 | } |
1230 | 0 | new_un = ustr_n; |
1231 | 0 | if (new_un == old_un) |
1232 | 0 | return; |
1233 | | // No new strings observed, seen them all before in previous |
1234 | | // arg. ustr already sufficient. If we ever make ustr |
1235 | | // permanently held by data.table, we'll just need to make the |
1236 | | // final loop to set -i-1 before returning here. sort ustr. |
1237 | | |
1238 | | // TODO: just sort new ones and merge them in. These allocs are |
1239 | | // here, to save them being in the recursive cradix_r() |
1240 | 0 | if (cradix_counts_alloc < maxlen) { |
1241 | 0 | cradix_counts_alloc = maxlen + 10; // +10 to save too many reallocs |
1242 | 0 | cradix_counts = (int *)realloc(cradix_counts, |
1243 | 0 | cradix_counts_alloc * 256 * sizeof(int)); |
1244 | 0 | if (!cradix_counts) |
1245 | 0 | Error("Failed to alloc cradix_counts"); |
1246 | 0 | memset(cradix_counts, 0, cradix_counts_alloc * 256 * sizeof(int)); |
1247 | 0 | } |
1248 | 0 | if (cradix_xtmp_alloc < ustr_n) { |
1249 | 0 | cradix_xtmp = (SEXP *) realloc(cradix_xtmp, ustr_n * sizeof(SEXP)); |
1250 | | // TO DO: Reuse the one we have in do_radixsort. |
1251 | | // Does it need to be n length? |
1252 | 0 | if (!cradix_xtmp) |
1253 | 0 | Error("Failed to alloc cradix_tmp"); |
1254 | 0 | cradix_xtmp_alloc = ustr_n; |
1255 | 0 | } |
1256 | | // sorts ustr in-place by reference save ordering in the |
1257 | | // CHARSXP. negative so as to distinguish with R's own usage. |
1258 | 0 | cradix_r(ustr, ustr_n, 0); |
1259 | 0 | for (int i = 0; i < ustr_n; i++) |
1260 | 0 | SET_TRLEN(ustr[i], -i - 1); |
1261 | 0 | } |
1262 | | |
1263 | | // functions to test vectors for sortedness: isorted, dsorted and csorted |
1264 | | |
1265 | | // base:is.unsorted returns NA in the presence of any NA, but we need |
1266 | | // to consider na.last, and we also return -1 if x is sorted in |
1267 | | // _strictly_ reverse order; a common case we optimize. If a vector |
1268 | | // is in decreasing order *with ties*, then an in-place reverse (no |
1269 | | // sort) would result in instability of ties, so we are strict. We |
1270 | | // also save grouping information during the check; that information |
1271 | | // is required when sorting by multiple arguments. |
1272 | | |
1273 | | // TO DO: test in big steps first to return faster if unsortedness is |
1274 | | // at the end (a common case of rbind'ing data to end) These are all |
1275 | | // sequential access to x, so very quick and cache efficient. |
1276 | | |
1277 | | // order = 1 is ascending and order=-1 is descending; also takes care |
1278 | | // of na.last argument with check through 'icheck' Relies on |
1279 | | // NA_INTEGER == INT_MIN, checked in init.c |
1280 | | static int isorted(int *x, int n) |
1281 | 12 | { |
1282 | 12 | int i = 1, j = 0; |
1283 | | // when nalast = NA, |
1284 | | // all NAs ? return special value to replace all o's values with '0' |
1285 | | // any NAs ? return 0 = unsorted and leave it |
1286 | | // to sort routines to replace o's with 0's |
1287 | | // no NAs ? continue to check rest of isorted - the same routine as usual |
1288 | 12 | if (nalast == 0) { |
1289 | 0 | for (int k = 0; k < n; k++) |
1290 | 0 | if (x[k] != NA_INTEGER) |
1291 | 0 | j++; |
1292 | 0 | if (j == 0) { |
1293 | 0 | push(n); |
1294 | 0 | return (-2); |
1295 | 0 | } |
1296 | 0 | if (j != n) |
1297 | 0 | return (0); |
1298 | 0 | } |
1299 | 12 | if (n <= 1) { |
1300 | 0 | push(n); |
1301 | 0 | return (1); |
1302 | 0 | } |
1303 | 12 | if (icheck(x[1]) < icheck(x[0])) { |
1304 | 0 | i = 2; |
1305 | 0 | while (i < n && icheck(x[i]) < icheck(x[i - 1])) |
1306 | 0 | i++; |
1307 | | // strictly opposite to expected 'order', no ties; |
1308 | 0 | if (i == n) { |
1309 | 0 | mpush(1, n); |
1310 | 0 | return (-1); |
1311 | 0 | } |
1312 | | // e.g. no more than one NA at the beginning/end (for order=-1/1) |
1313 | 0 | else return (0); |
1314 | 0 | } |
1315 | 12 | int old = gsngrp[flip]; |
1316 | 12 | int tt = 1; |
1317 | 72 | for (int i = 1; i < n; i++) { |
1318 | 60 | if (icheck(x[i]) < icheck(x[i - 1])) { |
1319 | 0 | gsngrp[flip] = old; |
1320 | 0 | return (0); |
1321 | 0 | } |
1322 | 60 | if (x[i] == x[i - 1]) |
1323 | 60 | tt++; |
1324 | 0 | else { |
1325 | 0 | push(tt); tt = 1; |
1326 | 0 | } |
1327 | 60 | } |
1328 | 12 | push(tt); |
1329 | | // same as 'order', NAs at the beginning for order=1, at end for |
1330 | | // order=-1, possibly with ties |
1331 | 12 | return(1); |
1332 | 12 | } |
1333 | | |
1334 | | // order=1 is ascending and -1 is descending |
1335 | | // also accounts for nalast=0 (=NA), =1 (TRUE), -1 (FALSE) (in twiddle) |
1336 | | static int dsorted(double *x, int n) |
1337 | 0 | { |
1338 | 0 | int i = 1, j = 0; |
1339 | 0 | unsigned long long prev, this; |
1340 | 0 | if (nalast == 0) { |
1341 | | // when nalast = NA, |
1342 | | // all NAs ? return special value to replace all o's values with '0' |
1343 | | // any NAs ? return 0 = unsorted and leave it to sort routines to |
1344 | | // replace o's with 0's |
1345 | | // no NAs ? continue to check the rest of isorted - |
1346 | | // the same routine as usual |
1347 | 0 | for (int k = 0; k < n; k++) |
1348 | 0 | if (!is_nan(x, k)) |
1349 | 0 | j++; |
1350 | 0 | if (j == 0) { |
1351 | 0 | push(n); |
1352 | 0 | return (-2); |
1353 | 0 | } |
1354 | 0 | if (j != n) |
1355 | 0 | return (0); |
1356 | 0 | } |
1357 | 0 | if (n <= 1) { |
1358 | 0 | push(n); |
1359 | 0 | return (1); |
1360 | 0 | } |
1361 | 0 | prev = twiddle(x, 0, order); |
1362 | 0 | this = twiddle(x, 1, order); |
1363 | 0 | if (this < prev) { |
1364 | 0 | i = 2; |
1365 | 0 | prev = this; |
1366 | 0 | while (i < n && (this = twiddle(x, i, order)) < prev) { |
1367 | 0 | i++; |
1368 | 0 | prev = this; |
1369 | 0 | } |
1370 | 0 | if (i == n) { |
1371 | 0 | mpush(1, n); |
1372 | 0 | return (-1); |
1373 | 0 | } |
1374 | | // strictly opposite of expected 'order', no ties; e.g. no |
1375 | | // more than one NA at the beginning/end (for order=-1/1) |
1376 | | |
1377 | | // TO DO: improve to be stable for ties in reverse |
1378 | 0 | else return(0); |
1379 | 0 | } |
1380 | 0 | int old = gsngrp[flip]; |
1381 | 0 | int tt = 1; |
1382 | 0 | for (int i = 1; i < n; i++) { |
1383 | | // TO DO: once we get past -Inf, NA and NaN at the bottom, and |
1384 | | // +Inf at the top, the middle only need be twiddled |
1385 | | // for tolerance (worth it?) |
1386 | 0 | this = twiddle(x, i, order); |
1387 | 0 | if (this < prev) { |
1388 | 0 | gsngrp[flip] = old; |
1389 | 0 | return (0); |
1390 | 0 | } |
1391 | 0 | if (this == prev) |
1392 | 0 | tt++; |
1393 | 0 | else { |
1394 | 0 | push(tt); |
1395 | 0 | tt = 1; |
1396 | 0 | } |
1397 | 0 | prev = this; |
1398 | 0 | } |
1399 | 0 | push(tt); |
1400 | | // exactly as expected in 'order' (1=increasing, -1=decreasing), |
1401 | | // possibly with ties |
1402 | 0 | return (1); |
1403 | 0 | } |
1404 | | |
1405 | | // order=1 is ascending and -1 is descending |
1406 | | // also accounts for nalast=0 (=NA), =1 (TRUE), -1 (FALSE) |
1407 | | static int csorted(SEXP *x, int n) |
1408 | 0 | { |
1409 | 0 | int i = 1, j = 0, tmp; |
1410 | 0 | if (nalast == 0) { |
1411 | | // when nalast = NA, |
1412 | | // all NAs ? return special value to replace all o's values with '0' |
1413 | | // any NAs ? return 0 = unsorted and leave it to sort routines |
1414 | | // to replace o's with 0's |
1415 | | // no NAs ? continue to check the rest of isorted - |
1416 | | // the same routine as usual |
1417 | 0 | for (int k = 0; k < n; k++) |
1418 | 0 | if (x[k] != NA_STRING) |
1419 | 0 | j++; |
1420 | 0 | if (j == 0) { |
1421 | 0 | push(n); |
1422 | 0 | return (-2); |
1423 | 0 | } |
1424 | 0 | if (j != n) |
1425 | 0 | return (0); |
1426 | 0 | } |
1427 | 0 | if (n <= 1) { |
1428 | 0 | push(n); |
1429 | 0 | return (1); |
1430 | 0 | } |
1431 | 0 | if (StrCmp2(x[1], x[0]) < 0) { |
1432 | 0 | i = 2; |
1433 | 0 | while (i < n && StrCmp2(x[i], x[i - 1]) < 0) |
1434 | 0 | i++; |
1435 | 0 | if (i == n) { |
1436 | 0 | mpush(1, n); |
1437 | 0 | return (-1); |
1438 | 0 | } |
1439 | | // strictly opposite of expected 'order', no ties; |
1440 | | // e.g. no more than one NA at the beginning/end (for order=-1/1) |
1441 | 0 | else |
1442 | 0 | return (0); |
1443 | 0 | } |
1444 | 0 | int old = gsngrp[flip]; |
1445 | 0 | int tt = 1; |
1446 | 0 | for (int i = 1; i < n; i++) { |
1447 | 0 | tmp = StrCmp2(x[i], x[i - 1]); |
1448 | 0 | if (tmp < 0) { |
1449 | 0 | gsngrp[flip] = old; |
1450 | 0 | return (0); |
1451 | 0 | } |
1452 | 0 | if (tmp == 0) |
1453 | 0 | tt++; |
1454 | 0 | else { |
1455 | 0 | push(tt); |
1456 | 0 | tt = 1; |
1457 | 0 | } |
1458 | 0 | } |
1459 | 0 | push(tt); |
1460 | | // exactly as expected in 'order', possibly with ties |
1461 | 0 | return (1); |
1462 | 0 | } |
1463 | | |
1464 | | static void isort(int *x, int *o, int n) |
1465 | 0 | { |
1466 | 0 | if (n <= 2) { |
1467 | | // nalast = 0 and n == 2 (check bottom of this file for explanation) |
1468 | 0 | if (nalast == 0 && n == 2) { |
1469 | 0 | if (o[0] == -1) { |
1470 | 0 | o[0] = 1; |
1471 | 0 | o[1] = 2; |
1472 | 0 | } |
1473 | 0 | for (int i = 0; i < n; i++) |
1474 | 0 | if (x[i] == NA_INTEGER) |
1475 | 0 | o[i] = 0; |
1476 | 0 | push(1); push(1); |
1477 | 0 | return; |
1478 | 0 | } else Error("Internal error: isort received n=%d. isorted should have dealt with this (e.g. as a reverse sorted vector) already",n); |
1479 | 0 | } |
1480 | 0 | if (n < N_SMALL && o[0] != -1 && nalast != 0) { |
1481 | | // see comment above in iradix_r on N_SMALL=200. |
1482 | | /* if not o[0] then can't just populate with 1:n here, since x |
1483 | | is changed by ref too (so would need to be copied). */ |
1484 | | /* pushes inside too. Changes x and o by reference, so not |
1485 | | suitable in first arg when o hasn't been populated yet |
1486 | | and x is an actual argument (hence check on o[0]). */ |
1487 | 0 | if (order != 1 || nalast != -1) |
1488 | | // so that default case, i.e., order=1, nalast=FALSE will |
1489 | | // not be affected (ex: `setkey`) |
1490 | 0 | for (int i = 0; i < n; i++) |
1491 | 0 | x[i] = icheck(x[i]); |
1492 | 0 | iinsert(x, o, n); |
1493 | 0 | } else { |
1494 | | /* Tighter range (e.g. copes better with a few abnormally large |
1495 | | values in some groups), but also, when setRange was once at |
1496 | | arg level that caused an extra scan of (long) x |
1497 | | first. 10,000 calls to setRange takes just 0.04s |
1498 | | i.e. negligible. */ |
1499 | 0 | setRange(x, n); |
1500 | 0 | if (range == NA_INTEGER) |
1501 | 0 | Error("Internal error: isort passed all-NA. isorted should have caught this before this point"); |
1502 | 0 | int *target = (o[0] != -1) ? newo : o; |
1503 | | // was range < 10000 for subgroups, but 1e5 for the first |
1504 | | // arg, tried to generalise here. 1e4 rather than 1e5 here |
1505 | | // because iterated was (thisgrpn < 200 || range > 20000) then |
1506 | | // radix a short vector with large range can bite icount when |
1507 | | // iterated (BLOCK 4 and 6) |
1508 | 0 | if (range <= N_RANGE && range <= n) { |
1509 | 0 | icount(x, target, n); |
1510 | 0 | } else { |
1511 | 0 | iradix(x, target, n); |
1512 | 0 | } |
1513 | 0 | } |
1514 | 0 | } |
1515 | | |
1516 | | static void dsort(double *x, int *o, int n) |
1517 | 0 | { |
1518 | 0 | if (n <= 2) { |
1519 | 0 | if (nalast == 0 && n == 2) { |
1520 | | // don't have to twiddle here.. at least one will be NA |
1521 | | // and 'n' WILL BE 2. |
1522 | 0 | if (o[0] == -1) { |
1523 | 0 | o[0] = 1; |
1524 | 0 | o[1] = 2; |
1525 | 0 | } |
1526 | 0 | for (int i = 0; i < n; i++) |
1527 | 0 | if (is_nan(x, i)) |
1528 | 0 | o[i] = 0; |
1529 | 0 | push(1); push(1); |
1530 | 0 | return; |
1531 | 0 | } |
1532 | 0 | Error("Internal error: dsort received n=%d. dsorted should have dealt with this (e.g. as a reverse sorted vector) already",n); |
1533 | 0 | } |
1534 | 0 | if (n < N_SMALL && o[0] != -1 && nalast != 0) { |
1535 | | // see comment above in iradix_r re N_SMALL=200, and isort for o[0] |
1536 | 0 | for (int i = 0; i < n; i++) |
1537 | 0 | ((unsigned long long *)x)[i] = twiddle(x, i, order); |
1538 | | // have to twiddle here anyways, can't speed up default case |
1539 | | // like in isort |
1540 | 0 | dinsert((unsigned long long *)x, o, n); |
1541 | 0 | } else { |
1542 | 0 | dradix((unsigned char *) x, (o[0] != -1) ? newo : o, n); |
1543 | 0 | } |
1544 | 0 | } |
1545 | | |
1546 | | attribute_hidden SEXP do_radixsort(SEXP call, SEXP op, SEXP args, SEXP rho) |
1547 | 12 | { |
1548 | 12 | int n = -1, narg = 0, ngrp, tmp, *osub, thisgrpn; |
1549 | 12 | R_xlen_t nl = n; |
1550 | 12 | bool isSorted = true, retGrp; |
1551 | 12 | void *xd; |
1552 | 12 | int *o = NULL; |
1553 | | |
1554 | | /* ML: FIXME: Here are just two of the dangerous assumptions here */ |
1555 | 12 | if (sizeof(int) != 4) { |
1556 | 0 | error("radix sort assumes sizeof(int) == 4"); |
1557 | 0 | } |
1558 | 12 | if (sizeof(double) != 8) { |
1559 | 0 | error("radix sort assumes sizeof(double) == 8"); |
1560 | 0 | } |
1561 | | |
1562 | 12 | nalast = (asLogical(CAR(args)) == NA_LOGICAL) ? 0 : |
1563 | 12 | (asLogical(CAR(args)) == TRUE) ? 1 : -1; // 1=TRUE, -1=FALSE, 0=NA |
1564 | 12 | args = CDR(args); |
1565 | 12 | SEXP decreasing = CAR(args); |
1566 | 12 | args = CDR(args); |
1567 | | |
1568 | | /* If TRUE, return starts of runs of identical values + max group size. */ |
1569 | 12 | retGrp = asBool2(CAR(args), call); |
1570 | 12 | args = CDR(args); |
1571 | | |
1572 | | /* If FALSE, get order of strings in appearance order. Essentially |
1573 | | abuses the CHARSXP table to group strings without hashing |
1574 | | them. Only makes sense when retGrp=TRUE. |
1575 | | */ |
1576 | 12 | sortStr = asBool2(CAR(args), call ); |
1577 | 12 | args = CDR(args); |
1578 | | |
1579 | | /* When grouping, we round off doubles to account for imprecision */ |
1580 | 12 | setNumericRounding(retGrp ? 2 : 0); |
1581 | | |
1582 | 12 | if (args == R_NilValue) |
1583 | 0 | return R_NilValue; |
1584 | 12 | if (isVector(CAR(args))) |
1585 | 12 | nl = XLENGTH(CAR(args)); |
1586 | 24 | for (SEXP ap = args; ap != R_NilValue; ap = CDR(ap), narg++) { |
1587 | 12 | if (!isVector(CAR(ap))) |
1588 | 0 | error(_("argument %d is not a vector"), narg + 1); |
1589 | | //Rprintf("%d, %d\n", XLENGTH(CAR(ap)), nl); |
1590 | 12 | if (XLENGTH(CAR(ap)) != nl) |
1591 | 0 | error(_("argument lengths differ")); |
1592 | 12 | } |
1593 | | |
1594 | 12 | if (narg != length(decreasing)) |
1595 | 0 | error(_("length(decreasing) must match the number of order arguments")); |
1596 | 24 | for (int i = 0; i < narg; i++) { |
1597 | 12 | if (LOGICAL(decreasing)[i] == NA_LOGICAL) |
1598 | 0 | error(_("'decreasing' elements must be TRUE or FALSE")); |
1599 | 12 | } |
1600 | 12 | order = asLogical(decreasing) ? -1 : 1; |
1601 | | |
1602 | 12 | SEXP x = CAR(args); |
1603 | 12 | args = CDR(args); |
1604 | | |
1605 | | // (ML) FIXME: need to support long vectors |
1606 | 12 | if (nl > INT_MAX) { |
1607 | 0 | error(_("long vectors not supported")); |
1608 | 0 | } |
1609 | 12 | n = (int) nl; |
1610 | | |
1611 | | // upper limit for stack size (all size 1 groups). We'll detect |
1612 | | // and avoid that limit, but if just one non-1 group (say 2), that |
1613 | | // can't be avoided. |
1614 | 12 | gsmaxalloc = n; |
1615 | | |
1616 | | // once for the result, needs to be length n. |
1617 | | |
1618 | | // TO DO: save allocation if NULL is returned (isSorted = =TRUE) so |
1619 | | // [i|c|d]sort know they can populate o directly with no working |
1620 | | // memory needed to reorder existing order had to repace this from |
1621 | | // '0' to '-1' because 'nalast = 0' replace 'o[.]' with 0 values. |
1622 | | |
1623 | 12 | SEXP ans = PROTECT(allocVector(INTSXP, n)); |
1624 | 12 | o = INTEGER(ans); |
1625 | 12 | if (n > 0) |
1626 | 12 | o[0] = -1; |
1627 | 12 | xd = DATAPTR(x); |
1628 | | |
1629 | 12 | stackgrps = narg > 1 || retGrp; |
1630 | | |
1631 | 12 | if (TYPEOF(x) == STRSXP) { |
1632 | 0 | checkEncodings(x); |
1633 | 0 | } |
1634 | | |
1635 | 12 | savetl_init(); // from now on use Error not error. |
1636 | | |
1637 | 12 | switch (TYPEOF(x)) { |
1638 | 0 | case INTSXP: |
1639 | 12 | case LGLSXP: |
1640 | 12 | tmp = isorted(xd, n); |
1641 | 12 | break; |
1642 | 0 | case REALSXP : |
1643 | 0 | twiddle = &dtwiddle; |
1644 | 0 | is_nan = &dnan; |
1645 | 0 | tmp = dsorted(xd, n); |
1646 | 0 | break; |
1647 | 0 | case STRSXP : |
1648 | 0 | tmp = csorted(xd, n); |
1649 | 0 | break; |
1650 | 0 | default : |
1651 | 0 | Error("First arg is type '%s', not yet supported", |
1652 | 12 | R_typeToChar(x)); |
1653 | 12 | } |
1654 | 12 | if (tmp) { |
1655 | | // -1 or 1. NEW: or -2 in case of nalast == 0 and all NAs |
1656 | 12 | if (tmp == 1) { |
1657 | | // same as expected in 'order' (1 = increasing, -1 = decreasing) |
1658 | 12 | isSorted = true; |
1659 | 84 | for (int i = 0; i < n; i++) |
1660 | 72 | o[i] = i + 1; |
1661 | 12 | } else if (tmp == -1) { |
1662 | | // -1 (or -n for result of strcmp), strictly opposite to |
1663 | | // -expected 'order' |
1664 | 0 | isSorted = false; |
1665 | 0 | for (int i = 0; i < n; i++) |
1666 | 0 | o[i] = n - i; |
1667 | 0 | } else if (nalast == 0 && tmp == -2) { |
1668 | | // happens only when nalast=NA/0. Means all NAs, replace |
1669 | | // with 0's therefore! |
1670 | 0 | isSorted = false; |
1671 | 0 | for (int i = 0; i < n; i++) |
1672 | 0 | o[i] = 0; |
1673 | 0 | } |
1674 | 12 | } else { |
1675 | 0 | isSorted = false; |
1676 | 0 | switch (TYPEOF(x)) { |
1677 | 0 | case INTSXP: |
1678 | 0 | case LGLSXP: |
1679 | 0 | isort(xd, o, n); |
1680 | 0 | break; |
1681 | 0 | case REALSXP : |
1682 | 0 | dsort(xd, o, n); |
1683 | 0 | break; |
1684 | 0 | case STRSXP : |
1685 | 0 | if (sortStr) { |
1686 | 0 | csort_pre(xd, n); |
1687 | 0 | alloc_csort_otmp(n); |
1688 | 0 | csort(xd, o, n); |
1689 | 0 | } else |
1690 | 0 | cgroup(xd, o, n); |
1691 | 0 | break; |
1692 | 0 | default: |
1693 | 0 | Error |
1694 | 0 | ("Internal error: previous default should have caught unsupported type"); |
1695 | 0 | } |
1696 | 0 | } |
1697 | | |
1698 | 12 | int maxgrpn = gsmax[flip]; // biggest group in the first arg |
1699 | 12 | void *xsub = NULL; // local |
1700 | | // This was not valid C23, and clang 15 warns it was not valid C99 either. |
1701 | | // int (*f) (); // called with fn pointer, int |
1702 | | // void (*g) (); // called with fn pointer, int *, int |
1703 | 12 | int fgtype; |
1704 | | |
1705 | 12 | if (narg > 1 && gsngrp[flip] < n) { |
1706 | | // double is the largest type, 8 |
1707 | 0 | xsub = (void *) malloc(maxgrpn * sizeof(double)); |
1708 | 0 | if (xsub == NULL) |
1709 | 0 | Error("Couldn't allocate xsub in do_radixsort, requested %d * %d bytes.", |
1710 | 0 | maxgrpn, (int)sizeof(double)); |
1711 | | // global variable, used by isort, dsort, sort and cgroup |
1712 | 0 | newo = (int *) malloc(maxgrpn * sizeof(int)); |
1713 | 0 | if (newo == NULL) |
1714 | 0 | Error("Couldn't allocate newo in do_radixsort, requested %d * %d bytes.", |
1715 | 0 | maxgrpn, (int)sizeof(int)); |
1716 | 0 | } |
1717 | | |
1718 | 12 | for (int col = 2; col <= narg; col++) { |
1719 | 0 | x = CAR(args); |
1720 | 0 | args = CDR(args); |
1721 | 0 | xd = DATAPTR(x); |
1722 | 0 | ngrp = gsngrp[flip]; |
1723 | 0 | if (ngrp == n && nalast != 0) |
1724 | 0 | break; |
1725 | 0 | flipflop(); |
1726 | 0 | stackgrps = col != narg || retGrp; |
1727 | 0 | order = LOGICAL(decreasing)[col - 1] ? -1 : 1; |
1728 | 0 | switch (TYPEOF(x)) { |
1729 | 0 | case INTSXP: |
1730 | 0 | case LGLSXP: |
1731 | | // f = &isorted; |
1732 | | // g = &isort; |
1733 | 0 | fgtype = 1; |
1734 | 0 | break; |
1735 | 0 | case REALSXP: |
1736 | 0 | twiddle = &dtwiddle; |
1737 | 0 | is_nan = &dnan; |
1738 | 0 | fgtype = 2; |
1739 | | // f = &dsorted; |
1740 | | // g = &dsort; |
1741 | 0 | break; |
1742 | 0 | case STRSXP: |
1743 | 0 | fgtype = 3; |
1744 | | // f = &csorted; |
1745 | 0 | if (sortStr) { |
1746 | 0 | csort_pre(xd, n); |
1747 | 0 | alloc_csort_otmp(gsmax[1 - flip]); |
1748 | | // g = &csort; |
1749 | 0 | } |
1750 | | // no increasing/decreasing order required if sortStr = FALSE, |
1751 | | // just a dummy argument |
1752 | 0 | else { |
1753 | 0 | fgtype = 4; |
1754 | | // g = &cgroup; |
1755 | 0 | } |
1756 | 0 | break; |
1757 | 0 | default: |
1758 | 0 | Error("Arg %d is type '%s', not yet supported", |
1759 | 0 | col, R_typeToChar(x)); |
1760 | 0 | } |
1761 | 0 | int i = 0; |
1762 | 0 | for (int grp = 0; grp < ngrp; grp++) { |
1763 | 0 | thisgrpn = gs[1 - flip][grp]; |
1764 | 0 | if (thisgrpn == 1) { |
1765 | 0 | if (nalast == 0) { |
1766 | | // this edge case had to be taken care of |
1767 | | // here.. (see the bottom of this file for |
1768 | | // more explanation) |
1769 | 0 | if (o[i] == 0) { // already sorted as NA |
1770 | 0 | isSorted = false; |
1771 | 0 | } else switch (TYPEOF(x)) { |
1772 | 0 | case INTSXP: |
1773 | 0 | if (INTEGER(x)[o[i] - 1] == NA_INTEGER) { |
1774 | 0 | isSorted = false; |
1775 | 0 | o[i] = 0; |
1776 | 0 | } |
1777 | 0 | break; |
1778 | 0 | case LGLSXP: |
1779 | 0 | if (LOGICAL(x)[o[i] - 1] == NA_LOGICAL) { |
1780 | 0 | isSorted = false; |
1781 | 0 | o[i] = 0; |
1782 | 0 | } |
1783 | 0 | break; |
1784 | 0 | case REALSXP: |
1785 | 0 | if (ISNAN(REAL(x)[o[i] - 1])) { |
1786 | 0 | isSorted = false; |
1787 | 0 | o[i] = 0; |
1788 | 0 | } |
1789 | 0 | break; |
1790 | 0 | case STRSXP: |
1791 | 0 | if (STRING_ELT(x, o[i] - 1) == NA_STRING) { |
1792 | 0 | isSorted = false; |
1793 | 0 | o[i] = 0; |
1794 | 0 | } break; |
1795 | 0 | default : |
1796 | 0 | Error("Internal error: previous default should have caught unsupported type"); |
1797 | 0 | } |
1798 | 0 | } |
1799 | 0 | i++; |
1800 | 0 | push(1); |
1801 | 0 | continue; |
1802 | 0 | } |
1803 | 0 | osub = o+i; |
1804 | | // ** TO DO **: if isSorted, we can just point xsub |
1805 | | // into x directly. If (*f)() returns 0, |
1806 | | // though, will have to copy x at that point |
1807 | | // When doing this, xsub could be allocated at |
1808 | | // that point for the first time. |
1809 | 0 | if (TYPEOF(x) == STRSXP) |
1810 | 0 | for (int j = 0; j < thisgrpn; j++) |
1811 | 0 | ((SEXP *) xsub)[j] = ((SEXP *) xd)[o[i++] - 1]; |
1812 | 0 | else if (TYPEOF(x) == REALSXP) |
1813 | 0 | for (int j = 0; j < thisgrpn; j++) |
1814 | 0 | ((double *) xsub)[j] = ((double *) xd)[o[i++] - 1]; |
1815 | 0 | else |
1816 | 0 | for (int j = 0; j < thisgrpn; j++) |
1817 | 0 | ((int *) xsub)[j] = ((int *) xd)[o[i++] - 1]; |
1818 | | |
1819 | | // continue; // BASELINE short circuit timing |
1820 | | // point. Up to here is the cost of creating xsub. |
1821 | | // [i|d|c]sorted(); very low cost, sequential |
1822 | | // tmp = (*f)(xsub, thisgrpn); |
1823 | 0 | switch(fgtype) { |
1824 | 0 | case 1: |
1825 | 0 | tmp = isorted(xsub, thisgrpn); |
1826 | 0 | break; |
1827 | 0 | case 2: |
1828 | 0 | tmp = dsorted(xsub, thisgrpn); |
1829 | 0 | break; |
1830 | 0 | case 3: |
1831 | 0 | case 4: |
1832 | 0 | tmp = csorted(xsub, thisgrpn); |
1833 | 0 | } |
1834 | 0 | if (tmp) { |
1835 | | // *sorted will have already push()'d the groups |
1836 | 0 | if (tmp == -1) { |
1837 | 0 | isSorted = false; |
1838 | 0 | for (int k = 0; k < thisgrpn / 2; k++) { |
1839 | | // reverse the order in-place using no |
1840 | | // function call or working memory |
1841 | | // isorted only returns -1 for |
1842 | | // _strictly_ decreasing order, |
1843 | | // otherwise ties wouldn't be stable |
1844 | 0 | tmp = osub[k]; |
1845 | 0 | osub[k] = osub[thisgrpn - 1 - k]; |
1846 | 0 | osub[thisgrpn - 1 - k] = tmp; |
1847 | 0 | } |
1848 | 0 | } else if (nalast == 0 && tmp == -2) { |
1849 | | // all NAs, replace osub[.] with 0s. |
1850 | 0 | isSorted = false; |
1851 | 0 | for (int k = 0; k < thisgrpn; k++) osub[k] = 0; |
1852 | 0 | } |
1853 | 0 | continue; |
1854 | 0 | } |
1855 | 0 | isSorted = false; |
1856 | | // nalast=NA will result in newo[0] = 0. So had to change to -1. |
1857 | 0 | newo[0] = -1; |
1858 | | // may update osub directly, or if not will put the |
1859 | | // result in global newo |
1860 | | // (*g)(xsub, osub, thisgrpn); |
1861 | 0 | switch(fgtype) { |
1862 | 0 | case 1: isort(xsub, osub, thisgrpn); break; |
1863 | 0 | case 2: dsort(xsub, osub, thisgrpn); break; |
1864 | 0 | case 3: csort(xsub, osub, thisgrpn); break; |
1865 | 0 | case 4: cgroup(xsub, osub, thisgrpn); break; |
1866 | 0 | } |
1867 | 0 | if (newo[0] != -1) { |
1868 | 0 | if (nalast != 0) |
1869 | 0 | for (int j = 0; j < thisgrpn; j++) |
1870 | | // reuse xsub to reorder osub |
1871 | 0 | ((int *) xsub)[j] = osub[newo[j] - 1]; |
1872 | 0 | else |
1873 | 0 | for (int j = 0; j < thisgrpn; j++) |
1874 | | // final nalast case to handle! |
1875 | 0 | ((int *) xsub)[j] = (newo[j] == 0) ? 0 : |
1876 | 0 | osub[newo[j] - 1]; |
1877 | 0 | memcpy(osub, xsub, thisgrpn * sizeof(int)); |
1878 | 0 | } |
1879 | 0 | } |
1880 | 0 | } |
1881 | | |
1882 | 12 | if (!sortStr && ustr_n != 0) |
1883 | 0 | Error("Internal error: at the end of do_radixsort sortStr == FALSE but ustr_n !=0 [%d]", |
1884 | 12 | ustr_n); |
1885 | 12 | for(int i = 0; i < ustr_n; i++) |
1886 | 0 | SET_TRLEN(ustr[i], 0); |
1887 | 12 | maxlen = 1; // reset global. Minimum needed to count "" and NA |
1888 | 12 | ustr_n = 0; |
1889 | 12 | savetl_end(); |
1890 | 12 | free(ustr); |
1891 | 12 | ustr = NULL; |
1892 | 12 | ustr_alloc = 0; |
1893 | | |
1894 | 12 | if (retGrp) { |
1895 | 0 | int maxgrpn = NA_INTEGER; |
1896 | 0 | ngrp = gsngrp[flip]; |
1897 | 0 | SEXP s_ends = install("ends"); |
1898 | 0 | setAttrib(ans, s_ends, x = allocVector(INTSXP, ngrp)); |
1899 | 0 | if (ngrp > 0) { |
1900 | 0 | INTEGER(x)[0] = gs[flip][0]; |
1901 | 0 | for (int i = 1; i < ngrp; i++) |
1902 | 0 | INTEGER(x)[i] = INTEGER(x)[i - 1] + gs[flip][i]; |
1903 | 0 | maxgrpn = gsmax[flip]; |
1904 | 0 | } |
1905 | 0 | SEXP s_maxgrpn = install("maxgrpn"); |
1906 | 0 | setAttrib(ans, s_maxgrpn, ScalarInteger(maxgrpn)); |
1907 | 0 | SEXP nms; |
1908 | 0 | PROTECT(nms = allocVector(STRSXP, 2)); |
1909 | 0 | SET_STRING_ELT(nms, 0, mkChar("grouping")); |
1910 | 0 | SET_STRING_ELT(nms, 1, mkChar("integer")); |
1911 | 0 | setAttrib(ans, R_ClassSymbol, nms); |
1912 | 0 | UNPROTECT(1); |
1913 | 0 | } |
1914 | | |
1915 | 12 | bool dropZeros = !retGrp && !isSorted && nalast == 0; |
1916 | 12 | if (dropZeros) { |
1917 | 0 | int zeros = 0; |
1918 | 0 | for (int i = 0; i < n; i++) { |
1919 | 0 | if (o[i] == 0) |
1920 | 0 | zeros++; |
1921 | 0 | } |
1922 | 0 | if (zeros > 0) { |
1923 | 0 | PROTECT(ans = allocVector(INTSXP, n - zeros)); |
1924 | 0 | int *o2 = INTEGER(ans); |
1925 | 0 | for (int i = 0, i2 = 0; i < n; i++) { |
1926 | 0 | if (o[i] > 0) |
1927 | 0 | o2[i2++] = o[i]; |
1928 | 0 | } |
1929 | 0 | UNPROTECT(1); |
1930 | 0 | } |
1931 | 0 | } |
1932 | | |
1933 | 12 | gsfree(); |
1934 | 12 | free(radix_xsub); radix_xsub=NULL; radix_xsuballoc=0; |
1935 | 12 | free(xsub); free(newo); xsub=newo=NULL; |
1936 | 12 | free(xtmp); xtmp=NULL; xtmp_alloc=0; |
1937 | 12 | free(otmp); otmp=NULL; otmp_alloc=0; |
1938 | 12 | free(csort_otmp); csort_otmp=NULL; csort_otmp_alloc=0; |
1939 | | |
1940 | 12 | free(cradix_counts); cradix_counts=NULL; cradix_counts_alloc=0; |
1941 | 12 | free(cradix_xtmp); cradix_xtmp=NULL; cradix_xtmp_alloc=0; |
1942 | | // TO DO: use xtmp already got |
1943 | | |
1944 | 12 | UNPROTECT(1); |
1945 | 12 | return ans; |
1946 | 12 | } |