# These tests were previously kept as tests under \examples{\dontshow{...}}.
#   Converted to "proper" unit tests for clarity, after making them more
#   canonical within {testthat}, e.g. better capturing expected warnings,
#   changing stopifnot(identical(...)) to expect_identical(...).

test_that("identical.integer64", {
  i64 = NA_real_
  class(i64) = "integer64"
  expect_identical(unclass(i64 - 1.0), unclass(i64 + 1.0))
  expect_identical(i64 - 1.0, i64 + 1.0)
  expect_false(identical.integer64(i64 - 1.0, i64 + 1.0))
})

test_that("dispatch of 'c' method", {
  expect_true(identical.integer64(c(integer64(0L), NA), as.integer64(NA)))
})

test_that("Dispatch on the second argument fails and we want to be notified once that changes", {
  expect_false(identical.integer64(c(NA, integer64(0L)), as.integer64(NA)))
})

test_that("'range.integer64', multiplication, integer division, sqrt, power, and log", {
  i64 = integer64(63L)
  i64[1L] = 1.0
  for (i in 2:63)
    i64[i] = 2.0 * i64[i-1L]
  expect_true(identical.integer64(i64 * rev(i64), rep(i64[63L], 63L)))
  for (i in 63:2)
    i64[i-1L] = i64[i] %/% 2.0
  expect_true(identical.integer64(i64 * rev(i64), rep(i64[63L], 63L)))
  for (i in 63:2)
    i64[i-1L] = i64[i] / 2.0
  expect_true(identical.integer64(i64 * rev(i64), rep(i64[63L], 63L)))
  expect_true(identical.integer64(
    c(
      -i64[63L] - (i64[63L] - 1.0),
      i64[63L] + (i64[63L] - 1.0)
    ),
    lim.integer64()
  ))

  expect_true(identical.integer64(i64[-1L] %/% 2.0 * as.integer64(2L), i64[-1L]))
  expect_true(identical.integer64(i64[-1L] %/% 2L * as.integer64(2L), i64[-1L]))
  expect_true(identical.integer64(i64[-1L] / 2.0 * as.integer64(2L), i64[-1L]))
  expect_true(identical.integer64(i64[-1L] / 2L * as.integer64(2L), i64[-1L]))

  expect_true(identical.integer64(i64[-63L] * 2.0 %/% 2.0, i64[-63L]))
  expect_true(identical.integer64(i64[-63L] * 2L %/% 2L, i64[-63L]))
  expect_true(identical.integer64(as.integer64(i64[-63L] * 2.0 / 2.0), i64[-63L]))
  expect_true(identical.integer64(as.integer64(i64[-63L] * 2L / 2L), i64[-63L]))

  expect_true(identical.integer64(
    as.integer64(sqrt(
      i64[-1L][c(FALSE, TRUE)]) * sqrt(i64[-1L][c(FALSE, TRUE)]
    )),
    i64[-1L][c(FALSE, TRUE)]
  ))

  expect_true(identical.integer64(as.integer64(2L) ^ (0:62), i64))
  expect_true(identical.integer64(as.integer64(0:62), as.integer64(round(log2(i64)))))
  expect_true(identical.integer64(
    as.integer64(round(log(as.integer64(2L)^(0:62), 2.0))),
    as.integer64(0:62)
  ))
  expect_true(identical.integer64(
    as.integer64(round(log(as.integer64(3L)^(0:39), 3.0))),
    as.integer64(0:39)
  ))
  expect_true(identical.integer64(
    as.integer64(round(log(as.integer64(10L)^(0:18), 10.0))),
    as.integer64(0:18)
  ))
  expect_true(identical.integer64(
    as.integer64(round(log10(as.integer64(10L)^(0:18)))),
    as.integer64(0:18)
  ))

  expect_true(identical.integer64(
    (as.integer64(2L)^(1:62))^(1.0/1:62),
    as.integer64(rep(2.0, 62L))
  ))
  expect_true(identical.integer64(
    (as.integer64(3L)^(1:39))^(1.0/1:39),
    as.integer64(rep(3.0, 39L))
  ))
  expect_true(identical.integer64(
    (as.integer64(10L)^(1:18))^(1.0/1:18),
    as.integer64(rep(10.0, 18L))
  ))
})

