Coverage Report

Created: 2026-09-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}