skip_on_cran()

test_that("large local arrays are emitted as allocatable + allocate()", {
  f <- function(x) {
    declare(type(x = double(16, 64, 64, 16)))
    a <- x + x
    b <- a + x
    b
  }

  fsub <- r2f(f)
  code <- as.character(fsub)

  expect_match(code, "allocatable[^\\n]*:: a\\(")
  expect_match(code, "allocate\\(a\\(")

  fq <- quick(f)
  set.seed(1)
  x <- array(runif(16 * 64 * 64 * 16), dim = c(16, 64, 64, 16))
  expect_identical(f(x), fq(x))
})

test_that("multiple local arrays do not stack overflow under flang", {
  f <- function(x) {
    declare(type(x = double(16, 48, 48, 16)))
    a <- x + x
    b <- a + x
    c <- b + x
    c
  }

  fq <- quick(f)
  set.seed(2)
  x <- array(runif(16 * 48 * 48 * 16), dim = c(16, 48, 48, 16))
  expect_identical(f(x), fq(x))
})

test_that("local allocatable threshold accounts for storage mode", {
  mk <- function(mode, nrow = 300L, ncol = 300L) {
    force(mode)
    force(nrow)
    force(ncol)
    if (identical(mode, "integer")) {
      function(x) {
        declare(type(x = integer(nrow, ncol)))
        a <- x + x
        out <- a + x
        out
      }
    } else if (identical(mode, "complex")) {
      function(x) {
        declare(type(x = complex(nrow, ncol)))
        a <- x + x
        out <- a + x
        out
      }
    } else if (identical(mode, "logical")) {
      function(x) {
        declare(type(x = logical(nrow, ncol)))
        a <- !x
        out <- a & x
        out
      }
    } else {
      stop("unknown mode: ", mode)
    }
  }

  modes <- c("integer", "complex", "logical")
  for (mode in modes) {
    f <- mk(mode)
    code <- as.character(r2f(f))
    expect_match(code, "allocatable[^\\n]*:: a\\(")
    expect_match(code, "allocate\\(a\\(")
  }

  set.seed(3)
  x_int <- matrix(sample.int(100, 300L * 300L, replace = TRUE), nrow = 300L)
  expect_identical(quick(mk("integer"))(x_int), mk("integer")(x_int))

  set.seed(4)
  x_cplx <- matrix(
    complex(
      real = runif(300L * 300L),
      imaginary = runif(300L * 300L)
    ),
    nrow = 300L
  )
  expect_equal(quick(mk("complex"))(x_cplx), mk("complex")(x_cplx))

  set.seed(5)
  x_lgl <- matrix(
    sample(c(TRUE, FALSE), 300L * 300L, replace = TRUE),
    nrow = 300L
  )
  expect_identical(quick(mk("logical"))(x_lgl), mk("logical")(x_lgl))
})

test_that("unknown local element counts are treated as allocatable", {
  f <- function(x, n) {
    declare(type(n = integer(1)), type(x = double(n, n)))
    a <- x + x
    out <- a + x
    out
  }

  code <- as.character(r2f(f))
  expect_match(code, "allocatable[^\\n]*:: a\\(")
  expect_match(code, "allocate\\(a\\(")

  set.seed(6)
  x <- matrix(runif(40 * 40), nrow = 40)
  expect_identical(quick(f)(x, 40L), f(x, 40L))
})

test_that("raw locals are sized as 1 byte for heap allocation decisions", {
  f <- function(x) {
    declare(type(x = raw(513L, 513L)))
    a <- x
    b <- a
    b
  }

  code <- as.character(r2f(f))
  expect_match(code, "allocatable[^\\n]*:: a\\(")
  expect_match(code, "allocate\\(a\\(")
})

test_that("heap sizing accounts for complex and logical storage", {
  # Use local declarations with fixed dims so var_element_count() is known and
  # var_storage_bytes() is exercised.
  f_complex <- function() {
    declare(type(a = complex(10L, 10L)))
    1
  }
  f_logical <- function() {
    declare(type(a = logical(10L, 10L)))
    1
  }

  # Codegen is enough; no need to run compiled code here.
  expect_match(as.character(r2f(f_complex)), "complex\\(", perl = TRUE)
  expect_match(as.character(r2f(f_logical)), "logical|! logical", perl = TRUE)
})

test_that("deferred-shape locals are allocatable without explicit allocate()", {
  f <- function(x) {
    declare(type(x = double(NA, NA)), type(a = double(NA, NA)))
    a <- x
    1
  }

  code <- as.character(r2f(f))
  expect_match(code, "allocatable[^\\n]*:: a\\(")
  expect_match(code, ":: a\\(:,\\s*:\\)")
  expect_no_match(code, "allocate\\(a\\(")
})

test_that("deferred-shape locals (self-sized dims) snapshot", {
  # For locals declared with unknown sizes like double(NA, NA), the declared
  # sizes are rewritten to `a__dim_*` symbols. Those sizes are not available for
  # explicit allocation, so we rely on implicit allocation and emit `a(:, :)`.
  f <- function(x) {
    declare(type(x = double(NA, NA)), type(a = double(NA, NA)))
    a <- x
    1
  }

  expect_snapshot(r2f(f))

  x <- matrix(as.double(1:6), nrow = 2)
  expect_quick_identical(f, x)
})