test_that("c and rep", {
  expect_true(identical.integer64(
    as.integer64(rep(1:3, 1:3)),
    rep(as.integer64(1:3), 1:3)
  ))
  expect_true(identical.integer64(
    as.integer64(rep(1:3, 3L)),
    rep(as.integer64(1:3), 3L)
  ))

  x = rep(NA_real_, 3L)
  class(x) = "integer64"
  x = x + -1:1
  expect_true(identical.integer64(rep(x, 3L), c(x, x, x)))
  expect_true(identical.integer64(
    c.integer64(list(x, x, x), recursive=TRUE),
    c(x, x, x)
  ))
})

test_that("seq", {
  expect_true(identical.integer64(
    seq(as.integer64(1L), 10.0, 2.0),
    as.integer64(seq(1.0, 10.0, 2.0))
  ))
  expect_true(identical.integer64(
    seq(as.integer64(1L), by=2.0, length.out=5.0),
    as.integer64(seq(1.0, by=2.0, length.out=5.0))
  ))
  expect_true(identical.integer64(
    seq(as.integer64(1L), by=2.0, length.out=6.0),
    as.integer64(seq(1.0, by=2.0, length.out=6.0))
  ))
  expect_true(identical.integer64(
    seq.integer64(along.with=3:5),
    as.integer64(seq(along.with=3:5))
  ))
  expect_true(identical.integer64(
    seq(as.integer64(1L), to=-9.0),
    as.integer64(seq(1.0, to=-9.0))
  ))
})

test_that("cbind and rbind", {
  x = rep(as.integer64(1:3), 2L)
  dim(x) = c(3L, 2L)
  expect_true(identical.integer64(cbind(as.integer64(1:3), 1:3), x))
  expect_true(identical.integer64(rbind(as.integer64(1:3), 1:3), t(x)))
})

test_that("Coercion from integer64 works as intended", {
  expect_identical(
    as.double(as.integer64(c(NA, seq(0.0, 9.0, 0.25)))),
    as.double(as.integer(c(NA, seq(0.0, 9.0, 0.25))))
  )
  expect_identical(
    as.character(as.integer64(c(NA, seq(0.0, 9.0, 0.25)))),
    as.character(as.integer(c(NA, seq(0.0, 9.0, 0.25))))
  )
  expect_identical(
    as.integer(as.integer64(c(NA, seq(0.0, 9.0, 0.25)))),
    as.integer(c(NA, seq(0.0, 9.0, 0.25)))
  )
  expect_identical(
    as.logical(as.integer64(c(NA, seq(0.0, 9.0, 0.25)))),
    as.logical(as.integer(c(NA, seq(0.0, 9.0, 0.25))))
  )
  expect_identical(
    as.integer(as.integer64(c(NA, FALSE, TRUE))),
    as.integer(c(NA, FALSE, TRUE))
  )
})

test_that("Coercion to integer64 with as.integer64 works as intended", {
  expect_identical(
    as.integer64(as.integer(as.integer64(-9:9))),
    as.integer64(-9:9)
  )
  expect_identical(
    as.integer64(as.double(as.integer64(-9:9))),
    as.integer64(-9:9)
  )
  expect_identical(
    as.integer64(as.complex(-9:9)),
    as.integer64(-9:9)
  )
  expect_identical(
    as.integer64(as.raw(0:9)),
    as.integer64(0:9)
  )
  expect_identical(
    as.integer64(as.character(as.integer64(-9:9))),
    as.integer64(-9:9)
  )
  expect_identical(
    as.integer64(as.character(lim.integer64())),
    lim.integer64()
  )
})

test_that("Coercion to integer64 with as() works as intended", {
  expect_identical(
    as(as.raw(1L), "integer64"),
    as.integer64(1L)
  )
  expect_identical(
    as(TRUE, "integer64"),
    as.integer64(1L)
  )
  expect_identical(
    as(111L, "integer64"),
    as.integer64(111L)
  )
  expect_identical(
    as(111.0, "integer64"),
    as.integer64(111L)
  )
  expect_identical(
    as(111.0 + 0.0i, "integer64"),
    as.integer64(111L)
  )
  expect_identical(
    as("111", "integer64"),
    as.integer64(111L)
  )
  expect_identical(
    as(as.factor(111L), "integer64"),
    as.integer64(1L)
  )
  expect_identical(
    as(as.ordered(111L), "integer64"),
    as.integer64(1L)
  )
})

