skip_on_cran() test_that("<<- supports whole-vector superassignment via x[] across lengths", { fn <- function(x) { declare(type(x = double(NA))) f <- function() { x[] <<- x + 1.0 NULL } f() x } expect_quick_identical(fn, list(1), list(c(1, 2)), list(c(1, 2, 3, 4, 5))) }) test_that("<<- supports vector indexing with double scalar indices (casts to int)", { fn <- function(x, idx) { declare(type(x = double(NA)), type(idx = double(1))) f <- function() { x[idx] <<- x[idx] + 1.0 NULL } f() x } x <- c(1, 2, 3, 4) expect_quick_identical(fn, list(x, 1), list(x, 2), list(x, 4)) }) test_that("<<- supports matrix element and section targets together", { fn <- function(x) { declare(type(x = double(NA, NA))) j <- 2L f <- function() { x[1, ] <<- 1.0 x[, j] <<- x[, j] + 2.0 x[2, 2] <<- x[2, 2] + 3.0 NULL } f() x } set.seed(1) expect_quick_identical( fn, list(matrix(runif(6), 2, 3)), list(matrix(runif(12), 3, 4)) ) }) test_that("<<- mutating a matrix argument preserves caller copy-on-modify semantics", { fn <- function(x) { declare(type(x = double(NA, NA))) f <- function() { x[1, ] <<- 0.0 x[, 1] <<- x[, 1] + 1.0 NULL } f() x } x1 <- matrix(as.double(1:12), 3, 4) x2 <- matrix(as.double(1:6), 2, 3) expected1 <- x1 expected1[1, ] <- 0.0 expected1[, 1] <- expected1[, 1] + 1.0 expected2 <- x2 expected2[1, ] <- 0.0 expected2[, 1] <- expected2[, 1] + 1.0 expect_identical(fn(x1), expected1) expect_identical(fn(x2), expected2) expect_quick_identical(fn, x1, x2) qfn <- quick(fn) for (x0 in list(x1, x2)) { x <- x0 out <- qfn(x) expect_identical(out, fn(x0)) expect_identical(x, x0) } }) test_that("<<- supports 3D slice superassignment with missing indices", { fn <- function(nx, ny, nz) { declare(type(nx = integer(1)), type(ny = integer(1)), type(nz = integer(1))) a <- array(0.0, c(nx, ny, nz)) f <- function(k) { a[,, k] <<- as.double(k) a[1, 1, k] <<- a[1, 1, k] + 0.5 NULL } f(1L) f(nz) a } expect_quick_identical(fn, list(2L, 3L, 2L), list(3L, 2L, 3L)) }) test_that("sapply + <<- can mutate a matrix host argument and return a matrix", { fn <- function(x) { declare(type(x = double(NA, NA))) out <- x f <- function(j) { x[, j] <<- x[, j] * 2.0 x[, j] } out <- sapply(seq_len(ncol(x)), f, simplify = "array") list(x = x, out = out) } x1 <- matrix(as.double(1:12), 3, 4) x2 <- matrix(as.double(1:6), 2, 3) expect_identical(fn(x1), list(x = x1 * 2.0, out = x1 * 2.0)) expect_identical(fn(x2), list(x = x2 * 2.0, out = x2 * 2.0)) expect_quick_identical(fn, x1, x2) qfn <- quick(fn) for (x0 in list(x1, x2)) { x <- x0 out <- qfn(x) expect_identical(out, fn(x0)) expect_identical(x, x0) } }) test_that("sapply + <<- supports host scalar accumulation", { fn <- function(x) { declare(type(x = double(NA))) out <- double(length(x)) acc <- 0.0 f <- function(i) { acc <<- acc + x[i] acc } out <- sapply(seq_along(x), f) list(acc = acc, out = out) } set.seed(1) x <- runif(10) expect_quick_identical(fn, list(x)) }) test_that("statement closures can be called in loops while mutating host arrays", { fn <- function(n) { declare(type(n = integer(1))) x <- integer(n) f <- function(i) { x[i] <<- i NULL } for (i in seq_len(n)) { f(i) } x } expect_quick_identical(fn, list(1L), list(2L), list(7L)) }) test_that("<<- supports rank>1 linear indexing with computed scalar indices", { fn <- function(x) { declare(type(x = double(NA, NA))) k <- nrow(x) * ncol(x) kk <- as.double(k) f <- function() { x[k] <<- x[k] + 1.0 x[kk] <<- x[kk] + 2.0 NULL } f() x } set.seed(1) expect_quick_identical( fn, list(matrix(runif(6), 2, 3)), list(matrix(runif(12), 3, 4)) ) }) test_that("<<- supports 3D element superassignment via a[i, j, k]", { fn <- function(nx, ny, nz) { declare(type(nx = integer(1)), type(ny = integer(1)), type(nz = integer(1))) a <- array(0.0, c(nx, ny, nz)) bump <- function(i, j, k) { a[i, j, k] <<- as.double(i + j + k) NULL } bump(1L, 1L, 1L) bump(nx, ny, nz) a } expect_quick_identical(fn, list(2L, 3L, 2L), list(3L, 2L, 3L)) })