skip_on_cran()

test_that("[ handles scalar, missing, and logical subscripts", {
  m <- matrix(1:6, nrow = 2L, ncol = 3L, byrow = TRUE)

  scalar_drop <- function(m) {
    declare(type(m = integer(NA, NA)))
    m[1L, 1L]
  }
  expect_quick_identical(scalar_drop, list(m))

  scalar_keep <- function(m) {
    declare(type(m = integer(NA, NA)))
    m[1L, 1L, drop = FALSE]
  }
  expect_quick_identical(scalar_keep, list(m))

  whole_col_missing <- function(m) {
    declare(type(m = integer(NA, NA)))
    m[, 2L]
  }
  expect_quick_identical(whole_col_missing, list(m))

  whole_col_logical0 <- function(m) {
    declare(type(m = integer(NA, NA)))
    m[TRUE, 2L]
  }
  expect_quick_identical(whole_col_logical0, list(m))

  logical_rows_sum <- function(m) {
    declare(type(m = integer(NA, NA)))
    sum(m[c(TRUE, FALSE), ])
  }
  expect_quick_identical(logical_rows_sum, list(m))

  pack_filter_sum <- function(m) {
    declare(type(m = integer(NA, NA)))
    sum(m[m > 3L])
  }
  expect_quick_identical(pack_filter_sum, list(m))
})


test_that("reduction intrinsics cover scalar, multi-arg, and mask cases", {
  min_two <- function(x, y) {
    declare(type(x = integer(NA)), type(y = integer(NA)))
    min(x, y)
  }
  fsub <- r2f(min_two)
  expect_match(as.character(fsub), "minval\\(")
  expect_quick_identical(min_two, list(1:4, 2:5))

  min_mix_scalar <- function(x) {
    declare(type(x = integer(NA)))
    min(x, 0L)
  }
  fsub <- r2f(min_mix_scalar)
  expect_match(as.character(fsub), "minval\\(")
  expect_quick_identical(min_mix_scalar, list(c(3L, -1L, 2L)))

  sum_masked <- function(x) {
    declare(type(x = double(NA)))
    sum(x[x > 0])
  }
  fsub <- r2f(sum_masked)
  expect_match(as.character(fsub), "sum\\(x, mask =")
  expect_quick_equal(sum_masked, list(c(-2, 1, 3, -4, 5)))

  prod_two <- function(x, y) {
    declare(type(x = double(NA)), type(y = double(NA)))
    prod(x, y)
  }
  fsub <- r2f(prod_two)
  expect_match(as.character(fsub), "product\\(")
  expect_quick_equal(prod_two, list(c(1, 2, 3), c(4, 5, 6)))
})