test_that("Coercion from integer64 with as() works as intended", {
  expect_identical(
    as(as.integer64(1L), "logical"),
    TRUE
  )
  expect_identical(
    as(as.integer64(111L), "integer"),
    111L
  )
  expect_identical(
    as(as.integer64(111L), "numeric"),
    111.0
  )
  expect_identical(
    as(as.integer64(111L), "character"),
    "111"
  )
  expect_identical(
    as(as.integer64(111L), "factor"),
    as.factor("111")
  )
  expect_identical(
    as(as.integer64(111L), "ordered"),
    as.ordered("111")
  )
})

test_that("Coercion works for cases requiring recent R", {
  skip_unless_r(">= 4.0.0")

  expect_identical(
    as.complex(as.integer64(c(NA, seq(0.0, 9.0, 0.25)))),
    as.complex(as.integer(c(NA, seq(0.0, 9.0, 0.25))))
  )

  coercion_warning = "out-of-range values treated as 0 in coercion to raw"
  expect_warning({
    raw_from_int64 = as.raw(as.integer64(c(NA, seq(0.0, 1.25, 0.25))))
  }, coercion_warning, fixed=TRUE)
  expect_warning({
    raw_from_int32 = as.raw(as.integer(c(NA, seq(0.0, 1.25, 0.25))))
  }, coercion_warning, fixed=TRUE)

  expect_identical(raw_from_int64, raw_from_int32)

  expect_identical(
    as(as.integer64(1L), "raw"),
    as.raw(1L)
  )

  expect_identical(
    as(as.integer64(111L), "complex"),
    111.0 + 0.0i
  )
})

test_that("Vector functions", {
  xi = c(NA, -1:1)
  xi64 = as.integer64(xi)

  expect_true(identical.integer64(is.na(xi64), is.na(xi)))
  expect_true(identical.integer64(format(xi64), format(xi)))
  expect_true(identical.integer64(abs(xi64), as.integer64(abs(xi))))
  expect_true(identical.integer64(sign(xi64), as.integer64(sign(xi))))
  expect_true(identical.integer64(ceiling(xi64), as.integer64(ceiling(xi))))
  expect_true(identical.integer64(floor(xi64), as.integer64(floor(xi))))
  expect_true(identical.integer64(trunc(xi64), as.integer64(trunc(xi))))
  expect_true(identical.integer64(signif(xi64), xi64))
})

