diff --git a/R/integer64.R b/R/integer64.R index 81b29dc..d2716c9 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1255,22 +1255,29 @@ rep.integer64 = function(x, ...) { ret } -# FIXME no method dispatch for : +#' @export `:.integer64` <- function(from, to) { - from = as.integer64(from) - to = as.integer64(to) - ret = .Call(C_seq_integer64, from, as.integer64(1L), double(as.integer(to-from+1L))) + if (!length(from) || !length(to)) + stop("argument of length 0", domain="R") + from = as.integer64(from)[1L] + to = as.integer64(to)[1L] + if (is.na(from) || is.na(to)) + stop("NA/NaN argument", domain="R") + delta = suppressWarnings(to - from) + if (!is.finite(delta) || abs(delta) >= .Machine$integer.max) + stop("sequence generation would be too long") + ret = .Call(C_seq_integer64, from, if (delta < 0) as.integer64(-1L) else as.integer64(1L), double(as.integer(abs(delta) + 1L))) oldClass(ret) = "integer64" ret } #' Generating sequence of integer64 values #' -#' @param from integer64 scalar (in order to dispatch the integer64 method of [seq()]) +#' @param from integer64 (in order to dispatch the integer64 method of [seq()]) #' @param to scalar #' @param by scalar #' @param length.out scalar -#' @param along.with scalar +#' @param along.with R object #' @param ... ignored #' @details #' `seq.integer64` coerces its arguments `from`, `to`, and `by` to `integer64`. Consistency @@ -1292,77 +1299,122 @@ rep.integer64 = function(x, ...) { #' seq(as.integer64(1), 10, by=1.5) #' seq(as.integer64(1), 10, length.out=5) #' @export -seq.integer64 = function(from=NULL, to=NULL, by=NULL, length.out=NULL, along.with=NULL, ...) { - if (!is.null(along.with)) return(seq.integer64(from, to, by=by, length.out=length(along.with))) - - n_args = 4L - is.null(from) - is.null(to) - is.null(by) - is.null(length.out) +seq.integer64 = function(from=1L, to=1L, by=1L, length.out=NULL, along.with=NULL, ...) { + return_value = function(from, by, len) { + ret = .Call(C_seq_integer64, as.integer64(from), as.integer64(by), double(as.integer(len))) + oldClass(ret) = "integer64" + ret + } + + n_args = 4L - missing(from) - missing(to) - missing(by) - (if (missing(length.out) && missing(along.with)) 1L else 0L) + if (n_args == 4L) - stop("too many arguments") - - if (n_args == 1L) { - one = as.integer64(1L) - if (!is.null(from)) return(one:from) - if (!is.null(to)) return(one:to) - if (!is.null(length.out)) { - if (length.out < 0L) - stop("'length.out' must be a non-negative number") - if (length.out == 0L) - return(integer64()) - return(one:length.out) - } - # match seq(by=integer(1)) - return(one) + stop("too many arguments", domain="R-base") + + # warn only for explicitly provided arguments + specific_warn_check = function(arg) !(is.integer(arg) || is.integer64(arg) || is.double(arg) && (arg%%1 == 0) && arg <= .Machine$integer.max && arg >= -.Machine$integer.max) + if (!missing(from)) { + if (specific_warn_check(from)) + warning(gettextf("argument '%s' is coerced to integer64", "from")) + from = as.integer64(from) } - - if (n_args == 2L) { - if (!is.null(length.out)) { - if (length.out == 0L) - return(integer64()) - if (length.out < 0L) - stop("'length.out' must be a non-negative number") - # do before mixing with from/to to avoid integer64/double fraction arithmetic - if (is.double(length.out) && length.out %% 1L != 0L) - length.out = ceiling(length.out) - if (!is.null(from)) - return(seq.integer64(from, from+length.out-1L, by=1L)) - if (!is.null(to)) - return(seq.integer64(to-length.out+1L, to, by=1L)) - if (!is.null(by)) - return(seq.integer64(as.integer64(1L), by=by, length.out=length.out)) - } - if (!is.null(from) && !is.null(to)) return(seq.integer64(from, to, by=sign(to - from))) - if (!is.null(from) && !is.null(by)) return(seq.integer64(from, 1L, by=by)) - return(seq.integer64(as.integer64(1L), to, by=by)) + if (!missing(to)) { + if (specific_warn_check(to)) + warning(gettextf("argument '%s' is coerced to integer64", "to")) + to = as.integer64(to) } - - # match base behavior for seq(1, 2, length.out=1.5) - if (!is.null(length.out) && is.double(length.out)) - length.out = ceiling(length.out) - - if (!is.null(by) && !is.integer64(by)) + if (!missing(by)) { + if (length(by) != 1L) + stop(gettextf("'%s' must be of length 1", "by", domain="R"), domain = NA) + if (specific_warn_check(by)) + warning(gettextf("argument '%s' is coerced to integer64", "by")) by = as.integer64(by) - - if (is.null(from)) { - from = to - (length.out - 1L) * by - } else if (is.null(by)) { - if (length.out == 1L) - return(as.integer64(from)) - by = as.integer64((to - from) / (length.out - 1L)) - } else if (is.null(length.out)) { - if (to != from && by == 0L) - stop("invalid '(to - from)/by'") - if (to == from) + if (!is.numeric(by) && !is.finite(by)) + stop("'by' must be a finite number", domain="R-base") + } + if (!missing(along.with)) { + length.out = length(along.with) + } else if (!missing(length.out)) { + len = length(length.out) + if (!len) + stop(gettextf("'%s' must be of length 1", "length.out", domain="R"), domain=NA) + if (len > 1L) + warning("first element used of 'length.out' argument", domain="R-base") + length.out = as.integer(ceiling(length.out[1L])) + if (!is.finite(length.out) || length.out < 0L) + stop("'length.out' must be a non-negative number", domain="R-base") + } + chkDots(...) + + if (n_args == 0L) + return(as.integer64(1L)) + # special behavior if only from is given + if (n_args == 1L && !missing(from)) { + len_from = length(from) + if (!len_from) + return(integer64()) + if (len_from == 1L && !is.na(from)) { + if (!is.finite(from)) + stop("'from' must be a finite number", domain="R-base") + return(return_value(from=1L, by=if (from >= 1L) 1L else -1L, len=abs(from - 1L) + 1L)) + } else { + return(return_value(from=1L, by=1L, len=len_from)) + } + } + if (!missing(from)) { + if (length(from) != 1L) + stop(gettextf("'%s' must be of length 1", "from", domain="R"), domain=NA) + if (!is.finite(from)) + stop("'from' must be a finite number", domain="R-base") + } + if (!missing(to)) { + if (length(to) != 1L) + stop(gettextf("'%s' must be of length 1", "to", domain="R"), domain=NA) + if (!is.finite(to)) + stop("'to' must be a finite number", domain="R-base") + } + + if (is.null(length.out)) { + if (from == to) { return(as.integer64(from)) - if (sign(to - from) != sign(by)) - stop("wrong sign in 'by' argument'") - length.out = (to - from) / by + 1L + } + if (missing(by)) + by = if (from <= to) as.integer64(1L) else as.integer64(-1L) + len_out = suppressWarnings(to/by - from/by) + if (!is.finite(len_out)) + stop("invalid '(to - from)/by'", domain="R-base") + if (len_out < 0L) + stop("wrong sign in 'by' argument", domain="R-base") + if (len_out >= .Machine$integer.max) + stop("'by' argument is much too small", domain="R-base") + if (missing(from)) + from = 1L + length.out = as.integer(len_out + 1L) } - if (length.out < 0L) - stop("'length.out' must be a non-negative number") - ret = .Call(C_seq_integer64, as.integer64(from), by, double(as.integer(length.out))) - oldClass(ret) = "integer64" - ret + if (length.out == 0L) + return(integer64()) + if (n_args == 3L && missing(by)) { + if (length.out > 1L) { + len_out = length.out - 1L + diff = to - from + by = abs(diff)%/%len_out*sign(diff) + if (from%%len_out != to%%len_out) + warning("the resulting 'by' is truncated to integer64") + } + } + if (missing(to)) { + to = suppressWarnings(from + by*(length.out - 1L)) + if (!is.finite(to)) + stop("resulting sequence does not fit in integer64") + } + if (missing(from)) { + from = suppressWarnings(to - by*(length.out - 1L)) + if (!is.finite(from)) + stop("resulting sequence does not fit in integer64") + } + + return_value(from=from, by=by, len=length.out) } #' @rdname format.integer64 diff --git a/R/patch64.R b/R/patch64.R index b350b49..6f57024 100644 --- a/R/patch64.R +++ b/R/patch64.R @@ -85,9 +85,6 @@ NULL #' @export `:.default` <- function(from, to) base::`:`(from, to) -#' @export -`:.integer64` <- function(from, to) seq.integer64(from=from, to=to) - is.double <- function(x) UseMethod("is.double") #' @rdname bit64S3 #' @export diff --git a/man/seq.integer64.Rd b/man/seq.integer64.Rd index a44eca5..d6b9145 100644 --- a/man/seq.integer64.Rd +++ b/man/seq.integer64.Rd @@ -4,17 +4,10 @@ \alias{seq.integer64} \title{Generating sequence of integer64 values} \usage{ -\method{seq}{integer64}( - from = NULL, - to = NULL, - by = NULL, - length.out = NULL, - along.with = NULL, - ... -) +\method{seq}{integer64}(from = 1L, to = 1L, by = 1L, length.out = NULL, along.with = NULL, ...) } \arguments{ -\item{from}{integer64 scalar (in order to dispatch the integer64 method of \code{\link[=seq]{seq()}})} +\item{from}{integer64 (in order to dispatch the integer64 method of \code{\link[=seq]{seq()}})} \item{to}{scalar} @@ -22,7 +15,7 @@ \item{length.out}{scalar} -\item{along.with}{scalar} +\item{along.with}{R object} \item{...}{ignored} } diff --git a/tests/testthat/test-integer64.R b/tests/testthat/test-integer64.R index 3bd41e3..afaaf90 100644 --- a/tests/testthat/test-integer64.R +++ b/tests/testthat/test-integer64.R @@ -531,8 +531,62 @@ test_that("vector builders of integer64 work", { expect_identical(x[3L]:x[1L], x[3:1]) # rev() a separate method }) +test_that("seq method works analogously to integer: 0 argument", { + expect_identical(seq.integer64(), as.integer64(seq())) +}) + +test_that("seq method works analogously to integer: warning for unused arguments", { + expect_identical( + # remove call information + gsub("^.*:\\n(.+)$", "\\1", tryCatch(seq(as.integer64(1L), extraArg=5L), warning=conditionMessage)), + gsub("^.*:\\n(.+)$", "\\1", tryCatch(seq(1L, extraArg=5L), warning=conditionMessage)) + ) +}) + +test_that("seq method works analogously to integer: 1 (length 0) argument", { + n32 = integer() + n64 = integer64() + expect_identical(seq(n64), as.integer64(seq(n32))) + expect_identical(seq(from=n64), as.integer64(seq(from=n32))) + expect_identical( + tryCatch(seq(to=n64), error=conditionMessage), + tryCatch(seq(to=n32), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(by=n64), error=conditionMessage), + tryCatch(seq(by=n32), error=conditionMessage) + ) + expect_identical( + # for ubuntu-latest requires removing of "argument " + tryCatch(seq(length.out=n64), error=conditionMessage), + gsub("^argument (.*)", "\\1", tryCatch(seq(length.out=n32), error=conditionMessage)) + ) + expect_identical(seq(along.with=n64), as.integer64(seq(along.with=n32))) +}) + +test_that("seq method works analogously to integer: (1 length 2) argument", { + n32 = 1:2 + n64 = as.integer64(n32) + expect_identical(seq(n64), as.integer64(seq(n32))) + expect_identical(seq(from=n64), as.integer64(seq(from=n32))) + expect_identical( + tryCatch(seq(to=n64), error=conditionMessage), + tryCatch(seq(to=n32), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(by=n64), error=conditionMessage), + tryCatch(seq(by=n32), error=conditionMessage) + ) + suppressWarnings(expect_identical(seq(length.out=n64), as.integer64(seq(length.out=n32)))) + expect_identical( + tryCatch(seq(length.out=n64), warning=conditionMessage), + tryCatch(seq(length.out=n32), warning=conditionMessage) + ) + expect_identical(seq(along.with=n64), as.integer64(seq(along.with=n32))) +}) + with_parameters_test_that( - "seq method works analogously to integer: 1 argument (except along.with);", + "seq method works analogously to integer: 1 argument (except along.with)", { n64 = as.integer64(n) expect_identical(seq(n64), as.integer64(seq(n))) @@ -540,7 +594,10 @@ with_parameters_test_that( expect_identical(seq(to=n64), as.integer64(seq(to=n))) expect_identical(seq(by=n64), as.integer64(seq(by=n))) if (n < 0L) { - expect_error(seq(length.out=n64), "'length.out' must be a non-negative number") + expect_identical( + tryCatch(seq(length.out=n64), error=conditionMessage), + tryCatch(seq(length.out=n), error=conditionMessage) + ) } else { expect_identical(seq(length.out=n64), as.integer64(seq(length.out=n))) } @@ -559,28 +616,42 @@ with_parameters_test_that( n2_64 = as.integer64(n2) # TODO(#211): restore parity to seq() here - if (n2 %% 1L == 0L) expect_identical(seq(n1_64, n2), as.integer64(seq(n1_32, n2))) + if (n2 %% 1.0 == 0.0) expect_identical(seq(n1_64, n2), as.integer64(seq(n1_32, n2))) expect_identical(seq(n1_64, n2_64), as.integer64(seq(n1_32, n2_32))) - if (n2 == 0L) { - err_msg = "invalid '(to - from)/by'" - expect_error(seq(n1_64, by=n2), err_msg, fixed=TRUE) - expect_error(seq(to=n1_64, by=n2), err_msg, fixed=TRUE) + if (n2 == 0.0) { + # error "invalid '(to - from)/by'" + expect_identical( + tryCatch(seq(n1_64, by=n2), error=conditionMessage), + tryCatch(seq(n1_32, by=n2), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(to=n1_64, by=n2), error=conditionMessage), + tryCatch(seq(to=n1_32, by=n2), error=conditionMessage) + ) } else if (sign(1L - n1) == sign(n2)) { - if (n2 %% 1L == 0L) expect_identical(seq(n1_64, by=n2), as.integer64(seq(n1, by=n2))) - expect_identical(seq(n1_64, by=n2_64), as.integer64(seq(n1, by=n2_32))) + if (n2 %% 1.0 == 0.0) expect_identical(seq(n1_64, by=n2), as.integer64(seq(n1, by=n2))) + expect_identical(suppressWarnings(seq(n1_64, by=n2_64)), as.integer64(seq(n1, by=n2_32))) - expect_error(seq(to=n1_64, by=n2), "wrong sign in 'by' argument", fixed=TRUE) + # error "wrong sign in 'by' argument" + expect_identical( + tryCatch(suppressWarnings(seq(to=n1_64, by=n2)), error=conditionMessage), + tryCatch(seq(to=n1_32, by=n2), error=conditionMessage) + ) } else { - expect_error(seq(n1_64, by=n2), "wrong sign in 'by' argument", fixed=TRUE) + # error "wrong sign in 'by' argument" + expect_identical( + tryCatch(suppressWarnings(seq(n1_64, by=n2)), error=conditionMessage), + tryCatch(seq(n1_32, by=n2), error=conditionMessage) + ) # TODO(#211): restore parity to seq() here - if (n2 %% 1L == 0L) expect_identical(seq(to=n1_64, by=n2), as.integer64(seq(to=n1, by=n2))) - expect_identical(seq(to=n1_64, by=n2_64), as.integer64(seq(to=n1, by=n2_32))) + if (n2 %% 1.0 == 0.0) expect_identical(seq(to=n1_64, by=n2), as.integer64(seq(to=n1, by=n2))) + expect_identical(suppressWarnings(seq(to=n1_64, by=n2_64)), as.integer64(seq(to=n1, by=n2_32))) } - if (n2 >= 0L) { + if (n2 >= 0.0) { expect_identical(seq(n1_64, length.out=n2), as.integer64(seq(n1_32, length.out=n2))) expect_identical(seq(n1_64, length.out=n2_64), as.integer64(seq(n1_32, length.out=n2_32))) @@ -590,10 +661,19 @@ with_parameters_test_that( expect_identical(seq(by=n1_64, length.out=n2), as.integer64(seq(by=n1_32, length.out=n2))) expect_identical(seq(by=n1_64, length.out=n2_64), as.integer64(seq(by=n1_32, length.out=n2_32))) } else { - err_msg = "'length.out' must be a non-negative number" - expect_error(seq(n1_64, length.out=n2), err_msg, fixed=TRUE) - expect_error(seq(to=n1_64, length.out=n2), err_msg, fixed=TRUE) - expect_error(seq(by=n1_64, length.out=n2), err_msg, fixed=TRUE) + # error "'length.out' must be a non-negative number" + expect_identical( + tryCatch(seq(n1_64, length.out=n2), error=conditionMessage), + tryCatch(seq(n1_32, length.out=n2), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(to=n1_64, length.out=n2), error=conditionMessage), + tryCatch(seq(to=n1_32, length.out=n2), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(by=n1_64, length.out=n2), error=conditionMessage), + tryCatch(seq(by=n1_32, length.out=n2), error=conditionMessage) + ) } }, .cases = expand.grid(n1=c(0L, 5L, -1L), n2=c(0.0, 5.0, -1.0, 1.5)) @@ -614,33 +694,84 @@ with_parameters_test_that( # TODO(#211): restore parity to seq() here if (n2 == n1 || sign(n2 - n1) == sign(n3)) { # TODO(#211): restore parity to seq() here - if (n2 %% 1L == 0L && n3 %% 1L == 0L) { + if (n2 %% 1.0 == 0.0 && n3 %% 1.0 == 0.0) { expect_identical(seq(n1_64, n2, by=n3), as.integer64(seq(n1_32, n2, by=n3))) expect_identical(seq(n1_64, n2_64, by=n3), as.integer64(seq(n1_32, n2_32, by=n3))) expect_identical(seq(n1_64, n2, by=n3_64), as.integer64(seq(n1_32, n2, by=n3_32))) expect_identical(seq(n1_64, n2_64, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))) + } else if (n2 %% 1.0 == 0.0) { + # coerce by + expect_warning(expect_identical(seq(n1_64, n2, by=n3), as.integer64(seq(n1_32, n2, by=n3_32))), "argument 'by' is coerced to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(n1_64, n2_64, by=n3), as.integer64(seq(n1_32, n2_32, by=n3_32))), "argument 'by' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2, by=n3_64), as.integer64(seq(n1_32, n2, by=n3_32))) + expect_identical(seq(n1_64, n2_64, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))) + } else if (n3 %% 1.0 == 0.0) { + # coerce to + expect_warning(expect_identical(seq(n1_64, n2, by=n3), as.integer64(seq(n1_32, n2_32, by=n3))), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2_64, by=n3), as.integer64(seq(n1_32, n2_32, by=n3))) + expect_warning(expect_identical(seq(n1_64, n2, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2_64, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))) + } else { + # coerce by and to + if (getRversion() >= "4.0.0") { + # not all warnings are emitted in R < 4.0.0 + expect_warning( + expect_warning(expect_identical(seq(n1_64, n2, by=n3), as.integer64(seq(n1_32, n2_32, by=n3_32))), "argument 'by' is coerced to integer64", fixed=TRUE), + "argument 'to' is coerced to integer64", fixed=TRUE + ) + } + expect_warning(expect_identical(seq(n1_64, n2_64, by=n3), as.integer64(seq(n1_32, n2_32, by=n3_32))), "argument 'by' is coerced to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(n1_64, n2, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2_64, by=n3_64), as.integer64(seq(n1_32, n2_32, by=n3_32))) } } else { - err_msg <- if (n3 == 0L) "invalid '(to - from)/by'" else "wrong sign in 'by' argument" - expect_error(seq(n1_64, n2, by=n3), err_msg, fixed=TRUE) + expect_identical( + tryCatch(suppressWarnings(seq(n1_64, n2, by=n3)), error=conditionMessage), + tryCatch(seq(n1_32, n2, by=n3), error=conditionMessage) + ) } if (n3 < 0L) { - err_msg = "'length.out' must be a non-negative" - expect_error(seq(n1_64, n2, length.out=n3), err_msg, fixed=TRUE) - expect_error(seq(n1_64, by=n2, length.out=n3), err_msg, fixed=TRUE) - expect_error(seq(to=n1_64, by=n2, length.out=n3), err_msg, fixed=TRUE) + # error "'length.out' must be a non-negative" + expect_identical( + tryCatch(suppressWarnings(seq(n1_64, n2, length.out=n3)), error=conditionMessage), + tryCatch(seq(n1_32, n2, length.out=n3), error=conditionMessage) + ) + expect_identical( + tryCatch(suppressWarnings(seq(n1_64, by=n2, length.out=n3)), error=conditionMessage), + tryCatch(seq(n1_32, by=n2, length.out=n3), error=conditionMessage) + ) + expect_identical( + tryCatch(suppressWarnings(seq(to=n1_64, by=n2, length.out=n3)), error=conditionMessage), + tryCatch(seq(to=n1_32, by=n2, length.out=n3), error=conditionMessage) + ) } else { # TODO(#211): restore parity to seq() here - if (((n2 - n1) / (n3 - 1L)) %% 1L == 0L) { - expect_identical(seq(n1_64, n2, length.out=n3), as.integer64(seq(n1_32, n2, length.out=n3))) - expect_identical(seq(n1_64, n2_64, length.out=n3), as.integer64(seq(n1_32, n2_32, length.out=n3))) - expect_identical(seq(n1_64, n2, length.out=n3_64), as.integer64(seq(n1_32, n2, length.out=n3_32))) - expect_identical(seq(n1_64, n2_64, length.out=n3_64), as.integer64(seq(n1_32, n2_32, length.out=n3_32))) + if (n3 == 0L || ((n2 - n1) / (n3 - 1.0)) %% 1.0 == 0.0) { + if (n2 %% 1.0 == 0.0) { + expect_identical(seq(n1_64, n2, length.out=n3), as.integer64(seq(n1_32, n2, length.out=n3))) + expect_identical(seq(n1_64, n2_64, length.out=n3), as.integer64(seq(n1_32, n2_32, length.out=n3))) + expect_identical(seq(n1_64, n2, length.out=n3_64), as.integer64(seq(n1_32, n2, length.out=n3_32))) + expect_identical(seq(n1_64, n2_64, length.out=n3_64), as.integer64(seq(n1_32, n2_32, length.out=n3_32))) + } else { + expect_warning(expect_identical(seq(n1_64, n2, length.out=n3), as.integer64(seq(n1_32, n2_32, length.out=n3))), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2_64, length.out=n3), as.integer64(seq(n1_32, n2_32, length.out=n3))) + expect_warning(expect_identical(seq(n1_64, n2, length.out=n3_64), as.integer64(seq(n1_32, n2_32, length.out=n3_32))), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_identical(seq(n1_64, n2_64, length.out=n3_64), as.integer64(seq(n1_32, n2_32, length.out=n3_32))) + } + } else { + # truncate by warning + if (n2 %% 1.0 == 0.0) { + by = as.integer(((n2 - n1) / (n3 - 1.0))) + expect_warning(expect_identical(seq(n1_64, n2, length.out=n3), as.integer64(seq(n1_32, by=by, length.out=n3))), "the resulting 'by' is truncated to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(n1_64, n2_64, length.out=n3), as.integer64(seq(n1_32, by=by, length.out=n3))), "the resulting 'by' is truncated to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(n1_64, n2, length.out=n3_64), as.integer64(seq(n1_32, by=by, length.out=n3_32))), "the resulting 'by' is truncated to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(n1_64, n2_64, length.out=n3_64), as.integer64(seq(n1_32, by=by, length.out=n3_32))), "the resulting 'by' is truncated to integer64", fixed=TRUE) + } } # TODO(#211): restore parity to seq() here - if (n2 %% 1L == 0L) { + if (n2 %% 1.0 == 0.0) { expect_identical(seq(n1_64, by=n2, length.out=n3), as.integer64(seq(n1_32, by=n2, length.out=n3))) expect_identical(seq(n1_64, by=n2_64, length.out=n3), as.integer64(seq(n1_32, by=n2_32, length.out=n3))) expect_identical(seq(n1_64, by=n2, length.out=n3_64), as.integer64(seq(n1_32, by=n2, length.out=n3_32))) @@ -656,29 +787,205 @@ with_parameters_test_that( .cases = expand.grid(n1=c(0L, 5L, -1L), n2=c(0.0, 5.0, -1.0, 1.5), n3=c(0.0, 5.0, -1.0, 1.5)) ) +test_that("seq method works analogously to integer: 3 arguments with integer64 coercion and truncation of by", { + skip_unless_r(">= 4.0.0") + # not all warnings are emitted in R < 4.0.0 + expect_warning( + expect_warning( + expect_identical(seq(as.integer64(5L), 1.5, length.out=3.5), as.integer64(seq(5L, by=-1, length.out=3.5))), + "argument 'to' is coerced to integer64", fixed=TRUE + ), + "the resulting 'by' is truncated to integer64", fixed=TRUE + ) +}) + test_that("seq method works analogously to integer: 4 arguments", { - n = as.integer64(5L) + n32 = 5L + n64 = as.integer64(n32) - expect_error(seq(n, n, by=n, length.out=n), "too many arguments") - expect_error(seq(n, n, by=n, along.with=n), "too many arguments") + expect_identical( + tryCatch(seq(n64, n64, by=n64, length.out=n64), error=conditionMessage), + tryCatch(seq(n32, n32, by=n32, length.out=n32), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(n64, n64, by=n64, along.with=n64), error=conditionMessage), + tryCatch(seq(n32, n32, by=n32, along.with=n32), error=conditionMessage) + ) }) test_that("seq() works back-compatibly w.r.t. mixed integer+double inputs", { one = as.integer64(1L) - expect_identical(seq(one, 10L, by=1.5), as.integer64(1:10)) - expect_identical(seq(to=one, from=10L, by=-1.5), as.integer64(10:1)) + expect_warning(expect_identical(seq(one, 10L, by=1.5), as.integer64(1:10)), "argument 'by' is coerced to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(to=one, from=10L, by=-1.5), as.integer64(10:1)), "argument 'by' is coerced to integer64", fixed=TRUE) - expect_identical(seq(one, 10L, by=10.0/3.0), as.integer64(c(1L, 4L, 7L, 10L))) - expect_identical(seq(to=one, from=10L, by=-10.0/3.0), as.integer64(c(10L, 7L, 4L, 1L))) + expect_warning(expect_identical(seq(one, 10L, by=10.0/3.0), as.integer64(c(1L, 4L, 7L, 10L))), "argument 'by' is coerced to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(to=one, from=10L, by=-10.0/3.0), as.integer64(c(10L, 7L, 4L, 1L))), "argument 'by' is coerced to integer64", fixed=TRUE) - expect_error(seq(one, 10L, by=0.1), "invalid '(to - from)/by'", fixed=TRUE) - expect_error(seq(to=one, from=10L, by=-0.1), "invalid '(to - from)/by'", fixed=TRUE) + expect_error( + expect_warning(seq(one, 10L, by=0.1), "argument 'by' is coerced to integer64", fixed=TRUE), + "invalid '(to - from)/by'", fixed=TRUE + ) + expect_error( + expect_warning(seq(to=one, from=10L, by=-0.1), "argument 'by' is coerced to integer64", fixed=TRUE), + "invalid '(to - from)/by'", fixed=TRUE + ) + expect_warning(expect_identical(seq(one, 2.5), as.integer64(1:2)), "argument 'to' is coerced to integer64", fixed=TRUE) + expect_warning(expect_identical(seq(to=one, from=2.5), as.integer64(2:1)), "argument 'from' is coerced to integer64", fixed=TRUE) - expect_identical(seq(one, 2.5), as.integer64(1:2)) - expect_identical(seq(to=one, from=2.5), as.integer64(2:1)) + skip_unless_r(">= 4.0.0") + # not all warnings are emitted in R < 4.0.0 + expect_warning( + expect_warning(expect_identical(seq(one, 5.5, by=1.5), as.integer64(1:5)), "argument 'to' is coerced to integer64", fixed=TRUE), + "argument 'by' is coerced to integer64", fixed=TRUE + ) + expect_warning( + expect_warning(expect_identical(seq(to=one, from=5.5, by=-1.5), as.integer64(5:1)), "argument 'from' is coerced to integer64", fixed=TRUE), + "argument 'by' is coerced to integer64", fixed=TRUE + ) +}) + +test_that("seq method works analogously to integer: further tests", { + n_32 = 10L + n_64 = as.integer64(n_32) + + expect_identical(seq(from=n_64), as.integer64(seq(from=n_32))) + expect_identical(seq(to=n_64), as.integer64(seq(to=n_32))) + expect_identical(seq(length.out=n_64), as.integer64(seq(length.out=n_32))) + expect_identical(seq(along.with=n_64), as.integer64(seq(along.with=n_32))) + + expect_identical(seq(NA_integer64_), as.integer64(seq(NA))) + expect_identical(seq(rep(NA_integer64_, 2)), as.integer64(seq(rep(NA, 2)))) + expect_identical( + tryCatch(seq(NA_integer64_, 2L), error=conditionMessage), + tryCatch(seq(NA, 2L), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(to=NA_integer64_, 2L), error=conditionMessage), + tryCatch(seq(to=NA, 2L), error=conditionMessage) + ) + expect_identical( + tryCatch(seq(length.out=NA_integer64_, 2L), error=conditionMessage), + tryCatch(seq(length.out=NA, 2L), error=conditionMessage) + ) + + expect_identical(seq(as.integer64(0L)), as.integer64(seq(0L))) + + expect_identical(seq(integer64()), as.integer64(seq(integer()))) + + expect_identical( + tryCatch(seq(as.integer64(1:2), 10L), error=conditionMessage), + tryCatch(seq(1:2, 10L), error=conditionMessage) + ) + + expect_warning( + # by = as.integer(((10 - 1) / (5 - 1))) + expect_identical(seq(as.integer64(1L), 10L, along.with=1:5), as.integer64(seq(1L, by=2L, along.with=1:5))), + "the resulting 'by' is truncated to integer64", fixed=TRUE + ) + + expect_identical(seq(as.integer64(1L), 10L, along.with=1:10), as.integer64(seq(1L, 10L, along.with=1:10))) + + expect_identical(seq(as.integer64(1L), 10L, along.with=NULL), as.integer64(seq(1L, 10L, along.with=NULL))) + + expect_identical( + tryCatch(seq(as.integer64(1L), 10L, by=NULL), error=conditionMessage), + tryCatch(seq(1L, 10L, by=NULL), error=conditionMessage) + ) + + expect_identical( + # for ubuntu-latest requires removing of "argument " + tryCatch(seq(as.integer64(1L), 10L, length.out=NULL), error=conditionMessage), + gsub("^argument (.*)", "\\1", tryCatch(seq(1L, 10L, length.out=NULL), error=conditionMessage)) + ) + + expect_identical(seq(as.integer64(1L), 10L, length.out=0L), as.integer64(seq(1L, 10L, length.out=0L))) + + expect_identical(seq(as.integer64(1L), 10L, length.out=10L), as.integer64(seq(1L, 10L, length.out=10L))) + + expect_warning( + # by = as.integer(((10 - 1) / (3 - 1))) + expect_identical(seq(as.integer64(1L), 10L, length.out=3L), as.integer64(seq(1L, by=4L, length.out=3L))), + "the resulting 'by' is truncated to integer64", fixed=TRUE + ) + + expect_identical(seq(as.integer64(1L), along.with=1:5), as.integer64(seq(1L, along.with=1:5))) + + expect_identical(seq(as.integer64(1L), length.out=2L), as.integer64(seq(1L, length.out=2L))) + + expect_warning( + # by = as.integer(((10 - 1) / (5 - 1))) + expect_identical(seq(as.integer64(1L), 10L, along.with=1:5, length.out=2), as.integer64(seq(1L, by=2L, along.with=1:5, length.out=2))), + "the resulting 'by' is truncated to integer64", fixed=TRUE + ) + + expect_identical(seq(as.integer64(1L), along.with=1:5, by=2L), as.integer64(seq(1L, along.with=1:5, by=2L))) + + expect_identical(seq(length.out=as.integer64(2L)), as.integer64(seq(length.out=2L))) + + expect_identical(seq(as.integer64(10L), 1L, along.with=1:10), as.integer64(seq(10L, 1L, along.with=1:10))) + + expect_identical(seq(as.integer64(10L), 10L, length.out=3L), as.integer64(seq(10L, 10L, length.out=3L))) + + expect_warning( + # by = as.integer(((20 - 10) / (4 - 1))) + expect_identical(seq(as.integer64(10L), 20L, length.out=4L), as.integer64(seq(10L, by=3L, length.out=4L))), + "the resulting 'by' is truncated to integer64", fixed=TRUE + ) + + expect_identical(seq(as.integer64(10L), 20L, length.out=3L), as.integer64(seq(10L, 20L, length.out=3L))) + + expect_identical(seq(to=as.integer64(20L), length.out=4L), as.integer64(seq(to=20L, length.out=4L))) + + expect_identical(seq(from=as.integer64(20L), length.out=4L), as.integer64(seq(from=20L, length.out=4L))) + + expect_identical(seq(from=as.integer64(1L), to=-1L), as.integer64(seq(from=1L, to=-1L))) +}) + +test_that("seq error if result does not fit in integer64", { + expect_identical(seq(as.integer64(2^30), by=as.integer(2^30), length.out=2L), as.integer64(seq(as.integer(2^30), by=2^30, length.out=2L))) + expect_error(seq(as.integer64(2^62), by=as.integer64(2^62), length.out=2L), "resulting sequence does not fit in integer64") +}) + +test_that(":.integer64 works analogously to integer", { + i64_1 = as.integer64(1L) + i64_10 = as.integer64(10L) + + expect_identical(i64_1:i64_10, as.integer64(1:10)) + expect_identical(i64_1:10, as.integer64(1:10)) + expect_identical(i64_1:"10", as.integer64(1:10)) + expect_identical(i64_10:i64_1, as.integer64(10:1)) + expect_identical(i64_10:1, as.integer64(10:1)) + expect_identical(i64_10:"1", as.integer64(10:1)) + expect_identical( + tryCatch(i64_1:NA, error=conditionMessage), + tryCatch(1L:NA, error=conditionMessage) + ) + expect_identical( + tryCatch(i64_1:NA_integer64_, error=conditionMessage), + tryCatch(1L:NA, error=conditionMessage) + ) + expect_identical( + tryCatch(suppressWarnings(i64_1:"a"), error=conditionMessage), + tryCatch(suppressWarnings(1L:"a"), error=conditionMessage) + ) + expect_identical( + tryCatch(NA_integer64_:1L, error=conditionMessage), + tryCatch(NA:1L, error=conditionMessage) + ) + expect_identical( + tryCatch(integer64():1L, error=conditionMessage), + tryCatch(integer():1L, error=conditionMessage) + ) + expect_identical( + tryCatch(i64_1:integer(), error=conditionMessage), + tryCatch(1L:integer(), error=conditionMessage) + ) +}) + +test_that(":.integer64 some integer64 specific behavior", { + + expect_error(lim.integer64()[1]:lim.integer64()[2], "sequence generation would be too long") - expect_identical(seq(one, 5.5, by=1.5), as.integer64(1:5)) - expect_identical(seq(to=one, from=5.5, by=-1.5), as.integer64(5:1)) }) # These tests were previously kept as tests under \examples{\dontshow{...}}.