From 824666548a7fe31f4a0459354de702e8ca0c65f7 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 11 Mar 2026 11:44:08 +0100 Subject: [PATCH 01/17] calculate power precisely --- NEWS.md | 5 ++--- R/ops64.R | 3 +++ src/integer64.c | 4 ++-- src/integer64.h | 26 +++++++++++++++++++------- tests/testthat/test-ops64.R | 32 ++++++++++++++++++++++++++------ 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/NEWS.md b/NEWS.md index 7655f66a..74f2f76c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -20,6 +20,8 @@ 1. `log()` operations, especially for `log(x, base=10)` and `log(x, base=2)`, have better precision and consistency (#180). +1. `^.integer64` with integer or integer64 exponent now calculates the power precisely and returns an overflow warning, if an overflow appears (#288). + ## NOTES 1. The R version dependency has been bumped from 3.5.0 (2018) to 3.6.0 (2019). @@ -62,9 +64,6 @@ 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. -1. `as.integer64.integer64` returns a plain `integer64` vector stripped of any attributes. This is consistent with R-like behavior, e.g. `as.integer.integer`. -1. `%/%` matches base R/Knuth behavior of taking the `floor()` of a result, where previously 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. - ## NEW FEATURES 1. `anyNA` gets an `integer64` method. Thanks @hcirellu. diff --git a/R/ops64.R b/R/ops64.R index 058f10e4..6f113ff0 100644 --- a/R/ops64.R +++ b/R/ops64.R @@ -15,6 +15,9 @@ #' @param e1,e2,x numeric or complex vectors or objects which can be coerced to such, #' or other objects for which methods have been written for - especially 'integer64' vectors. #' +#' @details +#' [`^`] with 'integer' or 'integer64' exponent now calculates the power precisely and returns an overflow warning, if an overflow appears. +#' #' @returns #' - [`&`], [`|`], [`!`], [`!=`], [`==`], [`<`], [`<=`], [`>`], [`>=`] return a logical vector #' - [`/`] returns a double vector diff --git a/src/integer64.c b/src/integer64.c index 08dc44cc..b640540b 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -366,10 +366,10 @@ SEXP power_integer64_integer64(SEXP e1_, SEXP e2_, SEXP ret_){ long long * e1 = (long long *) REAL(e1_); long long * e2 = (long long *) REAL(e2_); long long * ret = (long long *) REAL(ret_); - long double longret; + long long base, exp, intermediate; Rboolean naflag = FALSE; mod_iterate(n1, n2, i1, i2) { - POW64(e1[i1],e2[i2],ret[i],naflag, longret) + POW64(e1[i1],e2[i2],ret[i],naflag,base,exp,intermediate) } if (naflag)warning(INTEGER64_OVERFLOW_WARNING); return ret_; diff --git a/src/integer64.h b/src/integer64.h index 6289fb5c..93f4a803 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -154,16 +154,28 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { ret = llroundl(longret); \ } -#define POW64(e1,e2,ret,naflag, longret) \ +#define POW64(e1,e2,ret,naflag,base,exp,intermediate) \ if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \ ret = NA_INTEGER64; \ else { \ - longret = pow(e1, (long double) e2); \ - if (isnan(longret)){ \ - naflag = TRUE; \ - ret = NA_INTEGER64; \ - }else \ - ret = llroundl(longret); \ + if (e2 >= 0 || e1 == 1 || e1 == -1) { \ + ret = 1; \ + base = e1; \ + exp = e2; \ + while (exp > 0) { \ + if (exp % 2 == 1) { \ + intermediate = ret; \ + ret *= base; \ + if (!GOODIPROD64(intermediate, base, ret)) { \ + ret = NA_INTEGER64; \ + naflag = TRUE; \ + break; \ + } \ + } \ + base *= base; \ + exp >>= 1; \ + } \ + } \ } #define POW64REAL(e1,e2,ret,naflag,longret) \ diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index c9ac0b99..42813ef4 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -299,15 +299,18 @@ with_parameters_test_that("{operator} with integer64 vs {class} (returning integ op = match.fun(operator) maybe_cast = if (operator %in% c("+", "-", "*", "^", "%%", "%/%")) as.integer64 else identity + maybe_cast_argument = if (operator == "^") as.double else identity expected = tryCatch(maybe_cast(op(x32, y)), error=conditionMessage) - actual = tryCatch(op(x64, y), error=conditionMessage) + actual = tryCatch(op(x64, maybe_cast_argument(y)), error=conditionMessage) expect_identical(actual, expected) - - expected = tryCatch(maybe_cast(op(y, x32)), error=conditionMessage) - actual = tryCatch(op(y, x64), error=conditionMessage) - expect_identical(actual, expected) -}, + + if (operator != "^") { + expected = tryCatch(maybe_cast(op(y, x32)), error=conditionMessage) + actual = tryCatch(op(y, x64), error=conditionMessage) + expect_identical(actual, expected) + } +}, .cases = expand.grid( operator=c("+", "-", "*", "/", "^", "%%", "%/%", "<", "<=", "==", ">=", ">", "!=", "&", "|", "xor"), class=c("integer", "double", "logical"), @@ -383,3 +386,20 @@ test_that("Edge cases for character/factor comparisons work", { # nolint next: expect_comparison_linter. Checking '==' method expect_true(as.integer64("999999999999999999") == as.factor("999999999999999999")) }) + +test_that("power with integer64", { + # within integer range + x = as.integer(sqrt(.Machine$integer.max)) + x = seq(-x, x) + expect_identical(as.integer64(x)^2L, as.integer64(x^2L)) + expect_identical(as.integer64(x)^2, as.integer64(x^2)) + + # within integer64 range, which fails with double exponent + expect_identical(as.integer64("2147483650")^2L, as.integer64("4611686027017322500")) + expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) + expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) + + expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") + # no warning for exponent double + expect_identical(as.integer64("2147483650")^3, NA_integer64_) +}) From da2ad31862793afeeda0d92d04e1028d92c77465 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 11 Mar 2026 12:35:44 +0100 Subject: [PATCH 02/17] fix test for macos --- tests/testthat/test-ops64.R | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 42813ef4..5d43e006 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -399,7 +399,10 @@ test_that("power with integer64", { expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) - expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") + # macos does not return overflow warning + actual_result = tryCatch(as.integer64("2147483650")^3L, warning=conditionMessage) + expected_result = tryCatch(as.integer64("2147483650")*as.integer64("2147483650")*as.integer64("2147483650"), warning=conditionMessage) + expect_identical(actual_result, expected_result) # no warning for exponent double expect_identical(as.integer64("2147483650")^3, NA_integer64_) }) From 694610c06ffbeadc197b0dd2b0052dab6564baec Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 11 Mar 2026 12:43:27 +0100 Subject: [PATCH 03/17] Revert "fix test for macos" This reverts commit 8952a257fa86f73dab5d14a2d6a707f5d41a8f24. --- tests/testthat/test-ops64.R | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 5d43e006..42813ef4 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -399,10 +399,7 @@ test_that("power with integer64", { expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) - # macos does not return overflow warning - actual_result = tryCatch(as.integer64("2147483650")^3L, warning=conditionMessage) - expected_result = tryCatch(as.integer64("2147483650")*as.integer64("2147483650")*as.integer64("2147483650"), warning=conditionMessage) - expect_identical(actual_result, expected_result) + expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") # no warning for exponent double expect_identical(as.integer64("2147483650")^3, NA_integer64_) }) From c3354f75eecd92312d1d79316e701106c0528f43 Mon Sep 17 00:00:00 2001 From: hcirellu Date: Wed, 11 Mar 2026 12:44:33 +0100 Subject: [PATCH 04/17] fix test for macos --- tests/testthat/test-ops64.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 42813ef4..c9084e3e 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -400,6 +400,4 @@ test_that("power with integer64", { expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") - # no warning for exponent double - expect_identical(as.integer64("2147483650")^3, NA_integer64_) }) From 4ecd2643ef37e50a8fcf5c544b2b21b49afb918b Mon Sep 17 00:00:00 2001 From: chiricom Date: Sun, 16 Aug 2026 18:14:35 +0000 Subject: [PATCH 05/17] use mul64_overflow --- src/integer64.c | 4 ++-- src/integer64.h | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/integer64.c b/src/integer64.c index b640540b..a5a1a19e 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -366,10 +366,10 @@ SEXP power_integer64_integer64(SEXP e1_, SEXP e2_, SEXP ret_){ long long * e1 = (long long *) REAL(e1_); long long * e2 = (long long *) REAL(e2_); long long * ret = (long long *) REAL(ret_); - long long base, exp, intermediate; + long long base, exp; Rboolean naflag = FALSE; mod_iterate(n1, n2, i1, i2) { - POW64(e1[i1],e2[i2],ret[i],naflag,base,exp,intermediate) + POW64(e1[i1],e2[i2],ret[i],naflag,base,exp) } if (naflag)warning(INTEGER64_OVERFLOW_WARNING); return ret_; diff --git a/src/integer64.h b/src/integer64.h index 93f4a803..95aa60df 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -154,7 +154,7 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { ret = llroundl(longret); \ } -#define POW64(e1,e2,ret,naflag,base,exp,intermediate) \ +#define POW64(e1,e2,ret,naflag,base,exp) \ if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \ ret = NA_INTEGER64; \ else { \ @@ -164,9 +164,7 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { exp = e2; \ while (exp > 0) { \ if (exp % 2 == 1) { \ - intermediate = ret; \ - ret *= base; \ - if (!GOODIPROD64(intermediate, base, ret)) { \ + if (mul64_overflow(ret, base, &ret)) { \ ret = NA_INTEGER64; \ naflag = TRUE; \ break; \ From ec1c793afb7df71fdd4e581dd49d3e0bb026ad37 Mon Sep 17 00:00:00 2001 From: chiricom Date: Sun, 16 Aug 2026 19:07:48 +0000 Subject: [PATCH 06/17] style --- R/ops64.R | 5 +++-- man/ops64.Rd | 4 ++++ tests/testthat/test-ops64.R | 8 ++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/R/ops64.R b/R/ops64.R index 6f113ff0..04e0e0ca 100644 --- a/R/ops64.R +++ b/R/ops64.R @@ -15,8 +15,9 @@ #' @param e1,e2,x numeric or complex vectors or objects which can be coerced to such, #' or other objects for which methods have been written for - especially 'integer64' vectors. #' -#' @details -#' [`^`] with 'integer' or 'integer64' exponent now calculates the power precisely and returns an overflow warning, if an overflow appears. +#' @details +#' [`^`] with 'integer' or 'integer64' exponent now calculates the power precisely and returns +#' an overflow warning, if an overflow appears. #' #' @returns #' - [`&`], [`|`], [`!`], [`!=`], [`==`], [`<`], [`<=`], [`>`], [`>=`] return a logical vector diff --git a/man/ops64.Rd b/man/ops64.Rd index 3c1de089..ee3c4a97 100644 --- a/man/ops64.Rd +++ b/man/ops64.Rd @@ -70,6 +70,10 @@ or other objects for which methods have been written for - especially 'integer64 \description{ Binary operators for integer64 vectors. } +\details{ +\code{\link{^}} with 'integer' or 'integer64' exponent now calculates the power precisely and returns +an overflow warning, if an overflow appears. +} \examples{ as.integer64(1:12) - 1 options(integer64_semantics="new") diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index c9084e3e..671e445d 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -304,13 +304,13 @@ with_parameters_test_that("{operator} with integer64 vs {class} (returning integ expected = tryCatch(maybe_cast(op(x32, y)), error=conditionMessage) actual = tryCatch(op(x64, maybe_cast_argument(y)), error=conditionMessage) expect_identical(actual, expected) - + if (operator != "^") { expected = tryCatch(maybe_cast(op(y, x32)), error=conditionMessage) actual = tryCatch(op(y, x64), error=conditionMessage) expect_identical(actual, expected) } -}, +}, .cases = expand.grid( operator=c("+", "-", "*", "/", "^", "%%", "%/%", "<", "<=", "==", ">=", ">", "!=", "&", "|", "xor"), class=c("integer", "double", "logical"), @@ -392,12 +392,12 @@ test_that("power with integer64", { x = as.integer(sqrt(.Machine$integer.max)) x = seq(-x, x) expect_identical(as.integer64(x)^2L, as.integer64(x^2L)) - expect_identical(as.integer64(x)^2, as.integer64(x^2)) + expect_identical(as.integer64(x)^2.0, as.integer64(x^2.0)) # within integer64 range, which fails with double exponent expect_identical(as.integer64("2147483650")^2L, as.integer64("4611686027017322500")) expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) - + expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") }) From 657e8c5cad03dd2500a38ce648375f747580d263 Mon Sep 17 00:00:00 2001 From: chiricom Date: Sun, 16 Aug 2026 19:09:44 +0000 Subject: [PATCH 07/17] style: collapse else if --- src/integer64.h | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/integer64.h b/src/integer64.h index 95aa60df..289984af 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -157,22 +157,20 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { #define POW64(e1,e2,ret,naflag,base,exp) \ if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \ ret = NA_INTEGER64; \ - else { \ - if (e2 >= 0 || e1 == 1 || e1 == -1) { \ - ret = 1; \ - base = e1; \ - exp = e2; \ - while (exp > 0) { \ - if (exp % 2 == 1) { \ - if (mul64_overflow(ret, base, &ret)) { \ - ret = NA_INTEGER64; \ - naflag = TRUE; \ - break; \ - } \ + else if (e2 >= 0 || e1 == 1 || e1 == -1) { \ + ret = 1; \ + base = e1; \ + exp = e2; \ + while (exp > 0) { \ + if (exp % 2 == 1) { \ + if (mul64_overflow(ret, base, &ret)) { \ + ret = NA_INTEGER64; \ + naflag = TRUE; \ + break; \ } \ - base *= base; \ - exp >>= 1; \ } \ + base *= base; \ + exp >>= 1; \ } \ } From 3bf23a6966701168c39f04ebd35215e04d757b30 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 00:53:56 +0000 Subject: [PATCH 08/17] Gemini: suggested test cases --- tests/testthat/test-ops64.R | 65 +++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 671e445d..0f632536 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -388,6 +388,8 @@ test_that("Edge cases for character/factor comparisons work", { }) test_that("power with integer64", { + overflow_warning = "NAs produced by integer64 overflow" + # within integer range x = as.integer(sqrt(.Machine$integer.max)) x = seq(-x, x) @@ -397,7 +399,64 @@ test_that("power with integer64", { # within integer64 range, which fails with double exponent expect_identical(as.integer64("2147483650")^2L, as.integer64("4611686027017322500")) expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) - expect_identical(as.integer64("94906267")^2L, as.integer64("94906267")*as.integer64("94906267")) - - expect_warning(expect_identical(as.integer64("2147483650")^3L, NA_integer64_), "NAs produced by integer64 overflow") + expect_identical(as.integer64("94906267")^2L, as.integer64("94906267") * as.integer64("94906267")) + + # integer64 base with integer64 exponent + expect_identical(as.integer64(10) ^ as.integer64(3), as.integer64(1000)) + expect_identical(as.integer64("2147483650") ^ as.integer64(2), as.integer64("4611686027017322500")) + expect_identical(as.integer64(2) ^ 62L, as.integer64("4611686018427387904")) + expect_identical(as.integer64(2) ^ as.integer64(62), as.integer64("4611686018427387904")) + expect_identical(as.integer64(3) ^ 39L, as.integer64("4052555153018976267")) + + # Special bases: 0, 1, -1 + expect_identical(as.integer64(0) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(0) ^ 1L, as.integer64(0L)) + expect_identical(as.integer64(0) ^ 5L, as.integer64(0L)) + expect_warning(expect_identical(as.integer64(0) ^ (-1L), NA_integer64_)) + + expect_identical(as.integer64(1) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(1) ^ 100L, as.integer64(1L)) + expect_identical(as.integer64(1) ^ (-5L), as.integer64(1L)) + expect_identical(as.integer64(1) ^ lim.integer64()[2L], as.integer64(1L)) + + expect_identical(as.integer64(-1) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(-1) ^ 1L, as.integer64(-1L)) + expect_identical(as.integer64(-1) ^ 2L, as.integer64(1L)) + expect_identical(as.integer64(-1) ^ 3L, as.integer64(-1L)) + expect_identical(as.integer64(-1) ^ (-1L), as.integer64(-1L)) + expect_identical(as.integer64(-1) ^ (-2L), as.integer64(1L)) + expect_identical(as.integer64(-1) ^ (-3L), as.integer64(-1L)) + expect_identical(as.integer64(-1) ^ lim.integer64()[2L], as.integer64(-1L)) + + # Negative exponents with |base| >= 2 + expect_identical(as.integer64(2) ^ (-1L), as.integer64(0L)) + expect_identical(as.integer64(2) ^ (-5L), as.integer64(0L)) + expect_identical(as.integer64(-2) ^ (-1L), as.integer64(0L)) + + # Overflow detection with both odd and even exponents + expect_warning(expect_identical(as.integer64("2147483650") ^ 3L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(100000) ^ 8L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(2) ^ 63L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(2) ^ 64L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(3) ^ 40L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(3) ^ 64L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(lim.integer64()[1L] ^ 2L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(lim.integer64()[2L] ^ 2L, NA_integer64_), overflow_warning) + + # Missing values and empty inputs + expect_no_warning(expect_identical(NA_integer64_ ^ 2L, NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(2) ^ NA_integer_, NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(2) ^ NA_integer64_, NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(2) ^ NA_real_, NA_integer64_)) + expect_identical(integer64(0) ^ 2L, integer64(0)) + expect_identical(as.integer64(2) ^ integer(0), integer64(0)) + expect_identical(integer64(0) ^ integer(0), integer64(0)) + + # Vectorization and recycling + expect_identical(c(as.integer64(2), as.integer64(3)) ^ 2L, as.integer64(c(4, 9))) + expect_identical(as.integer64(2) ^ c(1L, 2L, 3L), as.integer64(c(2, 4, 8))) + expect_warning( + expect_identical(c(as.integer64(2), as.integer64(2)) ^ c(2L, 64L), c(as.integer64(4), NA_integer64_)), + overflow_warning + ) }) From e4cd5ee1d3e2c876a5f1d6ded7d34f0de7d9f05f Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 01:33:03 +0000 Subject: [PATCH 09/17] implement pow64_overflow, lose the macro --- src/integer64.c | 5 ++-- src/integer64.h | 66 ++++++++++++++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/src/integer64.c b/src/integer64.c index a5a1a19e..8fc1d727 100644 --- a/src/integer64.c +++ b/src/integer64.c @@ -366,10 +366,11 @@ SEXP power_integer64_integer64(SEXP e1_, SEXP e2_, SEXP ret_){ long long * e1 = (long long *) REAL(e1_); long long * e2 = (long long *) REAL(e2_); long long * ret = (long long *) REAL(ret_); - long long base, exp; Rboolean naflag = FALSE; mod_iterate(n1, n2, i1, i2) { - POW64(e1[i1],e2[i2],ret[i],naflag,base,exp) + if (pow64_overflow(e1[i1], e2[i2], &ret[i])) { + naflag = TRUE; + } } if (naflag)warning(INTEGER64_OVERFLOW_WARNING); return ret_; diff --git a/src/integer64.h b/src/integer64.h index 289984af..9109259a 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -118,6 +118,52 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { #endif } +static inline bool pow64_overflow(long long base, long long exp, long long *res) { + // special cases: n^0, 1^m, 0^m, -1^m, n^-m, NA^m, n^NA + if (exp == 0) { + *res = 1; + return false; + } + if (base == 1) { + *res = 1; + return false; + } + if (base == 0) { + if (exp < 0) { + *res = NA_INTEGER64; + return true; + } + *res = 0; + return false; + } + if (base == -1) { + *res = exp & 1 ? -1 : 1; + return false; + } + if (exp < 0) { + *res = 0; + return false; + } + if (base == NA_INTEGER64 || exp == NA_INTEGER64) { + *res = NA_INTEGER64; + return false; + } + long long r = 1; + while (exp > 0) { + if (exp & 1 && mul64_overflow(r, base, &r)) { + *res = NA_INTEGER64; + return true; + } + exp >>= 1; + if (exp > 0 && mul64_overflow(base, base, &base)) { + *res = NA_INTEGER64; + return true; + } + } + *res = r; + return false; +} + #define PLUS64(e1,e2,ret,naflag) \ if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \ ret = NA_INTEGER64; \ @@ -154,26 +200,6 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { ret = llroundl(longret); \ } -#define POW64(e1,e2,ret,naflag,base,exp) \ - if (e1 == NA_INTEGER64 || e2 == NA_INTEGER64) \ - ret = NA_INTEGER64; \ - else if (e2 >= 0 || e1 == 1 || e1 == -1) { \ - ret = 1; \ - base = e1; \ - exp = e2; \ - while (exp > 0) { \ - if (exp % 2 == 1) { \ - if (mul64_overflow(ret, base, &ret)) { \ - ret = NA_INTEGER64; \ - naflag = TRUE; \ - break; \ - } \ - } \ - base *= base; \ - exp >>= 1; \ - } \ - } - #define POW64REAL(e1,e2,ret,naflag,longret) \ if (e1 == NA_INTEGER64 || ISNAN(e2)) \ ret = NA_INTEGER64; \ From 4272f9e54f3fa5a708a678d86841c3f62f0c7824 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 01:47:01 +0000 Subject: [PATCH 10/17] restore lost NEWS from merge --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index 74f2f76c..86404d43 100644 --- a/NEWS.md +++ b/NEWS.md @@ -64,6 +64,9 @@ 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. +1. `as.integer64.integer64` returns a plain `integer64` vector stripped of any attributes. This is consistent with R-like behavior, e.g. `as.integer.integer`. +1. `%/%` matches base R/Knuth behavior of taking the `floor()` of a result, where previously 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. + ## NEW FEATURES 1. `anyNA` gets an `integer64` method. Thanks @hcirellu. From b6f20917c8854d8fdf0553a96f0108baba576b1f Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 01:53:44 +0000 Subject: [PATCH 11/17] delint --- R/ops64.R | 2 +- tests/testthat/test-ops64.R | 80 ++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/R/ops64.R b/R/ops64.R index 04e0e0ca..ee43e73e 100644 --- a/R/ops64.R +++ b/R/ops64.R @@ -18,7 +18,7 @@ #' @details #' [`^`] with 'integer' or 'integer64' exponent now calculates the power precisely and returns #' an overflow warning, if an overflow appears. -#' +#' #' @returns #' - [`&`], [`|`], [`!`], [`!=`], [`==`], [`<`], [`<=`], [`>`], [`>=`] return a logical vector #' - [`/`] returns a double vector diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 0f632536..38071134 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -402,61 +402,61 @@ test_that("power with integer64", { expect_identical(as.integer64("94906267")^2L, as.integer64("94906267") * as.integer64("94906267")) # integer64 base with integer64 exponent - expect_identical(as.integer64(10) ^ as.integer64(3), as.integer64(1000)) - expect_identical(as.integer64("2147483650") ^ as.integer64(2), as.integer64("4611686027017322500")) - expect_identical(as.integer64(2) ^ 62L, as.integer64("4611686018427387904")) - expect_identical(as.integer64(2) ^ as.integer64(62), as.integer64("4611686018427387904")) - expect_identical(as.integer64(3) ^ 39L, as.integer64("4052555153018976267")) + expect_identical(as.integer64(10L) ^ as.integer64(3L), as.integer64(1000L)) + expect_identical(as.integer64("2147483650") ^ as.integer64(2L), as.integer64("4611686027017322500")) + expect_identical(as.integer64(2L) ^ 62L, as.integer64("4611686018427387904")) + expect_identical(as.integer64(2L) ^ as.integer64(62L), as.integer64("4611686018427387904")) + expect_identical(as.integer64(3L) ^ 39L, as.integer64("4052555153018976267")) # Special bases: 0, 1, -1 - expect_identical(as.integer64(0) ^ 0L, as.integer64(1L)) - expect_identical(as.integer64(0) ^ 1L, as.integer64(0L)) - expect_identical(as.integer64(0) ^ 5L, as.integer64(0L)) - expect_warning(expect_identical(as.integer64(0) ^ (-1L), NA_integer64_)) - - expect_identical(as.integer64(1) ^ 0L, as.integer64(1L)) - expect_identical(as.integer64(1) ^ 100L, as.integer64(1L)) - expect_identical(as.integer64(1) ^ (-5L), as.integer64(1L)) - expect_identical(as.integer64(1) ^ lim.integer64()[2L], as.integer64(1L)) - - expect_identical(as.integer64(-1) ^ 0L, as.integer64(1L)) - expect_identical(as.integer64(-1) ^ 1L, as.integer64(-1L)) - expect_identical(as.integer64(-1) ^ 2L, as.integer64(1L)) - expect_identical(as.integer64(-1) ^ 3L, as.integer64(-1L)) - expect_identical(as.integer64(-1) ^ (-1L), as.integer64(-1L)) - expect_identical(as.integer64(-1) ^ (-2L), as.integer64(1L)) - expect_identical(as.integer64(-1) ^ (-3L), as.integer64(-1L)) - expect_identical(as.integer64(-1) ^ lim.integer64()[2L], as.integer64(-1L)) + expect_identical(as.integer64(0L) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(0L) ^ 1L, as.integer64(0L)) + expect_identical(as.integer64(0L) ^ 5L, as.integer64(0L)) + expect_warning(expect_identical(as.integer64(0L) ^ (-1L), NA_integer64_)) + + expect_identical(as.integer64(1L) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(1L) ^ 100L, as.integer64(1L)) + expect_identical(as.integer64(1L) ^ (-5L), as.integer64(1L)) + expect_identical(as.integer64(1L) ^ lim.integer64()[2L], as.integer64(1L)) + + expect_identical(as.integer64(-1L) ^ 0L, as.integer64(1L)) + expect_identical(as.integer64(-1L) ^ 1L, as.integer64(-1L)) + expect_identical(as.integer64(-1L) ^ 2L, as.integer64(1L)) + expect_identical(as.integer64(-1L) ^ 3L, as.integer64(-1L)) + expect_identical(as.integer64(-1L) ^ (-1L), as.integer64(-1L)) + expect_identical(as.integer64(-1L) ^ (-2L), as.integer64(1L)) + expect_identical(as.integer64(-1L) ^ (-3L), as.integer64(-1L)) + expect_identical(as.integer64(-1L) ^ lim.integer64()[2L], as.integer64(-1L)) # Negative exponents with |base| >= 2 - expect_identical(as.integer64(2) ^ (-1L), as.integer64(0L)) - expect_identical(as.integer64(2) ^ (-5L), as.integer64(0L)) - expect_identical(as.integer64(-2) ^ (-1L), as.integer64(0L)) + expect_identical(as.integer64(2L) ^ (-1L), as.integer64(0L)) + expect_identical(as.integer64(2L) ^ (-5L), as.integer64(0L)) + expect_identical(as.integer64(-2L) ^ (-1L), as.integer64(0L)) # Overflow detection with both odd and even exponents expect_warning(expect_identical(as.integer64("2147483650") ^ 3L, NA_integer64_), overflow_warning) - expect_warning(expect_identical(as.integer64(100000) ^ 8L, NA_integer64_), overflow_warning) - expect_warning(expect_identical(as.integer64(2) ^ 63L, NA_integer64_), overflow_warning) - expect_warning(expect_identical(as.integer64(2) ^ 64L, NA_integer64_), overflow_warning) - expect_warning(expect_identical(as.integer64(3) ^ 40L, NA_integer64_), overflow_warning) - expect_warning(expect_identical(as.integer64(3) ^ 64L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(100000L) ^ 8L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(2L) ^ 63L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(2L) ^ 64L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(3L) ^ 40L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(3L) ^ 64L, NA_integer64_), overflow_warning) expect_warning(expect_identical(lim.integer64()[1L] ^ 2L, NA_integer64_), overflow_warning) expect_warning(expect_identical(lim.integer64()[2L] ^ 2L, NA_integer64_), overflow_warning) # Missing values and empty inputs expect_no_warning(expect_identical(NA_integer64_ ^ 2L, NA_integer64_)) - expect_no_warning(expect_identical(as.integer64(2) ^ NA_integer_, NA_integer64_)) - expect_no_warning(expect_identical(as.integer64(2) ^ NA_integer64_, NA_integer64_)) - expect_no_warning(expect_identical(as.integer64(2) ^ NA_real_, NA_integer64_)) - expect_identical(integer64(0) ^ 2L, integer64(0)) - expect_identical(as.integer64(2) ^ integer(0), integer64(0)) - expect_identical(integer64(0) ^ integer(0), integer64(0)) + expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer_, NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer64_, NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(2L) ^ NA_real_, NA_integer64_)) + expect_identical(integer64() ^ 2L, integer64()) + expect_identical(as.integer64(2L) ^ integer(), integer64()) + expect_identical(integer64() ^ integer(), integer64()) # Vectorization and recycling - expect_identical(c(as.integer64(2), as.integer64(3)) ^ 2L, as.integer64(c(4, 9))) - expect_identical(as.integer64(2) ^ c(1L, 2L, 3L), as.integer64(c(2, 4, 8))) + expect_identical(c(as.integer64(2L), as.integer64(3L)) ^ 2L, as.integer64(c(4L, 9L))) + expect_identical(as.integer64(2L) ^ c(1L, 2L, 3L), as.integer64(c(2L, 4L, 8L))) expect_warning( - expect_identical(c(as.integer64(2), as.integer64(2)) ^ c(2L, 64L), c(as.integer64(4), NA_integer64_)), + expect_identical(c(as.integer64(2L), as.integer64(2L)) ^ c(2L, 64L), c(as.integer64(4L), NA_integer64_)), overflow_warning ) }) From 19f9924de3aadbf436c3be7d639c6fb13ff72fc9 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 02:00:01 +0000 Subject: [PATCH 12/17] cast from double, not string, where possible --- tests/testthat/test-ops64.R | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 38071134..372f2d83 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -397,13 +397,13 @@ test_that("power with integer64", { expect_identical(as.integer64(x)^2.0, as.integer64(x^2.0)) # within integer64 range, which fails with double exponent - expect_identical(as.integer64("2147483650")^2L, as.integer64("4611686027017322500")) - expect_identical(as.integer64("-2147483650")^2L, as.integer64("4611686027017322500")) - expect_identical(as.integer64("94906267")^2L, as.integer64("94906267") * as.integer64("94906267")) + expect_identical(as.integer64(2147483650)^2L, as.integer64("4611686027017322500")) + expect_identical(as.integer64(-2147483650)^2L, as.integer64("4611686027017322500")) + expect_identical(as.integer64(94906267L)^2L, as.integer64(94906267L) * as.integer64(94906267L)) # integer64 base with integer64 exponent expect_identical(as.integer64(10L) ^ as.integer64(3L), as.integer64(1000L)) - expect_identical(as.integer64("2147483650") ^ as.integer64(2L), as.integer64("4611686027017322500")) + expect_identical(as.integer64(2147483650) ^ as.integer64(2L), as.integer64("4611686027017322500")) expect_identical(as.integer64(2L) ^ 62L, as.integer64("4611686018427387904")) expect_identical(as.integer64(2L) ^ as.integer64(62L), as.integer64("4611686018427387904")) expect_identical(as.integer64(3L) ^ 39L, as.integer64("4052555153018976267")) @@ -434,7 +434,7 @@ test_that("power with integer64", { expect_identical(as.integer64(-2L) ^ (-1L), as.integer64(0L)) # Overflow detection with both odd and even exponents - expect_warning(expect_identical(as.integer64("2147483650") ^ 3L, NA_integer64_), overflow_warning) + expect_warning(expect_identical(as.integer64(2147483650) ^ 3L, NA_integer64_), overflow_warning) expect_warning(expect_identical(as.integer64(100000L) ^ 8L, NA_integer64_), overflow_warning) expect_warning(expect_identical(as.integer64(2L) ^ 63L, NA_integer64_), overflow_warning) expect_warning(expect_identical(as.integer64(2L) ^ 64L, NA_integer64_), overflow_warning) From 011225b0e976c2046857b38dcbc1bf79f0f61c54 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 05:30:57 +0000 Subject: [PATCH 13/17] fix NA check ordering hidden by waldo bug --- src/integer64.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/integer64.h b/src/integer64.h index 9109259a..05da52a1 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -128,6 +128,10 @@ static inline bool pow64_overflow(long long base, long long exp, long long *res) *res = 1; return false; } + if (base == NA_INTEGER64 || exp == NA_INTEGER64) { + *res = NA_INTEGER64; + return false; + } if (base == 0) { if (exp < 0) { *res = NA_INTEGER64; @@ -144,10 +148,6 @@ static inline bool pow64_overflow(long long base, long long exp, long long *res) *res = 0; return false; } - if (base == NA_INTEGER64 || exp == NA_INTEGER64) { - *res = NA_INTEGER64; - return false; - } long long r = 1; while (exp > 0) { if (exp & 1 && mul64_overflow(r, base, &r)) { From abf28fdb845849036c187079b2953a029f37959c Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 05:34:17 +0000 Subject: [PATCH 14/17] Gemini: more missing tests --- tests/testthat/test-ops64.R | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 372f2d83..6b463fe6 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -299,17 +299,14 @@ with_parameters_test_that("{operator} with integer64 vs {class} (returning integ op = match.fun(operator) maybe_cast = if (operator %in% c("+", "-", "*", "^", "%%", "%/%")) as.integer64 else identity - maybe_cast_argument = if (operator == "^") as.double else identity expected = tryCatch(maybe_cast(op(x32, y)), error=conditionMessage) - actual = tryCatch(op(x64, maybe_cast_argument(y)), error=conditionMessage) + actual = tryCatch(op(x64, y), error=conditionMessage) expect_identical(actual, expected) - if (operator != "^") { - expected = tryCatch(maybe_cast(op(y, x32)), error=conditionMessage) - actual = tryCatch(op(y, x64), error=conditionMessage) - expect_identical(actual, expected) - } + expected = tryCatch(maybe_cast(op(y, x32)), error=conditionMessage) + actual = tryCatch(op(y, x64), error=conditionMessage) + expect_identical(actual, expected) }, .cases = expand.grid( operator=c("+", "-", "*", "/", "^", "%%", "%/%", "<", "<=", "==", ">=", ">", "!=", "&", "|", "xor"), @@ -412,12 +409,14 @@ test_that("power with integer64", { expect_identical(as.integer64(0L) ^ 0L, as.integer64(1L)) expect_identical(as.integer64(0L) ^ 1L, as.integer64(0L)) expect_identical(as.integer64(0L) ^ 5L, as.integer64(0L)) - expect_warning(expect_identical(as.integer64(0L) ^ (-1L), NA_integer64_)) + expect_warning(expect_identical(as.integer64(0L) ^ (-1L), NA_integer64_), overflow_warning) expect_identical(as.integer64(1L) ^ 0L, as.integer64(1L)) expect_identical(as.integer64(1L) ^ 100L, as.integer64(1L)) expect_identical(as.integer64(1L) ^ (-5L), as.integer64(1L)) expect_identical(as.integer64(1L) ^ lim.integer64()[2L], as.integer64(1L)) + expect_identical(as.integer64(1L) ^ NA_integer64_, as.integer64(1L)) + expect_identical(as.integer64(1L) ^ NA_integer_, as.integer64(1L)) expect_identical(as.integer64(-1L) ^ 0L, as.integer64(1L)) expect_identical(as.integer64(-1L) ^ 1L, as.integer64(-1L)) @@ -426,12 +425,18 @@ test_that("power with integer64", { expect_identical(as.integer64(-1L) ^ (-1L), as.integer64(-1L)) expect_identical(as.integer64(-1L) ^ (-2L), as.integer64(1L)) expect_identical(as.integer64(-1L) ^ (-3L), as.integer64(-1L)) + expect_identical(as.integer64(-1L) ^ lim.integer64()[1L], as.integer64(-1L)) expect_identical(as.integer64(-1L) ^ lim.integer64()[2L], as.integer64(-1L)) # Negative exponents with |base| >= 2 expect_identical(as.integer64(2L) ^ (-1L), as.integer64(0L)) expect_identical(as.integer64(2L) ^ (-5L), as.integer64(0L)) expect_identical(as.integer64(-2L) ^ (-1L), as.integer64(0L)) + expect_identical(as.integer64(-2L) ^ (-2L), as.integer64(0L)) + + # Boundary base values with exponent 1 + expect_identical(lim.integer64()[1L] ^ 1L, lim.integer64()[1L]) + expect_identical(lim.integer64()[2L] ^ 1L, lim.integer64()[2L]) # Overflow detection with both odd and even exponents expect_warning(expect_identical(as.integer64(2147483650) ^ 3L, NA_integer64_), overflow_warning) @@ -444,10 +449,23 @@ test_that("power with integer64", { expect_warning(expect_identical(lim.integer64()[2L] ^ 2L, NA_integer64_), overflow_warning) # Missing values and empty inputs + expect_no_warning(expect_identical(NA_integer64_ ^ 0L, as.integer64(1L))) expect_no_warning(expect_identical(NA_integer64_ ^ 2L, NA_integer64_)) + expect_true(is.na(NA_integer64_ ^ 2L)) + expect_no_warning(expect_identical(NA_integer64_ ^ (-1L), NA_integer64_)) + expect_true(is.na(NA_integer64_ ^ (-1L))) + expect_no_warning(expect_identical(NA_integer64_ ^ NA_integer64_, NA_integer64_)) + expect_true(is.na(NA_integer64_ ^ NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(0L) ^ NA_integer64_, NA_integer64_)) + expect_true(is.na(as.integer64(0L) ^ NA_integer64_)) + expect_no_warning(expect_identical(as.integer64(-1L) ^ NA_integer64_, NA_integer64_)) + expect_true(is.na(as.integer64(-1L) ^ NA_integer64_)) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer_, NA_integer64_)) + expect_true(is.na(as.integer64(2L) ^ NA_integer_)) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer64_, NA_integer64_)) + expect_true(is.na(as.integer64(2L) ^ NA_integer64_)) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_real_, NA_integer64_)) + expect_true(is.na(as.integer64(2L) ^ NA_real_)) expect_identical(integer64() ^ 2L, integer64()) expect_identical(as.integer64(2L) ^ integer(), integer64()) expect_identical(integer64() ^ integer(), integer64()) From de445c95f16ac6eefcb7748494561dc7743e4b9a Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 05:38:16 +0000 Subject: [PATCH 15/17] prefer expect_identical to expect_true, ignoring waldo bug --- tests/testthat/test-ops64.R | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/testthat/test-ops64.R b/tests/testthat/test-ops64.R index 6b463fe6..2e486574 100644 --- a/tests/testthat/test-ops64.R +++ b/tests/testthat/test-ops64.R @@ -451,21 +451,21 @@ test_that("power with integer64", { # Missing values and empty inputs expect_no_warning(expect_identical(NA_integer64_ ^ 0L, as.integer64(1L))) expect_no_warning(expect_identical(NA_integer64_ ^ 2L, NA_integer64_)) - expect_true(is.na(NA_integer64_ ^ 2L)) + expect_identical(NA_integer64_ ^ 2L, NA_integer64_) expect_no_warning(expect_identical(NA_integer64_ ^ (-1L), NA_integer64_)) - expect_true(is.na(NA_integer64_ ^ (-1L))) + expect_identical(NA_integer64_ ^ (-1L), NA_integer64_) expect_no_warning(expect_identical(NA_integer64_ ^ NA_integer64_, NA_integer64_)) - expect_true(is.na(NA_integer64_ ^ NA_integer64_)) + expect_identical(NA_integer64_ ^ NA_integer64_, NA_integer64_) expect_no_warning(expect_identical(as.integer64(0L) ^ NA_integer64_, NA_integer64_)) - expect_true(is.na(as.integer64(0L) ^ NA_integer64_)) + expect_identical(as.integer64(0L) ^ NA_integer64_, NA_integer64_) expect_no_warning(expect_identical(as.integer64(-1L) ^ NA_integer64_, NA_integer64_)) - expect_true(is.na(as.integer64(-1L) ^ NA_integer64_)) + expect_identical(as.integer64(-1L) ^ NA_integer64_, NA_integer64_) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer_, NA_integer64_)) - expect_true(is.na(as.integer64(2L) ^ NA_integer_)) + expect_identical(as.integer64(2L) ^ NA_integer_, NA_integer64_) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_integer64_, NA_integer64_)) - expect_true(is.na(as.integer64(2L) ^ NA_integer64_)) + expect_identical(as.integer64(2L) ^ NA_integer64_, NA_integer64_) expect_no_warning(expect_identical(as.integer64(2L) ^ NA_real_, NA_integer64_)) - expect_true(is.na(as.integer64(2L) ^ NA_real_)) + expect_identical(as.integer64(2L) ^ NA_real_, NA_integer64_) expect_identical(integer64() ^ 2L, integer64()) expect_identical(as.integer64(2L) ^ integer(), integer64()) expect_identical(integer64() ^ integer(), integer64()) From 8ab076d3568d39f1547903aeb0f6430a25c59d51 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 05:56:39 +0000 Subject: [PATCH 16/17] Gemini: more special cases & simpler loop for performance --- src/integer64.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/integer64.h b/src/integer64.h index 05da52a1..5ff0a5e0 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -132,6 +132,17 @@ static inline bool pow64_overflow(long long base, long long exp, long long *res) *res = NA_INTEGER64; return false; } + if (exp == 1) { + *res = base; + return false; + } + if (exp == 2) { + if (mul64_overflow(base, base, res)) { + *res = NA_INTEGER64; + return true; + } + return false; + } if (base == 0) { if (exp < 0) { *res = NA_INTEGER64; @@ -149,13 +160,14 @@ static inline bool pow64_overflow(long long base, long long exp, long long *res) return false; } long long r = 1; - while (exp > 0) { + while (1) { if (exp & 1 && mul64_overflow(r, base, &r)) { *res = NA_INTEGER64; return true; } exp >>= 1; - if (exp > 0 && mul64_overflow(base, base, &base)) { + if (!exp) break; + if (mul64_overflow(base, base, &base)) { *res = NA_INTEGER64; return true; } From 20e5a7e624493731b5877901a700fbfab678f723 Mon Sep 17 00:00:00 2001 From: chiricom Date: Mon, 17 Aug 2026 06:32:39 +0000 Subject: [PATCH 17/17] update comment --- src/integer64.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integer64.h b/src/integer64.h index 5ff0a5e0..ecf6e2f6 100644 --- a/src/integer64.h +++ b/src/integer64.h @@ -119,7 +119,7 @@ static inline bool mul64_overflow(long long a, long long b, long long *res) { } static inline bool pow64_overflow(long long base, long long exp, long long *res) { - // special cases: n^0, 1^m, 0^m, -1^m, n^-m, NA^m, n^NA + // special cases: n^0, 1^m, NA^m, n^NA, n^1, n^2, 0^m, -1^m, n^-m if (exp == 0) { *res = 1; return false;