From 3a8717aa627db8e66d1c4a1477e3b5bcec25207e Mon Sep 17 00:00:00 2001 From: jangorecki Date: Tue, 5 Feb 2019 10:54:09 +0530 Subject: [PATCH 1/3] fix shift RStudio issue, closes #3354 --- R/shift.R | 2 +- inst/tests/tests.Rraw | 3 +++ src/data.table.h | 3 +++ src/gsumm.c | 38 +++++++++++++++++++------------------- src/shift.c | 11 ++++++++--- 5 files changed, 34 insertions(+), 23 deletions(-) diff --git a/R/shift.R b/R/shift.R index 7f8595c755..6124c6af62 100644 --- a/R/shift.R +++ b/R/shift.R @@ -1,6 +1,6 @@ shift <- function(x, n=1L, fill=NA, type=c("lag", "lead", "shift"), give.names=FALSE) { type = match.arg(type) - ans = .Call(Cshift, x, as.integer(n), fill, type) + ans = .Call(Cshift, x, n, fill, type) if (give.names && is.list(ans)) { if (is.null(names(x))) { xsub = substitute(x) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 097ab1ec4b..25b8821e29 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -13304,6 +13304,9 @@ test(1979, DT[,.(a,b,if(FALSE)c)], DT[,c("a","b")]) x <- as.array(1:5) test(1980, names(data.table(x)), "x") +# this was crashing RStudio #3354 +dt = data.table( id = 1:5 , val = letters[1:5] ) +test(1981, dt[, new_col := shift(val, "lead")], error="n must be integer") ################################### # Add new tests above this line # diff --git a/src/data.table.h b/src/data.table.h index 498c6d6f88..d2232a201e 100644 --- a/src/data.table.h +++ b/src/data.table.h @@ -166,3 +166,6 @@ void fadaptiverollmeanExact(double *x, uint_fast64_t nx, double_ans_t *ans, int // frollR.c SEXP frollfunR(SEXP fun, SEXP obj, SEXP k, SEXP fill, SEXP algo, SEXP align, SEXP narm, SEXP hasNA, SEXP adaptive, SEXP verbose); + +// shift.c +SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type); diff --git a/src/gsumm.c b/src/gsumm.c index 419d0459e2..6fd3ee6e1b 100644 --- a/src/gsumm.c +++ b/src/gsumm.c @@ -9,7 +9,7 @@ static int irowslen = -1; // -1 is for irows = NULL static uint16_t *high=NULL, *low=NULL; // the group of each x item; a.k.a. which-group-am-I static int *restrict grp; // TODO: eventually this can be made local for gforce as won't be needed globally when all functions here use gather static size_t highSize; -static int shift, mask; +static int bitshift, mask; static char *gx=NULL; static size_t nBatch, batchSize, lastBatchSize; @@ -73,10 +73,10 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { } int nb = nbit(ngrp-1); - shift = nb/2; // /2 so that high and low can be uint16_t, and no limit (even for nb=4) to stress-test. - // shift=MAX(nb-8,0); if (shift>16) shift=nb/2; // TODO: when we have stress-test off mode, do this - mask = (1<>shift) + 1; + bitshift = nb/2; // /2 so that high and low can be uint16_t, and no limit (even for nb=4) to stress-test. + // bitshift=MAX(nb-8,0); if (bitshift>16) bitshift=nb/2; // TODO: when we have stress-test off mode, do this + mask = (1<>bitshift) + 1; grp = (int *)R_alloc(nrow, sizeof(int)); // TODO: use malloc and made this local as not needed globally when all functions here use gather // maybe better to malloc to avoid R's heap. This grp isn't global, so it doesn't need to be R_alloc @@ -89,8 +89,8 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { // TODO: enable stress-test mode in tests only (#3205) which can be turned off by default in release to decrease overhead on small data // if that is established to be biting (it may be fine). if (nBatch<1 || batchSize<1 || lastBatchSize<1) { - error("Internal error: nrow=%d ngrp=%d nbit=%d shift=%d highSize=%d nBatch=%d batchSize=%d lastBatchSize=%d\n", // # nocov - nrow, ngrp, nb, shift, highSize, nBatch, batchSize, lastBatchSize); // # nocov + error("Internal error: nrow=%d ngrp=%d nbit=%d bitshift=%d highSize=%d nBatch=%d batchSize=%d lastBatchSize=%d\n", // # nocov + nrow, ngrp, nb, bitshift, highSize, nBatch, batchSize, lastBatchSize); // # nocov } // initial population of g: #pragma omp parallel for num_threads(getDTthreads()) @@ -110,9 +110,9 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *restrict op = INTEGER(o); // o is a permutation of 1:nrow int nb = nbit(nrow-1); - int shift = MAX(nb-8, 0); // TODO: experiment nb/2. Here it doesn't have to be /2 currently. - int highSize = ((nrow-1)>>shift) + 1; - //Rprintf("When assigning grp[o] = g, highSize=%d nb=%d shift=%d nBatch=%d\n", highSize, nb, shift, nBatch); + int bitshift = MAX(nb-8, 0); // TODO: experiment nb/2. Here it doesn't have to be /2 currently. + int highSize = ((nrow-1)>>bitshift) + 1; + //Rprintf("When assigning grp[o] = g, highSize=%d nb=%d bitshift=%d nBatch=%d\n", highSize, nb, bitshift, nBatch); int *counts = calloc(nBatch*highSize, sizeof(int)); // TODO: cache-line align and make highSize a multiple of 64 int *TMP = malloc(nrow*2*sizeof(int)); if (!counts || !TMP ) error("Internal error: Failed to allocate counts or TMP when assigning g in gforce"); @@ -122,7 +122,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *my_o = op + b*batchSize; int *restrict my_counts = counts + b*highSize; for (int i=0; i> shift; + const int w = (my_o[i]-1) >> bitshift; my_counts[w]++; } for (int i=0, cum=0; i> shift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too + const int w = (my_o[i]-1) >> bitshift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too int *p = my_tmp + 2*my_counts[w]++; *p++ = my_o[i]-1; *p = my_g[i]; @@ -174,7 +174,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *my_pg = gp + b*batchSize; const int howMany = b==nBatch-1 ? lastBatchSize : batchSize; for (int i=0; i> shift; + const int w = my_pg[i] >> bitshift; my_counts[w]++; my_high[i] = (uint16_t)w; // reduce 4 bytes to 2 } @@ -187,7 +187,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { int *restrict my_tmpcounts = tmpcounts + omp_get_thread_num()*highSize; memcpy(my_tmpcounts, my_counts, highSize*sizeof(int)); for (int i=0; i> shift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too + const int w = my_pg[i] >> bitshift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too my_low[my_tmpcounts[w]++] = (uint16_t)(my_pg[i] & mask); } // counts is now cumulated within batch (with ending values) and we leave it that way @@ -331,7 +331,7 @@ SEXP gsum(SEXP x, SEXP narmArg) if (!anyNA) { #pragma omp parallel for num_threads(getDTthreads()) //schedule(dynamic,1) for (int h=0; h #include -SEXP shift(SEXP obj, SEXP k, SEXP fill, SEXP type) { +SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type) { size_t size; R_len_t i=0, j, m, nx, nk, xrows, thisk, protecti=0; @@ -16,8 +16,13 @@ SEXP shift(SEXP obj, SEXP k, SEXP fill, SEXP type) { } else x = obj; if (!isNewList(x)) error("x must be a list, data.frame or data.table"); - if (!isInteger(k)) - error("Internal error: n must be integer"); // # nocov + SEXP k=R_NilValue; // coerce to integer here rather than in R as it might crash RStudio #3354 + if (!isInteger(k_obj)) { + if (!isReal(k_obj)) error("n must be integer"); + k = PROTECT(coerceVector(k_obj, INTSXP)); protecti++; + } else { + k = k_obj; + } if (length(fill) != 1) error("fill must be a vector of length 1"); // the following two errors should be caught by match.arg() at the R level From f43e8540936c7f0cdcd1639419d1bf1f3b73582d Mon Sep 17 00:00:00 2001 From: mattdowle Date: Thu, 7 Feb 2019 17:59:32 -0800 Subject: [PATCH 2/3] Added news item --- NEWS.md | 2 ++ R/shift.R | 3 +- inst/tests/tests.Rraw | 6 ++-- src/data.table.h | 3 -- src/gsumm.c | 38 +++++++++++----------- src/shift.c | 74 ++++++++++++++++++++----------------------- 6 files changed, 62 insertions(+), 64 deletions(-) diff --git a/NEWS.md b/NEWS.md index 3a1036e651..6246080291 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ 1. `rbindlist()` of a malformed factor missing levels attribute is now a helpful error rather than a cryptic error about `STRING_ELT`, [#3315](https://github.com/Rdatatable/data.table/issues/3315). Thanks to Michael Chirico for reporting. +2. Forgetting `type=` in `shift(val, "lead")` would segfault, [#3354](https://github.com/Rdatatable/data.table/issues/3354). A helpful error is now produced to indicate `"lead"` is being passed to `n=` rather than the intended `type=` argument. Thanks to @SymbolixAU for reporting. + #### NOTES 1. When upgrading to 1.12.0 some Windows users might have seen `CdllVersion not found` in some circumstances. We found a way to catch that so the [helpful message](https://twitter.com/MattDowle/status/1084528873549705217) now occurs for those upgrading from versions prior to 1.12.0 too, as well as those upgrading from 1.12.0 to a later version. See item 1 in notes section of 1.12.0 below for more background. diff --git a/R/shift.R b/R/shift.R index 6124c6af62..4f6e7bdc70 100644 --- a/R/shift.R +++ b/R/shift.R @@ -1,6 +1,7 @@ shift <- function(x, n=1L, fill=NA, type=c("lag", "lead", "shift"), give.names=FALSE) { type = match.arg(type) - ans = .Call(Cshift, x, n, fill, type) + stopifnot(is.numeric(n)) + ans = .Call(Cshift, x, as.integer(n), fill, type) if (give.names && is.list(ans)) { if (is.null(names(x))) { xsub = substitute(x) diff --git a/inst/tests/tests.Rraw b/inst/tests/tests.Rraw index 25b8821e29..9db1c0dbcb 100644 --- a/inst/tests/tests.Rraw +++ b/inst/tests/tests.Rraw @@ -13305,8 +13305,10 @@ x <- as.array(1:5) test(1980, names(data.table(x)), "x") # this was crashing RStudio #3354 -dt = data.table( id = 1:5 , val = letters[1:5] ) -test(1981, dt[, new_col := shift(val, "lead")], error="n must be integer") +DT = data.table( id = 1:5 , val = letters[1:5] ) +test(1981.1, DT[, new_col := shift(val, "lead")], error="is.numeric(n) is not TRUE") +test(1981.2, DT[, new_col := shift(val, NA_integer_)], error="Item 1 of n is NA") + ################################### # Add new tests above this line # diff --git a/src/data.table.h b/src/data.table.h index d2232a201e..498c6d6f88 100644 --- a/src/data.table.h +++ b/src/data.table.h @@ -166,6 +166,3 @@ void fadaptiverollmeanExact(double *x, uint_fast64_t nx, double_ans_t *ans, int // frollR.c SEXP frollfunR(SEXP fun, SEXP obj, SEXP k, SEXP fill, SEXP algo, SEXP align, SEXP narm, SEXP hasNA, SEXP adaptive, SEXP verbose); - -// shift.c -SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type); diff --git a/src/gsumm.c b/src/gsumm.c index 6fd3ee6e1b..419d0459e2 100644 --- a/src/gsumm.c +++ b/src/gsumm.c @@ -9,7 +9,7 @@ static int irowslen = -1; // -1 is for irows = NULL static uint16_t *high=NULL, *low=NULL; // the group of each x item; a.k.a. which-group-am-I static int *restrict grp; // TODO: eventually this can be made local for gforce as won't be needed globally when all functions here use gather static size_t highSize; -static int bitshift, mask; +static int shift, mask; static char *gx=NULL; static size_t nBatch, batchSize, lastBatchSize; @@ -73,10 +73,10 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { } int nb = nbit(ngrp-1); - bitshift = nb/2; // /2 so that high and low can be uint16_t, and no limit (even for nb=4) to stress-test. - // bitshift=MAX(nb-8,0); if (bitshift>16) bitshift=nb/2; // TODO: when we have stress-test off mode, do this - mask = (1<>bitshift) + 1; + shift = nb/2; // /2 so that high and low can be uint16_t, and no limit (even for nb=4) to stress-test. + // shift=MAX(nb-8,0); if (shift>16) shift=nb/2; // TODO: when we have stress-test off mode, do this + mask = (1<>shift) + 1; grp = (int *)R_alloc(nrow, sizeof(int)); // TODO: use malloc and made this local as not needed globally when all functions here use gather // maybe better to malloc to avoid R's heap. This grp isn't global, so it doesn't need to be R_alloc @@ -89,8 +89,8 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { // TODO: enable stress-test mode in tests only (#3205) which can be turned off by default in release to decrease overhead on small data // if that is established to be biting (it may be fine). if (nBatch<1 || batchSize<1 || lastBatchSize<1) { - error("Internal error: nrow=%d ngrp=%d nbit=%d bitshift=%d highSize=%d nBatch=%d batchSize=%d lastBatchSize=%d\n", // # nocov - nrow, ngrp, nb, bitshift, highSize, nBatch, batchSize, lastBatchSize); // # nocov + error("Internal error: nrow=%d ngrp=%d nbit=%d shift=%d highSize=%d nBatch=%d batchSize=%d lastBatchSize=%d\n", // # nocov + nrow, ngrp, nb, shift, highSize, nBatch, batchSize, lastBatchSize); // # nocov } // initial population of g: #pragma omp parallel for num_threads(getDTthreads()) @@ -110,9 +110,9 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *restrict op = INTEGER(o); // o is a permutation of 1:nrow int nb = nbit(nrow-1); - int bitshift = MAX(nb-8, 0); // TODO: experiment nb/2. Here it doesn't have to be /2 currently. - int highSize = ((nrow-1)>>bitshift) + 1; - //Rprintf("When assigning grp[o] = g, highSize=%d nb=%d bitshift=%d nBatch=%d\n", highSize, nb, bitshift, nBatch); + int shift = MAX(nb-8, 0); // TODO: experiment nb/2. Here it doesn't have to be /2 currently. + int highSize = ((nrow-1)>>shift) + 1; + //Rprintf("When assigning grp[o] = g, highSize=%d nb=%d shift=%d nBatch=%d\n", highSize, nb, shift, nBatch); int *counts = calloc(nBatch*highSize, sizeof(int)); // TODO: cache-line align and make highSize a multiple of 64 int *TMP = malloc(nrow*2*sizeof(int)); if (!counts || !TMP ) error("Internal error: Failed to allocate counts or TMP when assigning g in gforce"); @@ -122,7 +122,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *my_o = op + b*batchSize; int *restrict my_counts = counts + b*highSize; for (int i=0; i> bitshift; + const int w = (my_o[i]-1) >> shift; my_counts[w]++; } for (int i=0, cum=0; i> bitshift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too + const int w = (my_o[i]-1) >> shift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too int *p = my_tmp + 2*my_counts[w]++; *p++ = my_o[i]-1; *p = my_g[i]; @@ -174,7 +174,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { const int *my_pg = gp + b*batchSize; const int howMany = b==nBatch-1 ? lastBatchSize : batchSize; for (int i=0; i> bitshift; + const int w = my_pg[i] >> shift; my_counts[w]++; my_high[i] = (uint16_t)w; // reduce 4 bytes to 2 } @@ -187,7 +187,7 @@ SEXP gforce(SEXP env, SEXP jsub, SEXP o, SEXP f, SEXP l, SEXP irowsArg) { int *restrict my_tmpcounts = tmpcounts + omp_get_thread_num()*highSize; memcpy(my_tmpcounts, my_counts, highSize*sizeof(int)); for (int i=0; i> bitshift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too + const int w = my_pg[i] >> shift; // could use my_high but may as well use my_pg since we need my_pg anyway for the lower bits next too my_low[my_tmpcounts[w]++] = (uint16_t)(my_pg[i] & mask); } // counts is now cumulated within batch (with ending values) and we leave it that way @@ -331,7 +331,7 @@ SEXP gsum(SEXP x, SEXP narmArg) if (!anyNA) { #pragma omp parallel for num_threads(getDTthreads()) //schedule(dynamic,1) for (int h=0; h #include -SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type) { +SEXP shift(SEXP obj, SEXP k, SEXP fill, SEXP type) { size_t size; - R_len_t i=0, j, m, nx, nk, xrows, thisk, protecti=0; + int protecti=0; + //R_len_t i=0, j, m, xrows, thisk, SEXP x, tmp=R_NilValue, elem, ans, thisfill, klass; unsigned long long *dthisfill; enum {LAG, LEAD/*, SHIFT, CYCLIC*/} stype = LAG; // currently SHIFT maps to LAG and CYCLIC is unimplemented (see comments in #1708) @@ -16,45 +17,40 @@ SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type) { } else x = obj; if (!isNewList(x)) error("x must be a list, data.frame or data.table"); - SEXP k=R_NilValue; // coerce to integer here rather than in R as it might crash RStudio #3354 - if (!isInteger(k_obj)) { - if (!isReal(k_obj)) error("n must be integer"); - k = PROTECT(coerceVector(k_obj, INTSXP)); protecti++; - } else { - k = k_obj; - } if (length(fill) != 1) error("fill must be a vector of length 1"); // the following two errors should be caught by match.arg() at the R level if (!isString(type) || length(type) != 1) error("Internal error: invalid type for shift(), should have been caught before. please report to data.table issue tracker"); // # nocov - if (!strcmp(CHAR(STRING_ELT(type, 0)), "lag")) stype = LAG; else if (!strcmp(CHAR(STRING_ELT(type, 0)), "lead")) stype = LEAD; else if (!strcmp(CHAR(STRING_ELT(type, 0)), "shift")) stype = LAG; // when we get rid of nested if branches we can use SHIFT, for now it maps to LAG else error("Internal error: invalid type for shift(), should have been caught before. please report to data.table issue tracker"); // # nocov - nx = length(x); nk = length(k); + int nx = length(x), nk = length(k); + if (!isInteger(k)) error("Internal error: k must be integer"); // # nocov + const int *kd = INTEGER(k); + for (int i=0; i= 0) ? INTEGER(k)[j] : -INTEGER(k)[j]; + for (int j=0; j= 0) ? kd[j] : -kd[j]; thisk = (xrows >= thisk) ? thisk : xrows; SET_VECTOR_ELT(ans, i*nk+j, tmp=allocVector(INTSXP, xrows) ); // LAG when type = 'lag' and n >= 0 _or_ type = 'lead' and n < 0 - if ((stype == LAG && INTEGER(k)[j] >= 0) || (stype == LEAD && INTEGER(k)[j] < 0)) { + if ((stype == LAG && kd[j] >= 0) || (stype == LEAD && kd[j] < 0)) { if (xrows - thisk > 0) memmove((char *)DATAPTR(tmp)+(thisk*size), (char *)DATAPTR(elem), (xrows-thisk)*size); - for (m=0; m=0 _or_ type = 'lag', n<0 } else { @@ -62,7 +58,7 @@ SEXP shift(SEXP obj, SEXP k_obj, SEXP fill, SEXP type) { memmove((char *)DATAPTR(tmp), (char *)DATAPTR(elem)+(thisk*size), (xrows-thisk)*size); - for (m=xrows-thisk; m= 0) ? INTEGER(k)[j] : -INTEGER(k)[j]; - thisk = (xrows >= INTEGER(k)[j]) ? INTEGER(k)[j] : xrows; + for (int j=0; j= 0) ? kd[j] : -kd[j]; + thisk = (xrows >= kd[j]) ? kd[j] : xrows; SET_VECTOR_ELT(ans, i*nk+j, tmp=allocVector(REALSXP, xrows) ); - if ((stype == LAG && INTEGER(k)[j] >= 0) || (stype == LEAD && INTEGER(k)[j] < 0)) { + if ((stype == LAG && kd[j] >= 0) || (stype == LEAD && kd[j] < 0)) { if (xrows - thisk > 0) { memmove((char *)DATAPTR(tmp)+(thisk*size), (char *)DATAPTR(elem), (xrows-thisk)*size); } - for (m=0; m= 0) ? INTEGER(k)[j] : -INTEGER(k)[j]; + for (int j=0; j= 0) ? kd[j] : -kd[j]; thisk = (xrows >= thisk) ? thisk : xrows; SET_VECTOR_ELT(ans, i*nk+j, tmp=allocVector(LGLSXP, xrows) ); - if ((stype == LAG && INTEGER(k)[j] >= 0) || (stype == LEAD && INTEGER(k)[j] < 0)) { + if ((stype == LAG && kd[j] >= 0) || (stype == LEAD && kd[j] < 0)) { if (xrows - thisk > 0) memmove((char *)DATAPTR(tmp)+(thisk*size), (char *)DATAPTR(elem), (xrows-thisk)*size); - for (m=0; m 0) memmove((char *)DATAPTR(tmp), (char *)DATAPTR(elem)+(thisk*size), (xrows-thisk)*size); - for (m=xrows-thisk; m= 0) ? INTEGER(k)[j] : -INTEGER(k)[j]; - if ((stype == LAG && INTEGER(k)[j] >= 0) || (stype == LEAD && INTEGER(k)[j] < 0)) { - for (m=0; m= 0) ? kd[j] : -kd[j]; + if ((stype == LAG && kd[j] >= 0) || (stype == LEAD && kd[j] < 0)) { + for (int m=0; m= 0) ? INTEGER(k)[j] : -INTEGER(k)[j]; - if ((stype == LAG && INTEGER(k)[j] >= 0) || (stype == LEAD && INTEGER(k)[j] < 0)) { - for (m=0; m= 0) ? kd[j] : -kd[j]; + if ((stype == LAG && kd[j] >= 0) || (stype == LEAD && kd[j] < 0)) { + for (int m=0; m Date: Thu, 7 Feb 2019 18:04:46 -0800 Subject: [PATCH 3/3] removed commented line. previous commit raised many deep INTEGER(k) and added a check for any NA in k --- src/shift.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shift.c b/src/shift.c index dbf54f5be5..da7555f7b9 100644 --- a/src/shift.c +++ b/src/shift.c @@ -6,7 +6,6 @@ SEXP shift(SEXP obj, SEXP k, SEXP fill, SEXP type) { size_t size; int protecti=0; - //R_len_t i=0, j, m, xrows, thisk, SEXP x, tmp=R_NilValue, elem, ans, thisfill, klass; unsigned long long *dthisfill; enum {LAG, LEAD/*, SHIFT, CYCLIC*/} stype = LAG; // currently SHIFT maps to LAG and CYCLIC is unimplemented (see comments in #1708)