From 4a90da11d1672cec09b5733c9f9efd3589cacfbf Mon Sep 17 00:00:00 2001 From: hcirellu Date: Tue, 27 Jan 2026 14:48:42 +0100 Subject: [PATCH 1/7] fix sortqtl and orderqtl --- NEWS.md | 3 +++ R/sortuse64.R | 29 +++++++++++++++++------------ tests/testthat/test-highlevel64.R | 4 ++-- tests/testthat/test-sortuse64.R | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 570fb462..c92bdeba 100644 --- a/NEWS.md +++ b/NEWS.md @@ -43,6 +43,8 @@ 1. `%/%` matches base R/Knuth behavior of taking the `floor()` of a result, where before truncation was towards zero. For example, `as.integer64(-10L) %/% as.integer64(7L)` now gives `-2L`, not `-1L`. This is consistent with `-10L %/% 7L` in base R. Consequently, `%%` is also affected, e.g. `as.integer64(-10L) %% as.integer64(7L)` now gives `4L`, not `-3L`, consistent with `-10L %% 7L` in base R. +1. `quantile` and `median` have consistent behavior to base (#247). This is achieved by a change of `sortqtl` and `orderqtl`, which is used in `qtile` and thus also in `quantile` and `median`. This means that `median(as.integer64(1, 3))` new returns `2` (previously `3`), consistent with `median(c(1, 3))` in base R. + ## NEW FEATURES 1. `anyNA` gets an `integer64` method. Thanks @hcirellu. @@ -73,6 +75,7 @@ 1. `as.integer64.character` now returns `NA` for out of range values, with warning, e.g. `as.integer64("22222222222222222222")` (#175). Thanks @hcirellu. 1. `quicksort()` and others no longer segfault on trivial cases (e.g. sorting 0 or 1 item, #220). 1. `as.integer64(2^63)` returns `NA_integer64_` more consistently (e.g. on ARM), consistent with `as.integer(2^31)` (#19). Thanks @dipterix. +1. `quantile` and `median` have consistent behavior to base (#247). This is achieved by a change of `sortqtl` and `orderqtl`, which is used in `qtile` and thus also in `quantile` and `median`. ## NOTES diff --git a/R/sortuse64.R b/R/sortuse64.R index 09756b26..b52c87f5 100644 --- a/R/sortuse64.R +++ b/R/sortuse64.R @@ -533,29 +533,34 @@ sortorderrnk.integer64 <- function(sorted, order, na.count, ...) { #' @rdname sortnut #' @param probs vector of probabilities in `[0..1]` for which we seek quantiles #' @export -sortqtl <- function(sorted, na.count, probs, ...) UseMethod("sortqtl") +sortqtl = function(sorted, na.count, probs, ...) UseMethod("sortqtl") #' @rdname sortnut #' @export -sortqtl.integer64 <- function(sorted, na.count, probs, ...) { - n <- length(sorted) - na.count # nvalid - ret <- sorted[na.count + round(1L + probs * (n-1L))] - # TODO(#31): Remove this once `[` can return NA for integer64 directly - ret[is.na(probs)] <- NA - ret +sortqtl.integer64 = function(sorted, na.count, probs, ...) { + n = length(sorted) - na.count # nvalid + sel = na.count + (1L + probs * (n-1L)) + idx = matrix(c(floor(sel), ceiling(sel)), nrow=2L, byrow=TRUE) + neighbouring_values = matrix(sorted[idx], nrow=2L) + ret = neighbouring_values[1L,] + (neighbouring_values[2L,] - neighbouring_values[1L,])*(sel%%1) + # TODO(#31): Remove this once `[` can return NA for integer64 directly + ret[is.na(probs)] = NA + ret } #' @rdname sortnut #' @export -orderqtl <- function(table, order, na.count, probs, ...) UseMethod("orderqtl") +orderqtl = function(table, order, na.count, probs, ...) UseMethod("orderqtl") #' @rdname sortnut #' @export -orderqtl.integer64 <- function(table, order, na.count, probs, ...) { +orderqtl.integer64 = function(table, order, na.count, probs, ...) { n = length(table) - na.count # nvalid - idx = na.count + round(1L + probs * (n-1L)) - ret = table[order[idx]] + sel = na.count + (1L + probs * (n-1L)) + idx = matrix(c(floor(sel), ceiling(sel)), nrow=2L, byrow=TRUE) + neighbouring_values = matrix(table[order[idx]], nrow=2L) + ret = neighbouring_values[1L,] + (neighbouring_values[2L,] - neighbouring_values[1L,])*(sel%%1) # TODO(#31): Remove this once `[` can return NA for integer64 directly - ret[is.na(probs)] <- NA + ret[is.na(probs)] = NA ret } diff --git a/tests/testthat/test-highlevel64.R b/tests/testthat/test-highlevel64.R index 37814dab..f5db8ee1 100644 --- a/tests/testthat/test-highlevel64.R +++ b/tests/testthat/test-highlevel64.R @@ -228,7 +228,7 @@ test_that("sorting methods work", { expect_identical(rank(x, method="orderrnk"), x_rank) x = as.integer64(1:100) - q = as.integer64(c(1L, 26L, 50L, 75L, 100L)) + q = as.integer64(c(1L, 26L, 51L, 75L, 100L)) expect_identical(quantile(x, names=FALSE), q) expect_identical(median(x), q[3L]) names(q) = c('0%', '25%', '50%', '75%', '100%') @@ -239,7 +239,7 @@ test_that("sorting methods work", { expect_error(quantile(NA_integer64_), "missing values not allowed") x = as.integer64(1:100) - q = as.integer64(c(1L, 26L, 50L, 75L, 100L)) + q = as.integer64(c(1L, 26L, 51L, 75L, 100L)) names(q) = c('0%', '25%', '50%', '75%', '100%') expect_identical(qtile(x, method="sortqtl"), q) expect_identical(qtile(x, method="orderqtl"), q) diff --git a/tests/testthat/test-sortuse64.R b/tests/testthat/test-sortuse64.R index 91ec9315..2ffcc70c 100644 --- a/tests/testthat/test-sortuse64.R +++ b/tests/testthat/test-sortuse64.R @@ -173,3 +173,22 @@ test_that("sortorderkey works", { # Note: NA_integer_ is used for NAs in the key vector expect_identical(sortorderkey(x_na, o_na, na.skip.num=2L), c(NA_integer_, NA_integer_, 1L, 2L)) }) + +with_parameters_test_that("quantile, median", { + x32 = as.integer(x) + x64 = as.integer64(x32) + convert_x32_result_to_integer64 = function(x) { + myRound = function(x) {res = round(x); res[x%%1 == 0.5] = ceiling(x[x%%1 == 0.5]); res} + setNames(as.integer64(myRound(x)), names(x)) + } + expect_identical(quantile(x64, probs=probs, na.rm=TRUE), convert_x32_result_to_integer64(quantile(x32, probs=probs, na.rm=TRUE))) + expect_identical(median(x64, na.rm=TRUE), convert_x32_result_to_integer64(median(x32, na.rm=TRUE))) + }, .cases = expand.grid( + x = I(list(c(1, 5, 7, NA, 1), c(1, 5, 7, NA, 1), 1:2, 1:3, c(1, 3), c(-5, -2, 0, 2))), + probs = I(list(c(0, 0.25, 0.5, 0.75, 1), c(0.1, 0.6, 0.9))) + ) +) + +test_that("special median", { + expect_identical(median(as.integer64(c(1152921504606846976, 1152921504606847232))), as.integer64("1152921504606847104")) +}) From f6a53720166b3e33eedeee78efe3bbe547ccb0bb Mon Sep 17 00:00:00 2001 From: hcirellu Date: Tue, 27 Jan 2026 15:09:16 +0100 Subject: [PATCH 2/7] `=` instead of `<-` in sortuse64.R --- R/sortuse64.R | 408 +++++++++++++++++++++++++------------------------- 1 file changed, 207 insertions(+), 201 deletions(-) diff --git a/R/sortuse64.R b/R/sortuse64.R index b52c87f5..f4636de1 100644 --- a/R/sortuse64.R +++ b/R/sortuse64.R @@ -72,13 +72,13 @@ #' message("check the code of 'optimizer64' for examples:") #' print(optimizer64) #' @export -sortnut <- function(sorted, ...) UseMethod("sortnut") +sortnut = function(sorted, ...) UseMethod("sortnut") #' @rdname sortnut #' @export -sortnut.integer64 <- function(sorted, ...) { - ret <- .Call(C_r_ram_integer64_sortnut, x = sorted) - names(ret) <- c("nunique", "nties") +sortnut.integer64 = function(sorted, ...) { + ret = .Call(C_r_ram_integer64_sortnut, x=sorted) + names(ret) = c("nunique", "nties") ret } @@ -86,51 +86,53 @@ sortnut.integer64 <- function(sorted, ...) { #' @param table the original data with original order under the sorted vector #' @param order an [`integer`] order vector that turns 'table' into 'sorted' #' @export -ordernut <- function(table, order, ...) UseMethod("ordernut") +ordernut = function(table, order, ...) UseMethod("ordernut") #' @rdname sortnut #' @export -ordernut.integer64 <- function(table, order, ...) { - ret <- .Call(C_r_ram_integer64_ordernut, table = as.integer64(table), order = as.integer(order)) - names(ret) <- c("nunique", "nties") +ordernut.integer64 = function(table, order, ...) { + ret = .Call(C_r_ram_integer64_ordernut, table=as.integer64(table), order=as.integer(order)) + names(ret) = c("nunique", "nties") ret } #' @rdname sortnut #' @param x an [`integer64`] vector #' @export -sortfin <- function(sorted, x, ...) UseMethod("sortfin") +sortfin = function(sorted, x, ...) UseMethod("sortfin") #' @rdname sortnut #' @export -sortfin.integer64 <- function(sorted, x, method=NULL, ...) { - n <- length(x) +sortfin.integer64 = function(sorted, x, method=NULL, ...) { + n = length(x) if (is.null(method)) { - if (n<2048L) { - method <- 1L - } else if (n Date: Tue, 27 Jan 2026 15:16:26 +0100 Subject: [PATCH 3/7] typo --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c92bdeba..217dab4b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -43,7 +43,7 @@ 1. `%/%` matches base R/Knuth behavior of taking the `floor()` of a result, where before truncation was towards zero. For example, `as.integer64(-10L) %/% as.integer64(7L)` now gives `-2L`, not `-1L`. This is consistent with `-10L %/% 7L` in base R. Consequently, `%%` is also affected, e.g. `as.integer64(-10L) %% as.integer64(7L)` now gives `4L`, not `-3L`, consistent with `-10L %% 7L` in base R. -1. `quantile` and `median` have consistent behavior to base (#247). This is achieved by a change of `sortqtl` and `orderqtl`, which is used in `qtile` and thus also in `quantile` and `median`. This means that `median(as.integer64(1, 3))` new returns `2` (previously `3`), consistent with `median(c(1, 3))` in base R. +1. `quantile` and `median` have consistent behavior to base (#247). This is achieved by a change of `sortqtl` and `orderqtl`, which is used in `qtile` and thus also in `quantile` and `median`. This means that `median(as.integer64(c(1, 3)))` new returns `2` (previously `3`), consistent with `median(c(1, 3))` in base R. ## NEW FEATURES From c6b1e4baee842f8bea4b3db1e9e6f6d0f292e654 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Tue, 27 Jan 2026 16:28:09 +0100 Subject: [PATCH 4/7] In `optimizer64` change quantile type from 1L to 7L, wich is the default in R, and add a special round function, to not round to the next even number. --- R/highlevel64.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/highlevel64.R b/R/highlevel64.R index 1f5845be..d01a6525 100644 --- a/R/highlevel64.R +++ b/R/highlevel64.R @@ -1179,10 +1179,15 @@ optimizer64 = function(nsmall=2L^16L, , c("prep", "both", "use")) tim["quantile", "both"] <- timefun({ - p <- quantile(x, type=1L, na.rm=TRUE) + p <- quantile(x, type=7L, na.rm=TRUE) })[3L] p2 <- p - p <- as.integer64(p2) + roundToBigger = function(x) { + res = round(x) + res[x%%1 == 0.5] = ceiling(x[x%%1 == 0.5]) + res + } + p <- as.integer64(roundToBigger(p2)) names(p) <- names(p2) x <- as.integer64(x) From 7e33abf3034447c034f1089c0ae6bc634e6453ea Mon Sep 17 00:00:00 2001 From: hcirellu Date: Tue, 27 Jan 2026 16:38:57 +0100 Subject: [PATCH 5/7] fix macos test --- tests/testthat/test-sortuse64.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-sortuse64.R b/tests/testthat/test-sortuse64.R index 2ffcc70c..4426db52 100644 --- a/tests/testthat/test-sortuse64.R +++ b/tests/testthat/test-sortuse64.R @@ -190,5 +190,5 @@ with_parameters_test_that("quantile, median", { ) test_that("special median", { - expect_identical(median(as.integer64(c(1152921504606846976, 1152921504606847232))), as.integer64("1152921504606847104")) + expect_identical(median(as.integer64(c("1152921504606846976", "1152921504606847232"))), as.integer64("1152921504606847104")) }) From 8c52ed29d14a8e803e16d4775a8c9ec81badfdb6 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Tue, 27 Jan 2026 16:59:04 +0100 Subject: [PATCH 6/7] typo --- R/sortuse64.R | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/R/sortuse64.R b/R/sortuse64.R index f4636de1..c50cbc1c 100644 --- a/R/sortuse64.R +++ b/R/sortuse64.R @@ -547,8 +547,8 @@ sortqtl.integer64 = function(sorted, na.count, probs, ...) { n = length(sorted) - na.count # nvalid sel = na.count + (1L + probs * (n-1L)) idx = matrix(c(floor(sel), ceiling(sel)), nrow=2L, byrow=TRUE) - neighbouring_values = matrix(sorted[idx], nrow=2L) - ret = neighbouring_values[1L,] + (neighbouring_values[2L,] - neighbouring_values[1L,])*(sel%%1) + neighboring_values = matrix(sorted[idx], nrow=2L) + ret = neighboring_values[1L,] + (neighboring_values[2L,] - neighboring_values[1L,])*(sel%%1) # TODO(#31): Remove this once `[` can return NA for integer64 directly ret[is.na(probs)] = NA ret @@ -564,8 +564,8 @@ orderqtl.integer64 = function(table, order, na.count, probs, ...) { n = length(table) - na.count # nvalid sel = na.count + (1L + probs * (n-1L)) idx = matrix(c(floor(sel), ceiling(sel)), nrow=2L, byrow=TRUE) - neighbouring_values = matrix(table[order[idx]], nrow=2L) - ret = neighbouring_values[1L,] + (neighbouring_values[2L,] - neighbouring_values[1L,])*(sel%%1) + neighboring_values = matrix(table[order[idx]], nrow=2L) + ret = neighboring_values[1L,] + (neighboring_values[2L,] - neighboring_values[1L,])*(sel%%1) # TODO(#31): Remove this once `[` can return NA for integer64 directly ret[is.na(probs)] = NA ret From 72b512b7169fecd345533fc4d31799fd38edb9d4 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Sat, 14 Mar 2026 00:02:23 +0000 Subject: [PATCH 7/7] botched merge --- R/sortuse64.R | 107 -------------------------------------------------- 1 file changed, 107 deletions(-) diff --git a/R/sortuse64.R b/R/sortuse64.R index 588c53b5..14a5d564 100644 --- a/R/sortuse64.R +++ b/R/sortuse64.R @@ -78,11 +78,7 @@ sortnut = function(sorted, ...) UseMethod("sortnut") #' @rdname sortnut #' @export sortnut.integer64 = function(sorted, ...) { -<<<<<<< median ret = .Call(C_r_ram_integer64_sortnut, x=sorted) -======= - ret = .Call(C_r_ram_integer64_sortnut, x = sorted) ->>>>>>> main names(ret) = c("nunique", "nties") ret } @@ -96,11 +92,7 @@ ordernut = function(table, order, ...) UseMethod("ordernut") #' @rdname sortnut #' @export ordernut.integer64 = function(table, order, ...) { -<<<<<<< median ret = .Call(C_r_ram_integer64_ordernut, table=as.integer64(table), order=as.integer(order)) -======= - ret = .Call(C_r_ram_integer64_ordernut, table = as.integer64(table), order = as.integer(order)) ->>>>>>> main names(ret) = c("nunique", "nties") ret } @@ -115,15 +107,9 @@ sortfin = function(sorted, x, ...) UseMethod("sortfin") sortfin.integer64 = function(sorted, x, method=NULL, ...) { n = length(x) if (is.null(method)) { -<<<<<<< median if (n < 2048L) { method = 1L } else if (n < length(sorted)/128L) { -======= - if (n<2048L) { - method = 1L - } else if (n>>>>>> main method = 2L } else { method = 3L @@ -139,7 +125,6 @@ sortfin.integer64 = function(sorted, x, method=NULL, ...) { , ret=logical(n) ) } else { -<<<<<<< median sx = clone(as.integer64(x)) o = seq_along(x) ramsortorder(sx, o, na.last=FALSE, ...) @@ -149,15 +134,6 @@ sortfin.integer64 = function(sorted, x, method=NULL, ...) { , sorted=as.integer64(sorted) , method=method , ret=ret -======= - sx = clone(as.integer64(x)); o = seq_along(x); ramsortorder(sx, o, na.last=FALSE, ...) - ret = logical(n) - ret[o] = .Call(C_r_ram_integer64_sortfin_asc - , x = sx - , sorted = as.integer64(sorted) - , method= method - , ret = ret ->>>>>>> main ) ret } @@ -172,15 +148,9 @@ orderfin = function(table, order, x, ...) UseMethod("orderfin") orderfin.integer64 = function(table, order, x, method=NULL, ...) { n = length(x) if (is.null(method)) { -<<<<<<< median if (n < 4096L) { method = 1L } else if (n < length(table)/8L) { -======= - if (n<4096L) { - method = 1L - } else if (n>>>>>> main method = 2L } else { method = 3L @@ -198,7 +168,6 @@ orderfin.integer64 = function(table, order, x, method=NULL, ...) { ) } else { x = as.integer64(x) -<<<<<<< median o = seq_along(x) ramorder(x, o, na.last=FALSE, ...) ret = logical(n) @@ -208,16 +177,6 @@ orderfin.integer64 = function(table, order, x, method=NULL, ...) { , order=as.integer(order) , method=as.integer(method) , ret=ret -======= - o = seq_along(x); ramorder(x, o, na.last=FALSE, ...) - ret = logical(n) - ret[o] = .Call(C_r_ram_integer64_orderfin_asc - , x = x[o] - , table = as.integer64(table) - , order = as.integer(order) - , method= as.integer(method) - , ret = ret ->>>>>>> main ) ret } @@ -233,15 +192,9 @@ orderpos = function(table, order, x, ...) UseMethod("orderpos") orderpos.integer64 = function(table, order, x, nomatch=NA, method=NULL, ...) { n = length(x) if (is.null(method)) { -<<<<<<< median if (n < 4096L) { method = 1L } else if (n < length(table)/8L) { -======= - if (n<4096L) { - method = 1L - } else if (n>>>>>> main method = 2L } else { method = 3L @@ -260,7 +213,6 @@ orderpos.integer64 = function(table, order, x, nomatch=NA, method=NULL, ...) { ) } else { x = as.integer64(x) -<<<<<<< median o = seq_along(x) ramorder(x, o, na.last=FALSE, ...) ret = integer(n) @@ -271,17 +223,6 @@ orderpos.integer64 = function(table, order, x, nomatch=NA, method=NULL, ...) { , nomatch=as.integer(nomatch) , method=as.integer(method) , ret=ret -======= - o = seq_along(x); ramorder(x, o, na.last=FALSE, ...) - ret = integer(n) - ret[o] = .Call(C_r_ram_integer64_orderpos_asc - , x = x[o] - , table = as.integer64(table) - , order = as.integer(order) - , nomatch = as.integer(nomatch) - , method= as.integer(method) - , ret = ret ->>>>>>> main ) ret } @@ -296,15 +237,9 @@ sortorderpos = function(sorted, order, x, ...) UseMethod("sortorderpos") sortorderpos.integer64 = function(sorted, order, x, nomatch=NA, method=NULL, ...) { n = length(x) if (is.null(method)) { -<<<<<<< median if (n < 2048L) { method = 1L } else if (n < length(sorted)/128L) { -======= - if (n<2048L) { - method = 1L - } else if (n>>>>>> main method = 2L } else { method = 3L @@ -322,7 +257,6 @@ sortorderpos.integer64 = function(sorted, order, x, nomatch=NA, method=NULL, ... , ret=integer(n) ) } else { -<<<<<<< median sx = clone(as.integer64(x)) o = seq_along(x) ramsortorder(sx, o, na.last=FALSE, ...) @@ -334,17 +268,6 @@ sortorderpos.integer64 = function(sorted, order, x, nomatch=NA, method=NULL, ... , nomatch=as.integer(nomatch) , method=as.integer(method) , ret=ret -======= - sx = clone(as.integer64(x)); o = seq_along(x); ramsortorder(sx, o, na.last=FALSE, ...) - ret = integer(n) - ret[o] = .Call(C_r_ram_integer64_sortorderpos_asc - , x = sx - , sorted = as.integer64(sorted) - , order = as.integer(order) - , nomatch = as.integer(nomatch) - , method= as.integer(method) - , ret = ret ->>>>>>> main ) ret } @@ -358,11 +281,7 @@ orderdup = function(table, order, ...) UseMethod("orderdup") #' @export orderdup.integer64 = function(table, order, method=NULL, ...) { if (is.null(method)) { -<<<<<<< median if (length(table) < 4194304L) -======= - if (length(table)<4194304L) ->>>>>>> main method = 1L else method = 2L @@ -385,11 +304,7 @@ sortorderdup = function(sorted, order, ...) UseMethod("sortorderdup") #' @export sortorderdup.integer64 = function(sorted, order, method=NULL, ...) { if (is.null(method)) { -<<<<<<< median if (length(sorted) < 4194304L) -======= - if (length(sorted)<4194304L) ->>>>>>> main method = 1L else method = 2L @@ -459,21 +374,12 @@ orderupo = function(table, order, nunique, ...) UseMethod("orderupo") #' @rdname sortnut #' @export orderupo.integer64 = function(table, order, nunique, keep.order=FALSE, ...) { -<<<<<<< median .Call(C_r_ram_integer64_orderupo_asc , table=as.integer64(table) , order=as.integer(order) , keep.order=as.logical(keep.order) , ret=integer(nunique) ) -======= - .Call(C_r_ram_integer64_orderupo_asc - , table = as.integer64(table) - , order = as.integer(order) - , keep.order = as.logical(keep.order) - , ret = integer(nunique) - ) ->>>>>>> main } #' @rdname sortnut @@ -578,21 +484,12 @@ orderkey = function(table, order, na.skip.num=0L, ...) UseMethod("orderkey") #' @rdname sortnut #' @export orderkey.integer64 = function(table, order, na.skip.num=0L, ...) { -<<<<<<< median .Call(C_r_ram_integer64_orderkey_asc , table=as.integer64(table) , order=as.integer(order) , na.skip.num=na.skip.num , ret=integer(length(table)) ) -======= - .Call(C_r_ram_integer64_orderkey_asc - , table = as.integer64(table) - , order = as.integer(order) - , na.skip.num=na.skip.num - , ret = integer(length(table)) - ) ->>>>>>> main } #' @rdname sortnut @@ -649,14 +546,10 @@ sortqtl = function(sorted, na.count, probs, ...) UseMethod("sortqtl") #' @export sortqtl.integer64 = function(sorted, na.count, probs, ...) { n = length(sorted) - na.count # nvalid -<<<<<<< median sel = na.count + (1L + probs * (n-1L)) idx = matrix(c(floor(sel), ceiling(sel)), nrow=2L, byrow=TRUE) neighboring_values = matrix(sorted[idx], nrow=2L) ret = neighboring_values[1L,] + (neighboring_values[2L,] - neighboring_values[1L,])*(sel%%1) -======= - ret = sorted[na.count + round(1L + probs * (n-1L))] ->>>>>>> main # TODO(#31): Remove this once `[` can return NA for integer64 directly ret[is.na(probs)] = NA ret