test_that("Summary functions", {
  expect_identical(all(as.integer64(1L)), all(1L))
  expect_identical(all(as.integer64(0L)), all(0L))
  expect_identical(all(NA_integer64_), all(NA_integer_))
  expect_identical(all(NA_integer64_, na.rm=TRUE), all(NA_integer_, na.rm=TRUE))
  expect_identical(all(as.integer64(1L), NA), all(1L, NA))
  expect_identical(all(as.integer64(0L), NA), all(0L, NA))
  expect_identical(all(as.integer64(1L), NA, na.rm=TRUE), all(1L, NA, na.rm=TRUE))
  expect_identical(all(as.integer64(0L), NA, na.rm=TRUE), all(0L, NA, na.rm=TRUE))
  expect_identical(all(as.integer64(c(1L, NA))), all(c(1L, NA_integer_)))
  expect_identical(all(as.integer64(c(0L, NA))), all(c(0L, NA_integer_)))
  expect_identical(all(as.integer64(c(1L, NA)), na.rm=TRUE), all(c(1L, NA_integer_), na.rm=TRUE))
  expect_identical(all(as.integer64(c(0L, NA)), na.rm=TRUE), all(c(0L, NA_integer_), na.rm=TRUE))

  expect_identical(any(as.integer64(1L)), any(1L))
  expect_identical(any(as.integer64(0L)), any(0L))
  expect_identical(any(NA_integer64_), any(NA_integer_))
  expect_identical(any(NA_integer64_, na.rm=TRUE), any(NA_integer_, na.rm=TRUE))
  expect_identical(any(as.integer64(1L), NA), any(1L, NA))
  expect_identical(any(as.integer64(0L), NA), any(0L, NA))
  expect_identical(any(as.integer64(1L), NA, na.rm=TRUE), any(1L, NA, na.rm=TRUE))
  expect_identical(any(as.integer64(0L), NA, na.rm=TRUE), any(0L, NA, na.rm=TRUE))
  expect_identical(any(as.integer64(c(1L, NA))), any(c(1L, NA_integer_)))
  expect_identical(any(as.integer64(c(0L, NA))), any(c(0L, NA_integer_)))
  expect_identical(any(as.integer64(c(1L, NA)), na.rm=TRUE), any(c(1L, NA_integer_), na.rm=TRUE))
  expect_identical(any(as.integer64(c(0L, NA)), na.rm=TRUE), any(c(0L, NA_integer_), na.rm=TRUE))

  xd = c(2.0, 3.0, NA)
  xi64 = as.integer64(xd)

  expect_true(identical.integer64(
    as.integer64(sum(xd)),
    sum(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(xd, na.rm=TRUE)),
    sum(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(xd)),
    sum(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(xd, na.rm=TRUE)),
    sum(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(2.0, 3.0, NA)),
    sum(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(2.0, 3.0, NA, na.rm=TRUE)),
    sum(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(2.0, 3.0, NA)),
    sum(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(sum(2.0, 3.0, NA, na.rm=TRUE)),
    sum(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))

  expect_true(identical.integer64(
    as.integer64(prod(xd)),
    prod(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(xd, na.rm=TRUE)),
    prod(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(xd)),
    prod(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(xd, na.rm=TRUE)),
    prod(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(2.0, 3.0, NA)),
    prod(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(2.0, 3.0, NA, na.rm=TRUE)),
    prod(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(2.0, 3.0, NA)),
    prod(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(prod(2.0, 3.0, NA, na.rm=TRUE)),
    prod(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))

  expect_true(identical.integer64(
    as.integer64(min(xd)),
    min(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(min(xd, na.rm=TRUE)),
    min(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(min(xd)),
    min(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(min(xd, na.rm=TRUE)),
    min(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(min(2.0, 3.0, NA)),
    min(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)),
    min(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(min(2.0, 3.0, NA)),
    min(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(min(2.0, 3.0, NA, na.rm=TRUE)),
    min(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))

  expect_true(identical.integer64(
    as.integer64(max(xd)),
    max(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(max(xd, na.rm=TRUE)),
    max(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(max(xd)),
    max(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(max(xd, na.rm=TRUE)),
    max(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(max(2.0, 3.0, NA)),
    max(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)),
    max(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(max(2.0, 3.0, NA)),
    max(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(max(2.0, 3.0, NA, na.rm=TRUE)),
    max(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))

  expect_true(identical.integer64(
    as.integer64(range(xd)),
    range(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(range(xd, na.rm=TRUE)),
    range(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(range(xd)),
    range(xi64)
  ))
  expect_true(identical.integer64(
    as.integer64(range(xd, na.rm=TRUE)),
    range(xi64, na.rm=TRUE)
  ))
  expect_true(identical.integer64(
    as.integer64(range(2.0, 3.0, NA)),
    range(as.integer64(2L), 3.0, NA)
  ))
  expect_true(identical.integer64(
    as.integer64(range(2.0, 3.0, NA, na.rm=TRUE)),
    range(as.integer64(2L), 3.0, NA, na.rm=TRUE)
  ))
})

test_that("Cumulative functions", {
  xd = c(2.0, 3.0, NA, 1.0, 4.0)
  xi64 = as.integer64(xd)

  expect_true(identical.integer64(cumsum(xi64), as.integer64(cumsum(xd))))
  expect_true(identical.integer64(cumprod(xi64), as.integer64(cumprod(xd))))
  expect_true(identical.integer64(cummin(xi64), as.integer64(cummin(xd))))
  expect_true(identical.integer64(cummax(xi64), as.integer64(cummax(xd))))
})

test_that("diff", {
  d64 = diffinv(rep(.Machine$integer.max, 100L), lag=2.0, differences=2L)
  i64 = as.integer64(d64)
  expect_identical(
    diff(d64, lag=2L, differences=2L),
    as.double(diff(i64, lag=2L, differences=2L))
  )
})
