Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: TreeTools
Title: Create, Modify and Analyse Phylogenetic Trees
Version: 2.2.0
Version: 2.2.0.9001
Authors@R: c(
person("Martin R.", 'Smith', role = c("aut", "cre", "cph"),
email = "martin.smith@durham.ac.uk",
Expand Down
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# TreeTools 2.2.0.9001 #

# TreeTools 2.2.0 (2026-03-18) #

## New functionality
Expand Down
65 changes: 37 additions & 28 deletions src/splits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,15 @@ inline void check_16_bit(double x) {
}
}

// Core edge-to-splits computation; assumes validated inputs with n_tip >= 1.
// Edges must be listed in 'strict' postorder, i.e. two-by-two
// [[Rcpp::export]]
Rcpp::RawMatrix cpp_edge_to_splits(const Rcpp::IntegerMatrix& edge,
const Rcpp::IntegerVector& order,
const Rcpp::IntegerVector& nTip) {

// Check input is valid
if (edge.cols() != 2) {
Rcpp::stop("Edge matrix must contain two columns");
}

static Rcpp::RawMatrix edge_to_splits_impl(
const Rcpp::IntegerMatrix& edge,
const Rcpp::IntegerVector& order,
const uintx n_tip
) {
const uintx n_edge = edge.rows();
if (n_edge + 1 >= NOT_TRIVIAL) {
Rcpp::stop("Too many edges in tree for edge_to_splits: " // # nocov
"Contact maintainer for advice"); // # nocov
}

if (nTip[0] < 1) {
if (nTip[0] == 0) {
return RawMatrix(0, 0);
} else {
Rcpp::stop("Tree must contain non-negative number of tips.");
}
}

if (n_edge != static_cast<uintx>(order.length())) {
Rcpp::stop("Length of `order` must equal number of edges");
}

const uintx n_node = n_edge + 1;
const uintx n_tip = nTip[0];
const uintx n_bin = ((n_tip - 1) / BIN_SIZE) + 1;

if (n_edge == n_tip || n_tip < 4) {
Expand Down Expand Up @@ -140,6 +118,37 @@ Rcpp::RawMatrix cpp_edge_to_splits(const Rcpp::IntegerMatrix& edge,
return ret;
}

// [[Rcpp::export]]
Rcpp::RawMatrix cpp_edge_to_splits(const Rcpp::IntegerMatrix& edge,
const Rcpp::IntegerVector& order,
const Rcpp::IntegerVector& nTip) {

// Check input is valid
if (edge.cols() != 2) {
Rcpp::stop("Edge matrix must contain two columns");
}

const uintx n_edge = edge.rows();
if (n_edge + 1 >= NOT_TRIVIAL) {
Rcpp::stop("Too many edges in tree for edge_to_splits: " // # nocov
"Contact maintainer for advice"); // # nocov
}

if (nTip[0] < 1) {
if (nTip[0] == 0) {
return RawMatrix(0, 0);
} else {
Rcpp::stop("Tree must contain non-negative number of tips.");
}
}

if (n_edge != static_cast<uintx>(order.length())) {
Rcpp::stop("Length of `order` must equal number of edges");
}

return edge_to_splits_impl(edge, order, static_cast<uintx>(nTip[0]));
}

// [[Rcpp::export]]
LogicalVector duplicated_splits(const RawMatrix splits,
const LogicalVector fromLast) {
Expand Down
15 changes: 15 additions & 0 deletions tests/testthat/test-Splits.R
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ test_that("as.Splits.multiPhylo()", {

expect_equal(as.Splits(randomTrees[[1]]),
as.Splits(randomTrees)[[1]])

# asSplits = FALSE returns raw matrices
rawResults <- as.Splits(randomTrees, asSplits = FALSE)
for (i in seq_along(randomTrees)) {
expect_equal(rawResults[[i]],
as.Splits(randomTrees[[i]], asSplits = FALSE))
}

# Trees with different tip label order handled correctly
relabelled <- randomTrees
relabelled[[1]] <- RenumberTips(relabelled[[1]], rev(seq_len(11L)))
fallbackResults <- as.Splits(relabelled)
expect_equal(as.Splits(relabelled[[2]],
tipLabels = unique(unlist(TipLabels(relabelled)))),
fallbackResults[[2]])
})

test_that("as.Splits.Splits()", {
Expand Down
Loading