test_that("Integers fit into integer sizes", {
  INT_MAX <- 2147483647L
  expect_equal(as.integer64(INT_MAX), as.integer64("2147483647"))
  expect_gt(.TT_BASE[1], .TT_BASE[2])
})

test_that("as.phylo.numeric()", {
  expect_true(all.equal(as.phylo(0:2, 6, letters[1:6])[[1]],
                        PectinateTree(letters[c(1, 3:6, 2)])))
  expect_true(all.equal(as.phylo(0, tipLabels = letters[1:6]),
                        PectinateTree(letters[c(1, 3:6, 2)])))
  expect_error(as.phylo(0))
  expect_equal(as.phylo(123, nTip = 8),
               as.phylo(123L, nTip = 8))
  expect_equal(as.phylo(123, nTip = 8),
               as.phylo(as.integer64(123), nTip = 8))
  expect_equal(as.phylo(123:124, nTip = 10),
               as.phylo(as.integer64(123:124), nTip = 10))
  expect_equal(as.phylo(as.integer64(10), tipLabels = paste0("t", 1:8)),
               as.phylo(10, 8))
})

test_that("as.TreeNumber() error handling", {
  expect_error(as.phylo(integer64(1), NULL, NULL))
  expect_error(as.TreeNumber(StarTree(5)))
  expect_error(as.TreeNumber(BalancedTree(52)))  # > 51 leaves not supported
})

test_that("as.TreeNumber()", {
  expect_equal(SingleTaxonTree("t1"), as.phylo(integer64(1), 1, NULL))
  expect_equal(c("Phylogenetic tree number 0 of 105 ",
                 " 6 tips: t1 t2 t3 t4 t5 t6"),
               capture.output(print(as.TreeNumber(as.phylo(105, 6)))))
  expect_equal("Phylogenetic tree number 105 of 6332659870762850625 ",
               capture.output(print(as.TreeNumber(as.phylo(105, 19))))[1])
  # 20+ leaves: no warning, decimal string storage
  expect_no_warning(as.TreeNumber(BalancedTree(20)))
  expect_equal("Phylogenetic tree number 105 of 2.216431e+20 ",
               capture.output(print(as.TreeNumber(as.phylo(105, 20))))[1])
  expect_equal(unlist(lapply(1:3, as.integer64)),
               unlist(as.TreeNumber(as.phylo(1:3, 6))))

  tn6 <- as.TreeNumber(as.phylo(1, 6, letters[1:6]))
  expect_equal(as.phylo(1, 6), as.phylo(tn6, tipLabels = NULL))

  tn16 <- as.TreeNumber(as.phylo(1, 16, letters[1:16]))
  expect_equal(as.phylo(1, 16), as.phylo(tn16, tipLabels = NULL))
  expect_equal(as.TreeNumber(as.MixedBase(tn16)), as.TreeNumber(tn16))

  expect_error(as.phylo(as.integer64(2) ^ 62, 16), "too large ")
  bigNumber <- as.integer64(2) ^ 61 + 1
  # Not necessarily equal to as.integer64(2 ^ 61 + 1), due to numeric conversion
  tn16 <- as.TreeNumber(as.phylo(bigNumber, 16, letters[1:16]))
  expect_equal(as.phylo(bigNumber, 16), as.phylo(tn16, tipLabels = NULL))
  # Rounding:
  expect_identical(as.integer64(as.TreeNumber(as.phylo(bigNumber, 16))) - 1,
                   as.integer64(as.TreeNumber(as.phylo(bigNumber - 1, 16))))
               
  expect_equal(as.TreeNumber(as.MixedBase(tn16)), tn16)

  # Large trees: character-backed, round-trips correctly.
  # as.phylo.TreeNumber returns Preorder(RootTree(x, 1)), so compare against that.
  for (nTip in c(20L, 35L, 51L)) {
    tree <- BalancedTree(nTip)
    tn <- as.TreeNumber(tree)
    expect_s3_class(tn, "TreeNumber")
    expect_true(inherits(tn, "character"))
    expect_false(inherits(tn, "integer64"))
    expect_equal(Preorder(RootTree(tree, 1)), as.phylo(tn, tipLabels = NULL))
  }
})

