# Unit tests for cbind() and rbind() skip_on_cran() expect_bind_equal <- function(fn, ...) { qfn := quick(fn) args_list <- rlang::list2(...) args_list <- lapply(args_list, function(x) if (!is.list(x)) list(x) else x) for (args in args_list) { fn_res <- do.call(fn, args) qfn_res <- do.call(qfn, args) expect_identical(dim(fn_res), dim(qfn_res)) expect_equal(unname(fn_res), unname(qfn_res)) expect_identical(typeof(fn_res), typeof(qfn_res)) } } test_that("cbind binds vectors and matrices with scalar recycling", { cbind_vec <- function(x, y, s) { declare(type(x = double(n)), type(y = double(n)), type(s = double(1))) cbind(x, y, s) } set.seed(1) x <- runif(4) y <- runif(4) s <- 2.5 expect_bind_equal(cbind_vec, list(x, y, s)) cbind_mat <- function(A, v) { declare(type(A = double(n, m)), type(v = double(n))) cbind(A, v) } A <- matrix(runif(6), nrow = 3) v <- runif(3) expect_bind_equal(cbind_mat, list(A, v)) cbind_int <- function(x, y) { declare(type(x = integer(n)), type(y = integer(n))) cbind(x, y) } x_int <- 1:3 y_int <- 4:6 expect_bind_equal(cbind_int, list(x_int, y_int)) }) test_that("rbind binds vectors and matrices with scalar recycling", { rbind_vec <- function(x, y, s) { declare(type(x = double(n)), type(y = double(n)), type(s = double(1))) rbind(x, y, s) } set.seed(2) x <- runif(5) y <- runif(5) s <- -1.25 expect_bind_equal(rbind_vec, list(x, y, s)) rbind_mat <- function(A, v) { declare(type(A = double(n, m)), type(v = double(m))) rbind(A, v) } A <- matrix(runif(6), nrow = 2) v <- runif(3) expect_bind_equal(rbind_mat, list(A, v)) rbind_int <- function(x, y) { declare(type(x = integer(n)), type(y = integer(n))) rbind(x, y) } x_int <- 1:4 y_int <- 5:8 expect_bind_equal(rbind_int, list(x_int, y_int)) }) test_that("cbind/rbind enforce common lengths", { bad_cbind <- function(x, y) { declare(type(x = double(2)), type(y = double(3))) cbind(x, y) } expect_error( quick(bad_cbind), "common row count", fixed = TRUE ) bad_rbind <- function(A, B) { declare(type(A = double(2, 3)), type(B = double(2, 4))) rbind(A, B) } expect_error( quick(bad_rbind), "common column count", fixed = TRUE ) }) test_that("cbind/rbind reject rank > 2 inputs with clear errors", { capture_bind_error <- function(expr) { tryCatch(expr, error = function(e) cat(conditionMessage(e), "\n")) } bad_cbind <- function(x) { declare(type(x = double(2, 2, 2))) cbind(x) } bad_rbind <- function(x) { declare(type(x = double(2, 2, 2))) rbind(x) } expect_snapshot({ capture_bind_error(r2f(bad_cbind)) capture_bind_error(r2f(bad_rbind)) }) }) test_that("cbind/rbind handle mixed rank-2, rank-1, and rank-0 inputs", { cbind_mixed <- function(A, v1, s1, B, s2, v2, C, v3, s3) { declare( type(A = double(n, 2)), type(B = double(n, 1)), type(C = double(n, 3)), type(v1 = double(n)), type(v2 = double(n)), type(v3 = double(n)), type(s1 = double(1)), type(s2 = double(1)), type(s3 = double(1)) ) cbind(A, v1, s1, B, s2, v2, C, v3, s3) } rbind_mixed <- function(A, v1, s1, B, s2, v2, C, v3, s3) { declare( type(A = double(2, m)), type(B = double(1, m)), type(C = double(3, m)), type(v1 = double(m)), type(v2 = double(m)), type(v3 = double(m)), type(s1 = double(1)), type(s2 = double(1)), type(s3 = double(1)) ) rbind(A, v1, s1, B, s2, v2, C, v3, s3) } set.seed(42) n <- 2L A <- matrix(runif(n * 2L), nrow = n) B <- matrix(runif(n * 1L), nrow = n) C <- matrix(runif(n * 3L), nrow = n) v1 <- runif(n) v2 <- runif(n) v3 <- runif(n) s1 <- -0.5 s2 <- 1.25 s3 <- 0.0 expect_bind_equal( cbind_mixed, list(A, v1, s1, B, s2, v2, C, v3, s3) ) m <- 3L A2 <- matrix(runif(2L * m), nrow = 2L) B2 <- matrix(runif(1L * m), nrow = 1L) C2 <- matrix(runif(3L * m), nrow = 3L) w1 <- runif(m) w2 <- runif(m) w3 <- runif(m) t1 <- 2.0 t2 <- -1.0 t3 <- 0.5 expect_bind_equal( rbind_mixed, list(A2, w1, t1, B2, t2, w2, C2, w3, t3) ) }) test_that("cbind/rbind coerce logical inputs when mixed with integer", { cbind_int_lgl <- function(i, l) { declare(type(i = integer(n)), type(l = logical(n))) cbind(i, l) } rbind_int_lgl <- function(i, l) { declare(type(i = integer(n)), type(l = logical(n))) rbind(i, l) } i <- 1:4 l <- c(TRUE, FALSE, TRUE, FALSE) expect_bind_equal(cbind_int_lgl, list(i, l)) expect_bind_equal(rbind_int_lgl, list(i, l)) }) test_that("cbind/rbind coerce integers to double when mixed", { cbind_dbl_int <- function(d, i) { declare(type(d = double(n)), type(i = integer(n))) cbind(d, i) } rbind_dbl_int <- function(d, i) { declare(type(d = double(n)), type(i = integer(n))) rbind(d, i) } d <- c(1.25, -0.5, 3.0) i <- c(1L, 2L, 3L) expect_bind_equal(cbind_dbl_int, list(d, i)) expect_bind_equal(rbind_dbl_int, list(d, i)) }) test_that("cbind supports complex inputs", { cbind_complex <- function(z1, z2) { declare(type(z1 = complex(n)), type(z2 = complex(n))) cbind(z1, z2) } set.seed(10) x <- runif(4) y <- runif(4) z1 <- complex(real = x, imaginary = y) z2 <- complex(real = y, imaginary = x) expect_bind_equal(cbind_complex, list(z1, z2)) }) test_that("cbind rejects mixing complex with non-complex", { bad <- function(z, x) { declare(type(z = complex(n)), type(x = double(n))) cbind(z, x) } expect_error(quick(bad), "mixing complex", fixed = TRUE) }) test_that("cbind requires dimensions to be equal at compile time", { fn <- function(x, y, n, m) { declare( type(n = integer(1)), type(m = integer(1)), type(x = double(n)), type(y = double(m)) ) cbind(x, y) } expect_error( quick(fn), "common row count", fixed = TRUE ) }) test_that("cbind supports symbolic dimension expressions in output shape", { fn <- function(x, A, n, m) { declare( type(n = integer(1)), type(m = integer(1)), type(x = double(n)), type(A = double(n, m)) ) cbind(x, A) } set.seed(99) n <- 3L m <- 2L x <- runif(n) A <- matrix(runif(n * m), nrow = n) expect_bind_equal(fn, list(x, A, n, m)) }) test_that("cbind requires inferred dimensions to match", { unknown_rows <- function(A, B) { declare(type(A = double(NA, 2)), type(B = double(NA, 1))) cbind(A, B) } q_unknown_rows <- NULL expect_error( q_unknown_rows <- quick(unknown_rows), "common row count", fixed = TRUE ) expect_null(q_unknown_rows) })