test_that("any/all reduction intrinsics cover scalar, multi-arg, and mask cases", {
  any_basic <- function(x) {
    declare(type(x = logical(NA)))
    any(x)
  }
  fsub <- r2f(any_basic)
  expect_match(as.character(fsub), "any\\(")
  expect_quick_identical(any_basic, list(c(FALSE, FALSE)), list(c(FALSE, TRUE)))

  all_basic <- function(x) {
    declare(type(x = logical(NA)))
    all(x)
  }
  fsub <- r2f(all_basic)
  expect_match(as.character(fsub), "all\\(")
  expect_quick_identical(all_basic, list(c(TRUE, TRUE)), list(c(TRUE, FALSE)))

  any_two <- function(a, b) {
    declare(type(a = logical(NA)), type(b = logical(NA)))
    any(a, b)
  }
  fsub <- r2f(any_two)
  expect_match(as.character(fsub), "\\.or\\.")
  expect_quick_identical(
    any_two,
    list(a = c(FALSE, FALSE), b = c(FALSE, FALSE)),
    list(a = c(FALSE, FALSE), b = c(TRUE, FALSE))
  )

  all_two <- function(a, b) {
    declare(type(a = logical(NA)), type(b = logical(NA)))
    all(a, b)
  }
  fsub <- r2f(all_two)
  expect_match(as.character(fsub), "\\.and\\.")
  expect_quick_identical(
    all_two,
    list(a = c(TRUE, TRUE), b = c(TRUE, TRUE)),
    list(a = c(TRUE, TRUE), b = c(FALSE, TRUE))
  )

  # Scalar masked subset: preserve empty-selection semantics.
  any_scalar_masked_empty <- function(x) {
    declare(type(x = logical(1)))
    any(x[c(FALSE)])
  }
  expect_quick_identical(any_scalar_masked_empty, list(TRUE), list(FALSE))

  all_scalar_masked_empty <- function(x) {
    declare(type(x = logical(1)))
    all(x[c(FALSE)])
  }
  expect_quick_identical(all_scalar_masked_empty, list(TRUE), list(FALSE))

  # Scalar masked subset with a scalar mask variable: should compile and
  # preserve empty-selection semantics.
  any_scalar_masked_var <- function(x, m) {
    declare(type(x = logical(1)), type(m = logical(1)))
    any(x[m])
  }
  expect_quick_identical(
    any_scalar_masked_var,
    list(x = TRUE, m = FALSE),
    list(x = FALSE, m = TRUE)
  )

  all_scalar_masked_var <- function(x, m) {
    declare(type(x = logical(1)), type(m = logical(1)))
    all(x[m])
  }
  expect_quick_identical(
    all_scalar_masked_var,
    list(x = FALSE, m = FALSE),
    list(x = FALSE, m = TRUE)
  )

  # Scalar `x` with a longer logical mask diverges from simple empty/non-empty
  # semantics in R because selecting out-of-range positions yields NAs. We don't
  # implement that behavior here, so fail fast instead of silently emitting
  # wrong code.
  any_scalar_masked_long_mask <- function(x) {
    declare(type(x = logical(1)))
    any(x[c(FALSE, TRUE)])
  }
  expect_error(r2f(any_scalar_masked_long_mask), "scalar masked subsets")

  all_scalar_masked_long_mask <- function(x) {
    declare(type(x = logical(1)))
    all(x[c(FALSE, TRUE)])
  }
  expect_error(r2f(all_scalar_masked_long_mask), "scalar masked subsets")

  # 1-element vector expressions like c(FALSE) compile to Fortran array
  # constructors (`[.false.]`) but any()/all() must still return scalars.
  any_array_ctor_len1 <- function() {
    any(c(FALSE))
  }
  expect_quick_identical(any_array_ctor_len1, list())

  all_array_ctor_len1 <- function() {
    all(c(TRUE))
  }
  expect_quick_identical(all_array_ctor_len1, list())

  # Masked reduction over a 1-element array constructor: `[` can hoist the mask
  # while leaving `x` as a rank-1 array constructor (`[.true.]`), but the
  # reduction result must still be scalar.
  any_array_ctor_len1_masked <- function() {
    any(c(TRUE)[c(TRUE)])
  }
  expect_quick_identical(any_array_ctor_len1_masked, list())

  all_array_ctor_len1_masked_empty <- function() {
    all(c(TRUE)[c(FALSE)])
  }
  expect_quick_identical(all_array_ctor_len1_masked_empty, list())

  # Length-1 mask constructors (c(TRUE)/c(FALSE)) are hoisted as rank-1 array
  # constructors but should behave like scalars via recycling in R. The lowering
  # must scalarize them so elementwise `.and.`/`.or.` remain conformable.
  any_mask_ctor_len1 <- function(x) {
    declare(type(x = double(NA)))
    pred <- x > 1
    any(pred[c(TRUE)])
  }
  fsub <- r2f(any_mask_ctor_len1)
  expect_false(grepl("\\.and\\. \\(\\[", as.character(fsub)))
  expect_quick_identical(
    any_mask_ctor_len1,
    list(c(-2, -1)),
    list(c(0.2, 2))
  )

  all_mask_ctor_len1_empty <- function(x) {
    declare(type(x = double(NA)))
    pred <- x > 1
    all(pred[c(FALSE)])
  }
  fsub <- r2f(all_mask_ctor_len1_empty)
  expect_false(grepl("\\.not\\. \\(\\[", as.character(fsub)))
  expect_false(grepl("\\.or\\. \\(\\[", as.character(fsub)))
  expect_quick_identical(
    all_mask_ctor_len1_empty,
    list(c(-2, -1)),
    list(c(0.2, 2))
  )

  # Masked subset: preserve empty-selection semantics without pack() temporaries.
  any_masked <- function(x) {
    declare(type(x = double(NA)))
    pred <- x > 1
    any(pred[x > 0])
  }
  fsub <- r2f(any_masked)
  expect_false(grepl("pack\\(", as.character(fsub)))
  expect_match(as.character(fsub), "\\.and\\.")
  expect_match(as.character(fsub), "any\\(")
  expect_quick_identical(
    any_masked,
    list(c(-2, -1)), # empty selection -> any(logical(0)) == FALSE
    list(c(0.2, 0.3)), # selection all FALSE
    list(c(0.5, 2)) # selection contains TRUE
  )

  all_masked <- function(x) {
    declare(type(x = double(NA)))
    pred <- x > 1
    all(pred[x > 0])
  }
  fsub <- r2f(all_masked)
  expect_false(grepl("pack\\(", as.character(fsub)))
  expect_match(as.character(fsub), "\\.not\\.")
  expect_match(as.character(fsub), "\\.or\\.")
  expect_match(as.character(fsub), "all\\(")
  expect_quick_identical(
    all_masked,
    list(c(-2, -1)), # empty selection -> all(logical(0)) == TRUE
    list(c(0.2, 0.3)), # selection all FALSE -> FALSE
    list(c(0.5, 2)) # selection has a FALSE -> FALSE
  )
})


test_that("1x1 subsetting keeps dims and C bridge builds", {
  fn <- function(m) {
    declare(type(m = integer(NA, NA)))
    m[1L, 1L, drop = FALSE]
  }

  fsub <- r2f(fn)
  expect_no_error(make_c_bridge(fsub))
})