test_that("as.MixedBase()", {
  expect_error(as.MixedBase(RandomTree(100000)),
               "Too many \\w+ for mixed base")
  expect_equal(as.integer(as.TreeNumber(as.phylo(16, 16))), 16)
  nTip <- 9
  expect_equal(as.integer(as.MixedBase(as.TreeNumber(as.phylo(16, nTip)))),
               c(rep(0, nTip - 3 - 3), 1, 0, 1))
  
  expect_equal(as.MixedBase(as.phylo(0, 6)),
               structure(integer(3),
                         nTip = 6,
                         tip.label = paste0("t", 1:6),
                         binary = TRUE,
                         class = "MixedBase"))
  
  expect_equal(as.MixedBase(as.phylo(105, 20)),
               structure(tabulate(17 - 3, 17),
                         nTip = 20,
                         tip.label = paste0("t", 1:20),
                         binary = TRUE,
                         class = "MixedBase"))
  
  expect_equal(unlist(lapply(1:3, function(n) as.MixedBase(as.phylo(n, 6)))),
               unlist(as.MixedBase(as.phylo(1:3, 6))))
  
  mb3 <- as.MixedBase(as.phylo(1, 3, letters[1:3]))
  expect_equal(as.phylo(1, 3), as.phylo(mb3, tipLabels = NULL))
  
  mb6 <- as.MixedBase(as.phylo(1, 6, letters[1:6]))
  expect_equal(as.phylo(1, 6), as.phylo(mb6, tipLabels = NULL))
  
  mb16 <- as.TreeNumber(as.phylo(1337, 16, letters[1:16]))
  expect_equal(as.phylo(1337, 16), as.phylo(mb16, tipLabels = NULL))
  
  expect_error(as.MixedBase(42, TipLabels(2)))
  expect_true(as.MixedBase(as.phylo(1337, 16)) == as.MixedBase(1337, 16))
  expect_true(as.MixedBase(44) > as.MixedBase(as.MixedBase(42)))
  expect_true(as.MixedBase(44, 10) < as.MixedBase(42, 11))
  bigNo <- as.integer64(2) ^ 62 + 1337
  expect_equal(sum(as.integer(as.MixedBase(bigNo)) * .TT_BASE), bigNo)
})

test_that("as.MixedBase() supports larger trees", {
  expect_equal(tail(as.MixedBase(BalancedTree(100))),
               c(6, 10, 8, 3, 4, 1) # From observation, not calculation
  )
})

test_that("is.TreeNumber()", {
  expect_equal(is.TreeNumber(as.integer64(5)), FALSE)
  expect_equal(is.TreeNumber(as.TreeNumber(BalancedTree(5))), TRUE)
})

test_that("tree_number.h add_small carry propagation", {
  # The tree number 2^64 + 1 = "18446744073709551617" has packed chunks [4, 8, 5].
  # Reconstructing it via packed_to_tree_num() calls add_small(5) after mul_small()
  # leaves w[0] = 2^64 - 4, so w[0] + 5 overflows, triggering the carry-propagation
  # loop (lines 86-88 of inst/include/TreeTools/tree_number.h).
  # NUnrooted(20) > 2^64, so this is a valid 20-leaf tree number.
  tn <- as.TreeNumber("18446744073709551617", nTip = 20L)
  expect_true(inherits(tn, "character"))
  expect_equal(tn, as.TreeNumber(as.phylo(tn, tipLabels = NULL)))
})

test_that(".decimal_to_chunks() handles zero and empty input", {
  # Calling as.phylo() on a character-backed TreeNumber of "0" routes through
  # .decimal_to_chunks("0"), hitting the early-return branch.
  tn_zero <- as.TreeNumber("0", nTip = 20L)
  expect_equal(as.phylo(tn_zero, tipLabels = NULL),
               as.phylo(0L, nTip = 20L))

  # Direct internal check: both "0" and "" return 0L immediately.
  expect_equal(.decimal_to_chunks("0"), 0L)
  expect_equal(.decimal_to_chunks(""), 0L)
  expect_equal(.decimal_to_chunks(character()), 0L)

  # The else-branch is always taken when input is non-zero;
  # the `0L` branch is unreachable (while loop always appends >= 1 element).
  expect_equal(.decimal_to_chunks("1"), 1L)
})

test_that("as.TreeNumber.character() creates character-backed result for nTip > .TT_MAX_TIP", {
  tn <- as.TreeNumber("12345678901234", nTip = 25L)
  expect_s3_class(tn, "TreeNumber")
  expect_true(inherits(tn, "character"))
  expect_false(inherits(tn, "integer64"))
  expect_equal(attr(tn, "nTip"), 25L)
})

test_that("as.TreeNumber.MixedBase() errors for trees with > .TT_MAX_TIP leaves", {
  mb <- as.MixedBase(BalancedTree(20))
  expect_error(as.TreeNumber(mb), "MixedBase.*TreeNumber")
  tn <- as.TreeNumber(BalancedTree(20))
  expect_error(as.MixedBase(tn), "TreeNumber.*MixedBase")
})

test_that("as.integer64.TreeNumber() for character-backed TreeNumbers", {
  # converting a large (nTip > 19) character-backed TreeNumber
  # to integer64 is impossible; should error.
  tn_large <- as.TreeNumber(BalancedTree(20))
  expect_error(as.integer64(tn_large), "integer64")

  # a character-backed TreeNumber with nTip <= 19 (atypical but valid
  # as a manually-constructed object) can be converted exactly to integer64.
  tn_small_char <- structure("10", nTip = 6L, tip.label = paste0("t", 1:6),
                             class = c("TreeNumber", "character"))
  expect_equal(as.integer64(tn_small_char), as.integer64(10))
})
