From 3afc6f022ce023459f1e8205751d6cc5b7f52261 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 15 Oct 2025 17:44:35 +0200 Subject: [PATCH 01/14] range() wrong for multiple inputs if one is non-finite (#142) --- NEWS.md | 1 + R/integer64.R | 57 +++++++++++++++++++++++++++++-------------------- src/integer64.c | 19 +++++++++++++++++ 3 files changed, 54 insertions(+), 23 deletions(-) diff --git a/NEWS.md b/NEWS.md index df4adcc..17dad0c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,6 +42,7 @@ ## NOTES 1. {bit64} no longer prints any start-up messages through an `.onAttach()` hook (#106). Thanks @hadley for the request. +2. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly (#142). # bit64 4.6.0-1 (2025-01-16) diff --git a/R/integer64.R b/R/integer64.R index 83036d8..c3f1e92 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1389,34 +1389,38 @@ prod.integer64 <- function(..., na.rm=FALSE) { #' @rdname sum.integer64 #' @export -min.integer64 = function(..., na.rm=FALSE) { - l = list(...) +min.integer64 <- function(..., na.rm=FALSE) { + l <- list(...) + na.rm <- isTRUE(na.rm) if (length(l) == 1L) { - ret = .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) = "integer64" + ret <- .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) <- "integer64" } else { - ret = vapply(l, FUN.VALUE=integer64(1L), function(e) { + ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { .Call(C_min_integer64, e, na.rm, double(1L)) } else { as.integer64(min(e, na.rm=na.rm)) } }) - oldClass(ret) = "integer64" - ret = min(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + ret <- min(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && is.na(ret)) { + ret <- lim.integer64()[2L] warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) + } ret } #' @rdname sum.integer64 #' @export -max.integer64 = function(..., na.rm=FALSE) { - l = list(...) +max.integer64 <- function(..., na.rm=FALSE) { + l <- list(...) + na.rm <- isTRUE(na.rm) if (length(l) == 1L) { - ret = .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) = "integer64" + ret <- .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) <- "integer64" } else { ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { @@ -1425,23 +1429,28 @@ max.integer64 = function(..., na.rm=FALSE) { as.integer64(max(e, na.rm=na.rm)) } }) - oldClass(ret) = "integer64" - ret = max(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + ret <- max(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && is.na(ret)) { + ret <- lim.integer64()[1L] warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) + } ret } #' @rdname sum.integer64 #' @export -range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { - if (finite) - na.rm = TRUE +range.integer64 <- function(..., na.rm=FALSE, finite=FALSE) { + if (isTRUE(finite)) { + na.rm <- TRUE + } else { + na.rm <- isTRUE(na.rm) + } l <- list(...) if (length(l) == 1L) { - ret = .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) - oldClass(ret) = "integer64" + ret <- .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) + oldClass(ret) <- "integer64" } else { ret <- vapply(l, FUN.VALUE=integer64(2L), function(e) { if (is.integer64(e)) { @@ -1450,11 +1459,13 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { as.integer64(range(e, na.rm=na.rm)) } }) - oldClass(ret) = "integer64" - ret = range(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + ret <- range(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && any(is.na(ret))) { + ret <- c(lim.integer64()[2L], lim.integer64()[1L]) warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") + } ret } diff --git a/src/integer64.c b/src/integer64.c index 7c0bbd0..1d9ee23 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -680,10 +680,12 @@ SEXP min_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ long long i, n = LENGTH(e1_); long long * e1 = (long long *) REAL(e1_); long long * ret = (long long *) REAL(ret_); + bool onlyNas = true; ret[0] = MAX_INTEGER64; if (asLogical(na_rm_)){ for(i=0; iret[0]){ + onlyNas = false; ret[0] = e1[i]; } } @@ -718,11 +726,15 @@ SEXP max_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ ret[0] = NA_INTEGER64; return ret_; }else{ + onlyNas = false; if (e1[i]>ret[0]) ret[0] = e1[i]; } } } + if (onlyNas){ + ret[0] = NA_INTEGER64; + } return ret_; } @@ -730,11 +742,13 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ long long i, n = LENGTH(e1_); long long * e1 = (long long *) REAL(e1_); long long * ret = (long long *) REAL(ret_); + bool onlyNas = true; ret[0] = MAX_INTEGER64; ret[1] = MIN_INTEGER64; if (asLogical(na_rm_)){ for(i=0; iret[1]) @@ -747,6 +761,7 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ ret[0] = ret[1] = NA_INTEGER64; return ret_; }else{ + onlyNas = false; if (e1[i]ret[1]) @@ -754,6 +769,10 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ } } } + if (onlyNas){ + ret[0] = NA_INTEGER64; + ret[1] = NA_INTEGER64; + } return ret_; } From d4bbefd2f98c4ac1340281dc3297628eba4a91c5 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 15 Oct 2025 17:44:35 +0200 Subject: [PATCH 02/14] range() wrong for multiple inputs if one is non-finite (#142) --- NEWS.md | 1 + R/integer64.R | 75 ++++++++++++++++++++++++++++++++----------------- src/integer64.c | 19 +++++++++++++ 3 files changed, 69 insertions(+), 26 deletions(-) diff --git a/NEWS.md b/NEWS.md index df4adcc..17dad0c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,6 +42,7 @@ ## NOTES 1. {bit64} no longer prints any start-up messages through an `.onAttach()` hook (#106). Thanks @hadley for the request. +2. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly (#142). # bit64 4.6.0-1 (2025-01-16) diff --git a/R/integer64.R b/R/integer64.R index 83036d8..972406e 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1389,72 +1389,95 @@ prod.integer64 <- function(..., na.rm=FALSE) { #' @rdname sum.integer64 #' @export -min.integer64 = function(..., na.rm=FALSE) { - l = list(...) +min.integer64 <- function(..., na.rm=FALSE) { + l <- list(...) + na.rm <- isTRUE(na.rm) if (length(l) == 1L) { - ret = .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) = "integer64" + ret <- .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) <- "integer64" } else { - ret = vapply(l, FUN.VALUE=integer64(1L), function(e) { + ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { .Call(C_min_integer64, e, na.rm, double(1L)) } else { - as.integer64(min(e, na.rm=na.rm)) + suppressWarnings(as.integer64(min(e, na.rm=na.rm))) } }) - oldClass(ret) = "integer64" - ret = min(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + if (na.rm && all(is.na(ret))) { + warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) + return(lim.integer64()[2L]) + } + ret <- min(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && all(is.na(ret))) { + ret <- lim.integer64()[2L] warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) + } ret } #' @rdname sum.integer64 #' @export -max.integer64 = function(..., na.rm=FALSE) { - l = list(...) +max.integer64 <- function(..., na.rm=FALSE) { + l <- list(...) + na.rm <- isTRUE(na.rm) if (length(l) == 1L) { - ret = .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) = "integer64" + ret <- .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) <- "integer64" } else { ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { .Call(C_max_integer64, e, na.rm, double(1L)) } else { - as.integer64(max(e, na.rm=na.rm)) + suppressWarnings(as.integer64(max(e, na.rm=na.rm))) } }) - oldClass(ret) = "integer64" - ret = max(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + if (na.rm && all(is.na(ret))) { + warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) + return(lim.integer64()[1L]) + } + ret <- max(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && all(is.na(ret))) { warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) + ret <- lim.integer64()[1L] + } ret } #' @rdname sum.integer64 #' @export -range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { - if (finite) - na.rm = TRUE +range.integer64 <- function(..., na.rm=FALSE, finite=FALSE) { + if (isTRUE(finite)) { + na.rm <- TRUE + } else { + na.rm <- isTRUE(na.rm) + } l <- list(...) if (length(l) == 1L) { - ret = .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) - oldClass(ret) = "integer64" + ret <- .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) + oldClass(ret) <- "integer64" } else { ret <- vapply(l, FUN.VALUE=integer64(2L), function(e) { if (is.integer64(e)) { .Call(C_range_integer64, e, na.rm, double(2L)) } else { - as.integer64(range(e, na.rm=na.rm)) + suppressWarnings(as.integer64(range(e, na.rm=na.rm))) } }) - oldClass(ret) = "integer64" - ret = range(ret, na.rm=na.rm) + oldClass(ret) <- "integer64" + if (na.rm && all(is.na(ret))) { + warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") + return(c(lim.integer64()[2L], lim.integer64()[1L])) + } + ret <- range(ret, na.rm=na.rm) } - if (!any(lengths(l))) + if (na.rm && all(is.na(ret))) { warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") + ret <- c(lim.integer64()[2L], lim.integer64()[1L]) + } ret } diff --git a/src/integer64.c b/src/integer64.c index 7c0bbd0..1d9ee23 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -680,10 +680,12 @@ SEXP min_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ long long i, n = LENGTH(e1_); long long * e1 = (long long *) REAL(e1_); long long * ret = (long long *) REAL(ret_); + bool onlyNas = true; ret[0] = MAX_INTEGER64; if (asLogical(na_rm_)){ for(i=0; iret[0]){ + onlyNas = false; ret[0] = e1[i]; } } @@ -718,11 +726,15 @@ SEXP max_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ ret[0] = NA_INTEGER64; return ret_; }else{ + onlyNas = false; if (e1[i]>ret[0]) ret[0] = e1[i]; } } } + if (onlyNas){ + ret[0] = NA_INTEGER64; + } return ret_; } @@ -730,11 +742,13 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ long long i, n = LENGTH(e1_); long long * e1 = (long long *) REAL(e1_); long long * ret = (long long *) REAL(ret_); + bool onlyNas = true; ret[0] = MAX_INTEGER64; ret[1] = MIN_INTEGER64; if (asLogical(na_rm_)){ for(i=0; iret[1]) @@ -747,6 +761,7 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ ret[0] = ret[1] = NA_INTEGER64; return ret_; }else{ + onlyNas = false; if (e1[i]ret[1]) @@ -754,6 +769,10 @@ SEXP range_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ } } } + if (onlyNas){ + ret[0] = NA_INTEGER64; + ret[1] = NA_INTEGER64; + } return ret_; } From bee209489b150c02bf61080574568d60d09d3917 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 15 Oct 2025 18:45:06 +0200 Subject: [PATCH 03/14] typos in documentation --- R/integer64.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index 972406e..5cc6120 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -277,8 +277,8 @@ NULL #' #' @examples #' c(as.integer64(1), 2:6) -#' cbind(1:6, as.integer(1:6)) -#' rbind(1:6, as.integer(1:6)) +#' cbind(1:6, as.integer64(1:6)) +#' rbind(1:6, as.integer64(1:6)) #' @name c.integer64 NULL @@ -305,7 +305,7 @@ NULL #' #' Generating sequence of integer64 values #' -#' @param from integer64 scalar (in order to dispatch the integer64 method of [seq()] +#' @param from integer64 scalar (in order to dispatch the integer64 method of [seq()]) #' @param to scalar #' @param by scalar #' @param length.out scalar From c7a7489cf365fb662ced7b6c6938899c1bd9c680 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 15 Oct 2025 20:59:22 +0200 Subject: [PATCH 04/14] minor fix and adding of regression tests --- R/integer64.R | 89 +++++++++++++++++---------------- src/integer64.c | 16 +++--- tests/testthat/test-integer64.R | 42 ++++++++++++++-- 3 files changed, 95 insertions(+), 52 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index 5cc6120..4c3db62 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1389,94 +1389,97 @@ prod.integer64 <- function(..., na.rm=FALSE) { #' @rdname sum.integer64 #' @export -min.integer64 <- function(..., na.rm=FALSE) { - l <- list(...) - na.rm <- isTRUE(na.rm) +min.integer64 = function(..., na.rm=FALSE) { + l = list(...) + na.rm = isTRUE(na.rm) + ret = NULL + if (length(l) == 1L) { - ret <- .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) <- "integer64" + if (length(l[[1]]) > 0L) { + ret = .Call(C_min_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) = "integer64" + } } else { - ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { + ret = vapply(Filter(length, l), FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { .Call(C_min_integer64, e, na.rm, double(1L)) } else { suppressWarnings(as.integer64(min(e, na.rm=na.rm))) } }) - oldClass(ret) <- "integer64" - if (na.rm && all(is.na(ret))) { - warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) - return(lim.integer64()[2L]) - } - ret <- min(ret, na.rm=na.rm) + oldClass(ret) = "integer64" + if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + ret = min(ret, na.rm=na.rm) } - if (na.rm && all(is.na(ret))) { - ret <- lim.integer64()[2L] + if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) + ret = lim.integer64()[2L] } ret } #' @rdname sum.integer64 #' @export -max.integer64 <- function(..., na.rm=FALSE) { - l <- list(...) - na.rm <- isTRUE(na.rm) +max.integer64 = function(..., na.rm=FALSE) { + l = list(...) + na.rm = isTRUE(na.rm) + ret = NULL + if (length(l) == 1L) { - ret <- .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) - oldClass(ret) <- "integer64" + if (length(l[[1]]) > 0L) { + ret = .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) + oldClass(ret) = "integer64" + } } else { - ret <- vapply(l, FUN.VALUE=integer64(1L), function(e) { + ret = vapply(Filter(length, l), FUN.VALUE=integer64(1L), function(e) { if (is.integer64(e)) { .Call(C_max_integer64, e, na.rm, double(1L)) } else { suppressWarnings(as.integer64(max(e, na.rm=na.rm))) } }) - oldClass(ret) <- "integer64" - if (na.rm && all(is.na(ret))) { - warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) - return(lim.integer64()[1L]) - } - ret <- max(ret, na.rm=na.rm) + oldClass(ret) = "integer64" + if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + ret = max(ret, na.rm=na.rm) } - if (na.rm && all(is.na(ret))) { + if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) - ret <- lim.integer64()[1L] + ret = lim.integer64()[1L] } ret } #' @rdname sum.integer64 #' @export -range.integer64 <- function(..., na.rm=FALSE, finite=FALSE) { +range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { + l = list(...) if (isTRUE(finite)) { - na.rm <- TRUE + na.rm = TRUE } else { - na.rm <- isTRUE(na.rm) + na.rm = isTRUE(na.rm) } - l <- list(...) + ret = NULL + if (length(l) == 1L) { - ret <- .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) - oldClass(ret) <- "integer64" + if (length(l[[1]]) > 0L) { + ret = .Call(C_range_integer64, l[[1L]], na.rm, double(2L)) + oldClass(ret) = "integer64" + } } else { - ret <- vapply(l, FUN.VALUE=integer64(2L), function(e) { + ret = vapply(Filter(length, l), FUN.VALUE=integer64(2L), function(e) { if (is.integer64(e)) { .Call(C_range_integer64, e, na.rm, double(2L)) } else { suppressWarnings(as.integer64(range(e, na.rm=na.rm))) } }) - oldClass(ret) <- "integer64" - if (na.rm && all(is.na(ret))) { - warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") - return(c(lim.integer64()[2L], lim.integer64()[1L])) - } - ret <- range(ret, na.rm=na.rm) + oldClass(ret) = "integer64" + if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + ret = range(ret, na.rm=na.rm) } - if (na.rm && all(is.na(ret))) { + if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") - ret <- c(lim.integer64()[2L], lim.integer64()[1L]) + ret = c(lim.integer64()[2L], lim.integer64()[1L]) } ret } diff --git a/src/integer64.c b/src/integer64.c index 1d9ee23..abd2cb1 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -684,10 +684,12 @@ SEXP min_integer64(SEXP e1_, SEXP na_rm_, SEXP ret_){ ret[0] = MAX_INTEGER64; if (asLogical(na_rm_)){ for(i=0; iret[0]){ - onlyNas = false; + if (e1[i]!=NA_INTEGER64){ + onlyNas = false; + if (e1[i]>ret[0]){ ret[0] = e1[i]; - } + } + } } }else{ for(i=0; i Date: Wed, 15 Oct 2025 13:00:27 -0700 Subject: [PATCH 05/14] bug fix, not NOTE --- NEWS.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 17dad0c..bfc9cdd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,10 +39,13 @@ Because there was no recorded direct usage for any of these, I am opting to just rip the band-aid off and un-export them in this release as opposed to waiting a full cycle more to do so. +## BUG FIXES + +1. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly (#142). + ## NOTES 1. {bit64} no longer prints any start-up messages through an `.onAttach()` hook (#106). Thanks @hadley for the request. -2. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly (#142). # bit64 4.6.0-1 (2025-01-16) From 01cc808f130a79c8998533cce08ba367664d6dea Mon Sep 17 00:00:00 2001 From: hcirellu Date: Thu, 16 Oct 2025 14:28:33 +0200 Subject: [PATCH 06/14] fixed tests in test-bit64-package regarding min, max, range --- tests/testthat/test-bit64-package.R | 84 +++++++---------------------- 1 file changed, 20 insertions(+), 64 deletions(-) diff --git a/tests/testthat/test-bit64-package.R b/tests/testthat/test-bit64-package.R index b569e2a..2db72f3 100644 --- a/tests/testthat/test-bit64-package.R +++ b/tests/testthat/test-bit64-package.R @@ -372,34 +372,18 @@ test_that("Summary functions", { as.integer64(min(2.0, 3.0, NA)), min(as.integer64(2L), 3.0, NA) )) - expect_warning( - expect_warning( - expect_true(identical.integer64( - as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)), - min(as.integer64(2L), 3.0, NA, na.rm=TRUE) - )), - "no non-missing arguments to min; returning Inf", - fixed = TRUE - ), - "NAs produced by integer64 overflow", - fixed = TRUE - ) + expect_true(identical.integer64( + as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)), + min(as.integer64(2L), 3.0, NA, na.rm=TRUE) + )) expect_true(identical.integer64( as.integer64(min(2.0, 3.0, NA)), min(as.integer64(2L), 3.0, NA) )) - expect_warning( - expect_warning( - expect_true(identical.integer64( - as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)), - min(as.integer64(2L), 3.0, NA, na.rm=TRUE) - )), - "no non-missing arguments to min; returning Inf", - fixed = TRUE - ), - "NAs produced by integer64 overflow", - fixed = TRUE - ) + expect_true(identical.integer64( + as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)), + min(as.integer64(2L), 3.0, NA, na.rm=TRUE) + )) expect_true(identical.integer64( as.integer64(max(xd)), @@ -421,34 +405,18 @@ test_that("Summary functions", { as.integer64(max(2.0, 3.0, NA)), max(as.integer64(2L), 3.0, NA) )) - expect_warning( - expect_warning( - expect_true(identical.integer64( - as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)), - max(as.integer64(2L), 3.0, NA, na.rm=TRUE) - )), - "no non-missing arguments to max; returning -Inf", - fixed = TRUE - ), - "NAs produced by integer64 overflow", - fixed = TRUE - ) + expect_true(identical.integer64( + as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)), + max(as.integer64(2L), 3.0, NA, na.rm=TRUE) + )) expect_true(identical.integer64( as.integer64(max(2.0, 3.0, NA)), max(as.integer64(2L), 3.0, NA) )) - expect_warning( - expect_warning( - expect_true(identical.integer64( - as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)), - max(as.integer64(2L), 3.0, NA, na.rm=TRUE) - )), - "no non-missing arguments to max; returning -Inf", - fixed = TRUE - ), - "NAs produced by integer64 overflow", - fixed = TRUE - ) + expect_true(identical.integer64( + as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)), + max(as.integer64(2L), 3.0, NA, na.rm=TRUE) + )) expect_true(identical.integer64( as.integer64(range(xd)), @@ -470,22 +438,10 @@ test_that("Summary functions", { as.integer64(range(2.0, 3.0, NA)), range(as.integer64(2L), 3.0, NA) )) - expect_warning( - expect_warning( - expect_warning( - expect_true(identical.integer64( - as.integer64(range(2.0, 3.0, NA, na.rm=TRUE)), - range(as.integer64(2L), 3.0, NA, na.rm=TRUE) - )), - "no non-missing arguments to max; returning -Inf", - fixed = TRUE - ), - "no non-missing arguments to min; returning Inf", - fixed = TRUE - ), - "NAs produced by integer64 overflow", - fixed = TRUE - ) + expect_true(identical.integer64( + as.integer64(range(2.0, 3.0, NA, na.rm=TRUE)), + range(as.integer64(2L), 3.0, NA, na.rm=TRUE) + )) }) test_that("Cumulative functions", { From 72d1f8096e04912d944335b4903c9fa73f801dc6 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Thu, 16 Oct 2025 15:08:47 +0200 Subject: [PATCH 07/14] #include --- src/integer64.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/integer64.c b/src/integer64.c index abd2cb1..bb51aa6 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -20,6 +20,7 @@ #include // floor #include #include // strtoll +#include // for boolean #include #include From 0b19ba97141fb874fb521ce7c52c1629861e7347 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Sun, 19 Oct 2025 10:15:06 +0200 Subject: [PATCH 08/14] typos fix applied to man pages --- man/c.integer64.Rd | 4 ++-- man/seq.integer64.Rd | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/man/c.integer64.Rd b/man/c.integer64.Rd index 3e17158..5c600d9 100644 --- a/man/c.integer64.Rd +++ b/man/c.integer64.Rd @@ -34,8 +34,8 @@ first argument is 'integer64' } \examples{ c(as.integer64(1), 2:6) - cbind(1:6, as.integer(1:6)) - rbind(1:6, as.integer(1:6)) + cbind(1:6, as.integer64(1:6)) + rbind(1:6, as.integer64(1:6)) } \seealso{ \code{\link[=rep.integer64]{rep.integer64()}} \code{\link[=seq.integer64]{seq.integer64()}} \code{\link[=as.data.frame.integer64]{as.data.frame.integer64()}} diff --git a/man/seq.integer64.Rd b/man/seq.integer64.Rd index 1826732..05ea207 100644 --- a/man/seq.integer64.Rd +++ b/man/seq.integer64.Rd @@ -4,7 +4,7 @@ \alias{seq.integer64} \title{integer64: Sequence Generation} \arguments{ -\item{from}{integer64 scalar (in order to dispatch the integer64 method of \code{\link[=seq]{seq()}}} +\item{from}{integer64 scalar (in order to dispatch the integer64 method of \code{\link[=seq]{seq()}})} \item{to}{scalar} From 5f2fda9ebd275369aaa8fd2d36d575bd0409e8e3 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 21 Oct 2025 16:36:23 -0700 Subject: [PATCH 09/14] re-use variable --- R/integer64.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index 4c3db62..640e013 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1412,8 +1412,8 @@ min.integer64 = function(..., na.rm=FALSE) { ret = min(ret, na.rm=na.rm) } if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { - warning("no non-NA value, returning the highest possible integer64 value +", lim.integer64()[2L]) ret = lim.integer64()[2L] + warning("no non-NA value, returning the highest possible integer64 value +", ret) } ret } @@ -1443,8 +1443,8 @@ max.integer64 = function(..., na.rm=FALSE) { ret = max(ret, na.rm=na.rm) } if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { - warning("no non-NA value, returning the lowest possible integer64 value ", lim.integer64()[1L]) ret = lim.integer64()[1L] + warning("no non-NA value, returning the lowest possible integer64 value ", ret) } ret } @@ -1478,8 +1478,8 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { ret = range(ret, na.rm=na.rm) } if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { - warning("no non-NA value, returning c(+", lim.integer64()[2L], ", ", lim.integer64()[1L], ")") ret = c(lim.integer64()[2L], lim.integer64()[1L]) + warning("no non-NA value, returning c(+", ret[1L], ", ", ret[2L], ")") } ret } From 087c9944680ac8cf7f183445f4f9afa2bd0041fe Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Tue, 21 Oct 2025 16:41:48 -0700 Subject: [PATCH 10/14] Use a helper for complex condition --- R/integer64.R | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index 640e013..cf89942 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1387,6 +1387,12 @@ prod.integer64 <- function(..., na.rm=FALSE) { } } +all_na_values = function(x, na.rm) { + if (length(x) == 0L) return(TRUE) + if (!na.rm) return(FALSE) + return(all(is.na(x))) +} + #' @rdname sum.integer64 #' @export min.integer64 = function(..., na.rm=FALSE) { @@ -1408,10 +1414,10 @@ min.integer64 = function(..., na.rm=FALSE) { } }) oldClass(ret) = "integer64" - if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + if (!all_na_values(ret, na.rm)) ret = min(ret, na.rm=na.rm) } - if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { + if (all_na_values(ret)) { ret = lim.integer64()[2L] warning("no non-NA value, returning the highest possible integer64 value +", ret) } @@ -1439,10 +1445,10 @@ max.integer64 = function(..., na.rm=FALSE) { } }) oldClass(ret) = "integer64" - if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + if (!all_na_values(ret)) ret = max(ret, na.rm=na.rm) } - if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { + if (all_na_values(ret)) { ret = lim.integer64()[1L] warning("no non-NA value, returning the lowest possible integer64 value ", ret) } @@ -1474,10 +1480,10 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { } }) oldClass(ret) = "integer64" - if (length(ret) > 0L && !(na.rm && all(is.na(ret)))) + if (!all_na_values(ret)) ret = range(ret, na.rm=na.rm) } - if (length(ret) == 0L || (na.rm && all(is.na(ret)))) { + if (all_na_values(ret)) { ret = c(lim.integer64()[2L], lim.integer64()[1L]) warning("no non-NA value, returning c(+", ret[1L], ", ", ret[2L], ")") } From 5d88d3a110349d7eb9ff9f95e6d85dee68bc949a Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Wed, 22 Oct 2025 08:07:32 -0700 Subject: [PATCH 11/14] missing na.rm --- R/integer64.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index cf89942..811f35b 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1417,7 +1417,7 @@ min.integer64 = function(..., na.rm=FALSE) { if (!all_na_values(ret, na.rm)) ret = min(ret, na.rm=na.rm) } - if (all_na_values(ret)) { + if (all_na_values(ret, na.rm)) { ret = lim.integer64()[2L] warning("no non-NA value, returning the highest possible integer64 value +", ret) } @@ -1445,10 +1445,10 @@ max.integer64 = function(..., na.rm=FALSE) { } }) oldClass(ret) = "integer64" - if (!all_na_values(ret)) + if (!all_na_values(ret, na.rm)) ret = max(ret, na.rm=na.rm) } - if (all_na_values(ret)) { + if (all_na_values(ret, na.rm)) { ret = lim.integer64()[1L] warning("no non-NA value, returning the lowest possible integer64 value ", ret) } @@ -1480,10 +1480,10 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { } }) oldClass(ret) = "integer64" - if (!all_na_values(ret)) + if (!all_na_values(ret, na.rm)) ret = range(ret, na.rm=na.rm) } - if (all_na_values(ret)) { + if (all_na_values(ret, na.rm)) { ret = c(lim.integer64()[2L], lim.integer64()[1L]) warning("no non-NA value, returning c(+", ret[1L], ", ", ret[2L], ")") } From 66ed2d3017e1a2357699171a3fbcc7891b93047f Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 22 Oct 2025 21:12:06 +0200 Subject: [PATCH 12/14] save unnecessary calls of helper --- R/integer64.R | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/R/integer64.R b/R/integer64.R index 811f35b..c271fb1 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1387,10 +1387,9 @@ prod.integer64 <- function(..., na.rm=FALSE) { } } -all_na_values = function(x, na.rm) { +empty_or_all_na_values_with_naRm = function(x, na.rm) { if (length(x) == 0L) return(TRUE) - if (!na.rm) return(FALSE) - return(all(is.na(x))) + na.rm && all(is.na(x)) } #' @rdname sum.integer64 @@ -1399,6 +1398,7 @@ min.integer64 = function(..., na.rm=FALSE) { l = list(...) na.rm = isTRUE(na.rm) ret = NULL + resEmptyOrAllNa = NULL if (length(l) == 1L) { if (length(l[[1]]) > 0L) { @@ -1414,10 +1414,15 @@ min.integer64 = function(..., na.rm=FALSE) { } }) oldClass(ret) = "integer64" - if (!all_na_values(ret, na.rm)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (!resEmptyOrAllNa) { ret = min(ret, na.rm=na.rm) + resEmptyOrAllNa = NULL + } } - if (all_na_values(ret, na.rm)) { + if (is.null(resEmptyOrAllNa)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (resEmptyOrAllNa) { ret = lim.integer64()[2L] warning("no non-NA value, returning the highest possible integer64 value +", ret) } @@ -1430,7 +1435,8 @@ max.integer64 = function(..., na.rm=FALSE) { l = list(...) na.rm = isTRUE(na.rm) ret = NULL - + resEmptyOrAllNa = NULL + if (length(l) == 1L) { if (length(l[[1]]) > 0L) { ret = .Call(C_max_integer64, l[[1L]], na.rm, double(1L)) @@ -1445,10 +1451,15 @@ max.integer64 = function(..., na.rm=FALSE) { } }) oldClass(ret) = "integer64" - if (!all_na_values(ret, na.rm)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (!resEmptyOrAllNa) { ret = max(ret, na.rm=na.rm) + resEmptyOrAllNa = NULL + } } - if (all_na_values(ret, na.rm)) { + if (is.null(resEmptyOrAllNa)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (resEmptyOrAllNa) { ret = lim.integer64()[1L] warning("no non-NA value, returning the lowest possible integer64 value ", ret) } @@ -1465,6 +1476,7 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { na.rm = isTRUE(na.rm) } ret = NULL + resEmptyOrAllNa = NULL if (length(l) == 1L) { if (length(l[[1]]) > 0L) { @@ -1480,10 +1492,15 @@ range.integer64 = function(..., na.rm=FALSE, finite=FALSE) { } }) oldClass(ret) = "integer64" - if (!all_na_values(ret, na.rm)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (!resEmptyOrAllNa) { ret = range(ret, na.rm=na.rm) + resEmptyOrAllNa = NULL + } } - if (all_na_values(ret, na.rm)) { + if (is.null(resEmptyOrAllNa)) + resEmptyOrAllNa = empty_or_all_na_values_with_naRm(ret, na.rm) + if (resEmptyOrAllNa) { ret = c(lim.integer64()[2L], lim.integer64()[1L]) warning("no non-NA value, returning c(+", ret[1L], ", ", ret[2L], ")") } From 694f437936a807780438de42385c74a3adbc10eb Mon Sep 17 00:00:00 2001 From: hcirellu Date: Thu, 23 Oct 2025 12:06:07 +0200 Subject: [PATCH 13/14] added allNA() and anyNA() --- NAMESPACE | 4 +++ R/integer64.R | 26 ++++++++++++++++++- man/allNA.Rd | 14 ++++++++++ src/init.c | 4 +++ src/sortuse64.c | 46 +++++++++++++++++++++++++++++++++ tests/testthat/test-integer64.R | 20 ++++++++++++++ 6 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 man/allNA.Rd diff --git a/NAMESPACE b/NAMESPACE index 341e5bc..65c0497 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,6 +28,8 @@ S3method("|",integer64) S3method(abs,integer64) S3method(all,integer64) S3method(all.equal,integer64) +S3method(allNA,default) +S3method(allNA,integer64) S3method(any,integer64) S3method(aperm,integer64) S3method(as.bitstring,integer64) @@ -45,6 +47,7 @@ S3method(as.integer64,integer64) S3method(as.integer64,logical) S3method(as.list,integer64) S3method(as.logical,integer64) +S3method(base::anyNA,integer64) S3method(c,integer64) S3method(cbind,integer64) S3method(ceiling,integer64) @@ -179,6 +182,7 @@ export(NA_integer64_) export(abs.integer64) export(all.equal.integer64) export(all.integer64) +export(allNA) export(any.integer64) export(as.bitstring) export(as.bitstring.integer64) diff --git a/R/integer64.R b/R/integer64.R index c271fb1..3fa716d 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1389,7 +1389,7 @@ prod.integer64 <- function(..., na.rm=FALSE) { empty_or_all_na_values_with_naRm = function(x, na.rm) { if (length(x) == 0L) return(TRUE) - na.rm && all(is.na(x)) + na.rm && allNA(x) } #' @rdname sum.integer64 @@ -1753,3 +1753,27 @@ as.list.integer64 <- function(x, ...) { ret <- NextMethod("as.list", x, ...) .Call(C_as_list_integer64, ret) } + + +#' @exportS3Method base::anyNA integer64 +anyNA.integer64 = function(x) { + .Call(C_r_ram_integer64_any_na, x=x) +} + + +#' @title Not Available / Missing Values +#' @description The function allNA implements all(is.na(x)) in a possibly faster way for integer64 +#' @param x An R object to be tested. +#' +#' @export +allNA = function(x) UseMethod("allNA") +#' @exportS3Method allNA default +allNA.default = function(x) { + warning("Please promote that `allNA()` is going to be added in package base in future R versions - similar to `anyNA()`. Falling back to `all(is.na(x))`.") + length(x) && all(is.na(x)) +} + +#' @exportS3Method allNA integer64 +allNA.integer64 = function(x) { + .Call(C_r_ram_integer64_all_na, x=x) +} diff --git a/man/allNA.Rd b/man/allNA.Rd new file mode 100644 index 0000000..690f4a2 --- /dev/null +++ b/man/allNA.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/integer64.R +\name{allNA} +\alias{allNA} +\title{Not Available / Missing Values} +\usage{ +allNA(x) +} +\arguments{ +\item{x}{An R object to be tested.} +} +\description{ +The function allNA implements all(is.na(x)) in a possibly faster way for integer64 +} diff --git a/src/init.c b/src/init.c index d85e471..dd67350 100644 --- a/src/init.c +++ b/src/init.c @@ -67,6 +67,8 @@ extern SEXP r_ram_integer64_issorted_asc(SEXP); extern SEXP r_ram_integer64_mergeorder(SEXP, SEXP, SEXP, SEXP, SEXP); extern SEXP r_ram_integer64_mergesort(SEXP, SEXP, SEXP, SEXP); extern SEXP r_ram_integer64_mergesortorder(SEXP, SEXP, SEXP, SEXP, SEXP); +extern SEXP r_ram_integer64_all_na(SEXP); +extern SEXP r_ram_integer64_any_na(SEXP); extern SEXP r_ram_integer64_nacount(SEXP); extern SEXP r_ram_integer64_orderdup_asc(SEXP, SEXP, SEXP, SEXP); extern SEXP r_ram_integer64_orderfin_asc(SEXP, SEXP, SEXP, SEXP, SEXP); @@ -180,6 +182,8 @@ static const R_CallMethodDef CallEntries[] = { {"r_ram_integer64_mergeorder", (DL_FUNC) &r_ram_integer64_mergeorder, 5}, {"r_ram_integer64_mergesort", (DL_FUNC) &r_ram_integer64_mergesort, 4}, {"r_ram_integer64_mergesortorder", (DL_FUNC) &r_ram_integer64_mergesortorder, 5}, + {"r_ram_integer64_all_na", (DL_FUNC) &r_ram_integer64_all_na, 1}, + {"r_ram_integer64_any_na", (DL_FUNC) &r_ram_integer64_any_na, 1}, {"r_ram_integer64_nacount", (DL_FUNC) &r_ram_integer64_nacount, 1}, {"r_ram_integer64_orderdup_asc", (DL_FUNC) &r_ram_integer64_orderdup_asc, 4}, {"r_ram_integer64_orderfin_asc", (DL_FUNC) &r_ram_integer64_orderfin_asc, 5}, diff --git a/src/sortuse64.c b/src/sortuse64.c index 45668f8..130b824 100644 --- a/src/sortuse64.c +++ b/src/sortuse64.c @@ -41,6 +41,52 @@ SEXP r_ram_integer64_nacount( return ret_; } +SEXP r_ram_integer64_all_na( + SEXP x_ +) +{ + int i,n = LENGTH(x_); + ValueT *x = (ValueT *) REAL(x_); + SEXP ret_; + PROTECT( ret_ = allocVector(LGLSXP, 1) ); + Rboolean ret = FALSE; + if (n){ + ret = TRUE; + R_Busy(1); + for(i=0;i Date: Thu, 23 Oct 2025 12:31:58 +0200 Subject: [PATCH 14/14] missing parameter recursive --- NEWS.md | 3 ++- R/integer64.R | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index bfc9cdd..08e305c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,11 +41,12 @@ ## BUG FIXES -1. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly (#142). +1. `min.integer64`, `max.integer64` and `range.integer64` now support `na.rm=TRUE` correctly when combining across mutliple inputs like `min(x, NA_integer64_, na.rm=TRUE)` (#142). ## NOTES 1. {bit64} no longer prints any start-up messages through an `.onAttach()` hook (#106). Thanks @hadley for the request. +2. `anyNA` is supported for `integer64` and `allNA` is added. # bit64 4.6.0-1 (2025-01-16) diff --git a/R/integer64.R b/R/integer64.R index 3fa716d..cf5100a 100644 --- a/R/integer64.R +++ b/R/integer64.R @@ -1756,7 +1756,7 @@ as.list.integer64 <- function(x, ...) { #' @exportS3Method base::anyNA integer64 -anyNA.integer64 = function(x) { +anyNA.integer64 = function(x, recursive) { .Call(C_r_ram_integer64_any_na, x=x) }