From e8ee70c190d842e2aa6837d80b3e4964ebec5b6f Mon Sep 17 00:00:00 2001 From: Gregor Gorjanc Date: Tue, 3 Mar 2026 01:39:15 +0000 Subject: [PATCH] Address that tsk_size_t is uint64_t Fixes #108 --- RcppTskit/DESCRIPTION | 1 + RcppTskit/NAMESPACE | 2 + RcppTskit/NEWS.md | 3 + RcppTskit/R/RcppExports.R | 8 + RcppTskit/R/RcppTskit-package.R | 1 + RcppTskit/R/RcppTskit.R | 102 +++--- RcppTskit/inst/include/RcppTskit_public.hpp | 20 +- RcppTskit/src/RcppExports.cpp | 46 ++- RcppTskit/src/RcppTskit.cpp | 297 ++++++++++++++---- .../tests/testthat/test_TableCollection.R | 38 ++- .../testthat/test_load_summary_and_dump.R | 64 ++-- RcppTskit/tests/testthat/test_misc.R | 73 +++++ 12 files changed, 482 insertions(+), 173 deletions(-) diff --git a/RcppTskit/DESCRIPTION b/RcppTskit/DESCRIPTION index 66d3f85..05270d5 100644 --- a/RcppTskit/DESCRIPTION +++ b/RcppTskit/DESCRIPTION @@ -32,6 +32,7 @@ BugReports: https://github.com/HighlanderLab/RcppTskit/issues Depends: R (>= 4.0.0) Imports: + bit64, methods, R6, Rcpp (>= 1.0.8), diff --git a/RcppTskit/NAMESPACE b/RcppTskit/NAMESPACE index 6062784..e7f6666 100644 --- a/RcppTskit/NAMESPACE +++ b/RcppTskit/NAMESPACE @@ -15,6 +15,8 @@ export(tskit_version) importFrom(R6,R6Class) importFrom(Rcpp,cppFunction) importFrom(Rcpp,registerPlugin) +importFrom(bit64,as.integer64) +importFrom(bit64,is.integer64) importFrom(methods,is) importFrom(reticulate,import) importFrom(reticulate,is_py_object) diff --git a/RcppTskit/NEWS.md b/RcppTskit/NEWS.md index bce12ab..de60def 100644 --- a/RcppTskit/NEWS.md +++ b/RcppTskit/NEWS.md @@ -39,6 +39,9 @@ and releases adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html constructor argument from `pointer` to `xptr`. - Ensured `TableCollection$tree_sequence()` matches `tskit Python` API: it now builds indexes on the `TableCollection`, if indexes are not present. +- We now use `bit64::integer64` (signed 64 bit integer) instead of `int` aiming + to approach `tsk_size_t` in `tskit C` (unsigned 64 bit integer); in low-level + `rtsk_treeseq_get_num_*()` wrappers and count/metadata-length fields. - TODO ### Maintenance diff --git a/RcppTskit/R/RcppExports.R b/RcppTskit/R/RcppExports.R index 1b20348..915b68a 100644 --- a/RcppTskit/R/RcppExports.R +++ b/RcppTskit/R/RcppExports.R @@ -1,6 +1,14 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 +test_validate_options <- function(options, supported) { + .Call(`_RcppTskit_test_validate_options`, options, supported) +} + +test_rtsk_wrap_tsk_size_t_as_integer64 <- function(value, force_range_error = FALSE) { + .Call(`_RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64`, value, force_range_error) +} + #' @title Report the version of installed kastore C API #' @details The version is stored in the installed header \code{kastore.h}. #' @return A named vector with three elements \code{major}, \code{minor}, and diff --git a/RcppTskit/R/RcppTskit-package.R b/RcppTskit/R/RcppTskit-package.R index 9ae4696..e6eb768 100644 --- a/RcppTskit/R/RcppTskit-package.R +++ b/RcppTskit/R/RcppTskit-package.R @@ -20,6 +20,7 @@ #' @keywords internal #' #' @useDynLib RcppTskit, .registration = TRUE +#' @importFrom bit64 as.integer64 is.integer64 #' @importFrom methods is #' @importFrom R6 R6Class #' @importFrom Rcpp cppFunction registerPlugin diff --git a/RcppTskit/R/RcppTskit.R b/RcppTskit/R/RcppTskit.R index 5ac9f77..9ade202 100644 --- a/RcppTskit/R/RcppTskit.R +++ b/RcppTskit/R/RcppTskit.R @@ -214,7 +214,9 @@ tc_read <- tc_load # compared to Python API, so also not available here. # @return A list with two data.frames; the first contains tree sequence # properties and their value; the second contains the numbers of rows in -# tables and the length of their metadata. +# tables and the length of their metadata. All columns are character as they +# contain different types of values. Use specific functions if you want to +# obtain non-character values # @seealso \code{\link[=TreeSequence]{TreeSequence$print}} on how this # function is used and presented to users. # @examples @@ -243,17 +245,17 @@ rtsk_treeseq_print <- function(ts) { "file_uuid" ), value = c( - tmp_summary[["num_samples"]], - tmp_summary[["num_trees"]], - tmp_summary[["sequence_length"]], - tmp_summary[["discrete_genome"]], - tmp_summary[["has_reference_sequence"]], - tmp_summary[["time_units"]], - tmp_summary[["discrete_time"]], - tmp_summary[["min_time"]], - tmp_summary[["max_time"]], - tmp_metadata[["ts"]] > 0, - tmp_summary[["file_uuid"]] + as.character(tmp_summary[["num_samples"]]), + as.character(tmp_summary[["num_trees"]]), + as.character(tmp_summary[["sequence_length"]]), + as.character(tmp_summary[["discrete_genome"]]), + as.character(tmp_summary[["has_reference_sequence"]]), + as.character(tmp_summary[["time_units"]]), + as.character(tmp_summary[["discrete_time"]]), + as.character(tmp_summary[["min_time"]]), + as.character(tmp_summary[["max_time"]]), + as.character(tmp_metadata[["ts"]] > 0), + as.character(tmp_summary[["file_uuid"]]) ) ), tables = data.frame( @@ -268,24 +270,24 @@ rtsk_treeseq_print <- function(ts) { "mutations" ), number = c( - tmp_summary[["num_provenances"]], - tmp_summary[["num_populations"]], - tmp_summary[["num_migrations"]], - tmp_summary[["num_individuals"]], - tmp_summary[["num_nodes"]], - tmp_summary[["num_edges"]], - tmp_summary[["num_sites"]], - tmp_summary[["num_mutations"]] + as.character(tmp_summary[["num_provenances"]]), + as.character(tmp_summary[["num_populations"]]), + as.character(tmp_summary[["num_migrations"]]), + as.character(tmp_summary[["num_individuals"]]), + as.character(tmp_summary[["num_nodes"]]), + as.character(tmp_summary[["num_edges"]]), + as.character(tmp_summary[["num_sites"]]), + as.character(tmp_summary[["num_mutations"]]) ), has_metadata = c( NA, # provenances have no metadata - tmp_metadata[["populations"]] > 0, - tmp_metadata[["migrations"]] > 0, - tmp_metadata[["individuals"]] > 0, - tmp_metadata[["nodes"]] > 0, - tmp_metadata[["edges"]] > 0, - tmp_metadata[["sites"]] > 0, - tmp_metadata[["mutations"]] > 0 + as.character(tmp_metadata[["populations"]] > 0), + as.character(tmp_metadata[["migrations"]] > 0), + as.character(tmp_metadata[["individuals"]] > 0), + as.character(tmp_metadata[["nodes"]] > 0), + as.character(tmp_metadata[["edges"]] > 0), + as.character(tmp_metadata[["sites"]] > 0), + as.character(tmp_metadata[["mutations"]] > 0) ) ) ) @@ -299,7 +301,9 @@ rtsk_treeseq_print <- function(ts) { # \code{\link{rtsk_table_collection_metadata_length}}. # @return A list with two data.frames; the first contains table collection # properties and their value; the second contains the numbers of rows in -# tables and the length of their metadata. +# tables and the length of their metadata. All columns are character as they +# contain different types of values. Use specific functions if you want to +# obtain non-character values # @seealso \code{\link[=TableCollection]{TableCollection$print}} on how this # function is used and presented to users. # @examples @@ -323,12 +327,12 @@ rtsk_table_collection_print <- function(tc) { "has_index" ), value = c( - tmp_summary[["sequence_length"]], - tmp_summary[["has_reference_sequence"]], - tmp_summary[["time_units"]], - tmp_metadata[["tc"]] > 0, - tmp_summary[["file_uuid"]], - tmp_summary[["has_index"]] + as.character(tmp_summary[["sequence_length"]]), + as.character(tmp_summary[["has_reference_sequence"]]), + as.character(tmp_summary[["time_units"]]), + as.character(tmp_metadata[["tc"]] > 0), + as.character(tmp_summary[["file_uuid"]]), + as.character(tmp_summary[["has_index"]]) ) ), tables = data.frame( @@ -343,24 +347,24 @@ rtsk_table_collection_print <- function(tc) { "mutations" ), number = c( - tmp_summary[["num_provenances"]], - tmp_summary[["num_populations"]], - tmp_summary[["num_migrations"]], - tmp_summary[["num_individuals"]], - tmp_summary[["num_nodes"]], - tmp_summary[["num_edges"]], - tmp_summary[["num_sites"]], - tmp_summary[["num_mutations"]] + as.character(tmp_summary[["num_provenances"]]), + as.character(tmp_summary[["num_populations"]]), + as.character(tmp_summary[["num_migrations"]]), + as.character(tmp_summary[["num_individuals"]]), + as.character(tmp_summary[["num_nodes"]]), + as.character(tmp_summary[["num_edges"]]), + as.character(tmp_summary[["num_sites"]]), + as.character(tmp_summary[["num_mutations"]]) ), has_metadata = c( NA, # provenances have no metadata - tmp_metadata[["populations"]] > 0, - tmp_metadata[["migrations"]] > 0, - tmp_metadata[["individuals"]] > 0, - tmp_metadata[["nodes"]] > 0, - tmp_metadata[["edges"]] > 0, - tmp_metadata[["sites"]] > 0, - tmp_metadata[["mutations"]] > 0 + as.character(tmp_metadata[["populations"]] > 0), + as.character(tmp_metadata[["migrations"]] > 0), + as.character(tmp_metadata[["individuals"]] > 0), + as.character(tmp_metadata[["nodes"]] > 0), + as.character(tmp_metadata[["edges"]] > 0), + as.character(tmp_metadata[["sites"]] > 0), + as.character(tmp_metadata[["mutations"]] > 0) ) ) ) diff --git a/RcppTskit/inst/include/RcppTskit_public.hpp b/RcppTskit/inst/include/RcppTskit_public.hpp index b70abd5..696d0d0 100644 --- a/RcppTskit/inst/include/RcppTskit_public.hpp +++ b/RcppTskit/inst/include/RcppTskit_public.hpp @@ -19,16 +19,16 @@ void rtsk_table_collection_dump(SEXP tc, std::string &filename, SEXP rtsk_treeseq_copy_tables(SEXP ts, int options = 0); SEXP rtsk_treeseq_init(SEXP tc, int options = 0); -int rtsk_treeseq_get_num_provenances(SEXP ts); -int rtsk_treeseq_get_num_populations(SEXP ts); -int rtsk_treeseq_get_num_migrations(SEXP ts); -int rtsk_treeseq_get_num_individuals(SEXP ts); -int rtsk_treeseq_get_num_samples(SEXP ts); -int rtsk_treeseq_get_num_nodes(SEXP ts); -int rtsk_treeseq_get_num_edges(SEXP ts); -int rtsk_treeseq_get_num_trees(SEXP ts); -int rtsk_treeseq_get_num_sites(SEXP ts); -int rtsk_treeseq_get_num_mutations(SEXP ts); +SEXP rtsk_treeseq_get_num_provenances(SEXP ts); +SEXP rtsk_treeseq_get_num_populations(SEXP ts); +SEXP rtsk_treeseq_get_num_migrations(SEXP ts); +SEXP rtsk_treeseq_get_num_individuals(SEXP ts); +SEXP rtsk_treeseq_get_num_samples(SEXP ts); +SEXP rtsk_treeseq_get_num_nodes(SEXP ts); +SEXP rtsk_treeseq_get_num_edges(SEXP ts); +SEXP rtsk_treeseq_get_num_trees(SEXP ts); +SEXP rtsk_treeseq_get_num_sites(SEXP ts); +SEXP rtsk_treeseq_get_num_mutations(SEXP ts); double rtsk_treeseq_get_sequence_length(SEXP ts); bool rtsk_treeseq_get_discrete_genome(SEXP ts); bool rtsk_treeseq_has_reference_sequence(SEXP ts); diff --git a/RcppTskit/src/RcppExports.cpp b/RcppTskit/src/RcppExports.cpp index 0ef941f..a8be147 100644 --- a/RcppTskit/src/RcppExports.cpp +++ b/RcppTskit/src/RcppExports.cpp @@ -10,6 +10,30 @@ Rcpp::Rostream& Rcpp::Rcout = Rcpp::Rcpp_cout_get(); Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif +// test_validate_options +int test_validate_options(const int options, const int supported); +RcppExport SEXP _RcppTskit_test_validate_options(SEXP optionsSEXP, SEXP supportedSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const int >::type options(optionsSEXP); + Rcpp::traits::input_parameter< const int >::type supported(supportedSEXP); + rcpp_result_gen = Rcpp::wrap(test_validate_options(options, supported)); + return rcpp_result_gen; +END_RCPP +} +// test_rtsk_wrap_tsk_size_t_as_integer64 +SEXP test_rtsk_wrap_tsk_size_t_as_integer64(const std::string value, const bool force_range_error); +RcppExport SEXP _RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64(SEXP valueSEXP, SEXP force_range_errorSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< const std::string >::type value(valueSEXP); + Rcpp::traits::input_parameter< const bool >::type force_range_error(force_range_errorSEXP); + rcpp_result_gen = Rcpp::wrap(test_rtsk_wrap_tsk_size_t_as_integer64(value, force_range_error)); + return rcpp_result_gen; +END_RCPP +} // kastore_version Rcpp::IntegerVector kastore_version(); RcppExport SEXP _RcppTskit_kastore_version() { @@ -103,7 +127,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_provenances -int rtsk_treeseq_get_num_provenances(const SEXP ts); +SEXP rtsk_treeseq_get_num_provenances(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_provenances(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -114,7 +138,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_populations -int rtsk_treeseq_get_num_populations(const SEXP ts); +SEXP rtsk_treeseq_get_num_populations(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_populations(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -125,7 +149,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_migrations -int rtsk_treeseq_get_num_migrations(const SEXP ts); +SEXP rtsk_treeseq_get_num_migrations(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_migrations(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -136,7 +160,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_individuals -int rtsk_treeseq_get_num_individuals(const SEXP ts); +SEXP rtsk_treeseq_get_num_individuals(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_individuals(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -147,7 +171,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_samples -int rtsk_treeseq_get_num_samples(const SEXP ts); +SEXP rtsk_treeseq_get_num_samples(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_samples(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -158,7 +182,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_nodes -int rtsk_treeseq_get_num_nodes(const SEXP ts); +SEXP rtsk_treeseq_get_num_nodes(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_nodes(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -169,7 +193,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_edges -int rtsk_treeseq_get_num_edges(const SEXP ts); +SEXP rtsk_treeseq_get_num_edges(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_edges(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -180,7 +204,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_trees -int rtsk_treeseq_get_num_trees(const SEXP ts); +SEXP rtsk_treeseq_get_num_trees(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_trees(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -191,7 +215,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_sites -int rtsk_treeseq_get_num_sites(const SEXP ts); +SEXP rtsk_treeseq_get_num_sites(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_sites(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -202,7 +226,7 @@ BEGIN_RCPP END_RCPP } // rtsk_treeseq_get_num_mutations -int rtsk_treeseq_get_num_mutations(const SEXP ts); +SEXP rtsk_treeseq_get_num_mutations(const SEXP ts); RcppExport SEXP _RcppTskit_rtsk_treeseq_get_num_mutations(SEXP tsSEXP) { BEGIN_RCPP Rcpp::RObject rcpp_result_gen; @@ -502,6 +526,8 @@ END_RCPP } static const R_CallMethodDef CallEntries[] = { + {"_RcppTskit_test_validate_options", (DL_FUNC) &_RcppTskit_test_validate_options, 2}, + {"_RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64", (DL_FUNC) &_RcppTskit_test_rtsk_wrap_tsk_size_t_as_integer64, 2}, {"_RcppTskit_kastore_version", (DL_FUNC) &_RcppTskit_kastore_version, 0}, {"_RcppTskit_tskit_version", (DL_FUNC) &_RcppTskit_tskit_version, 0}, {"_RcppTskit_rtsk_treeseq_load", (DL_FUNC) &_RcppTskit_rtsk_treeseq_load, 2}, diff --git a/RcppTskit/src/RcppTskit.cpp b/RcppTskit/src/RcppTskit.cpp index 6330bef..eb6feb4 100644 --- a/RcppTskit/src/RcppTskit.cpp +++ b/RcppTskit/src/RcppTskit.cpp @@ -4,6 +4,9 @@ // they are synced! #define RCPPTSKIT_IMPL #include +#include +#include +#include namespace { // namespace to keep the contents local to this file @@ -25,6 +28,7 @@ constexpr tsk_flags_t kTreeseqInitSupportedFlags = // The \code{rtsk_treeseq_load} and \code{rtsk_table_collection_load} allocate // objects, so we can't work with \code{TSK_NO_INIT} // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.TSK_NO_INIT}. +// @return Validated flags as bitwise options. tsk_flags_t validate_load_options(const int options, const char *caller) { if (options < 0) { Rcpp::stop("%s does not support negative options", caller); @@ -52,6 +56,7 @@ tsk_flags_t validate_load_options(const int options, const char *caller) { // The \code{rtsk_treeseq_copy_tables} allocates table collection, so // we can't work with \code{TSK_NO_INIT} // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.TSK_NO_INIT}. +// @return Validated flags as bitwise options. tsk_flags_t validate_copy_tables_options(const int options, const char *caller) { if (options < 0) { @@ -83,6 +88,7 @@ tsk_flags_t validate_copy_tables_options(const int options, // \code{malloc()/free()}, while this wrapper manages that outer struct with // C++ \code{new()/delete()}. // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.TSK_TAKE_OWNERSHIP}. +// @return Validated flags as bitwise options. tsk_flags_t validate_treeseq_init_options(const int options, const char *caller) { if (options < 0) { @@ -112,6 +118,7 @@ tsk_flags_t validate_treeseq_init_options(const int options, // and // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.tsk_table_collection_dump}, // which currently expects \code{0}. +// @return Validated flags as bitwise options. tsk_flags_t validate_options(const int options, const tsk_flags_t supported, const char *caller) { if (options < 0) { @@ -130,8 +137,78 @@ tsk_flags_t validate_options(const int options, const tsk_flags_t supported, return flags; } +constexpr tsk_size_t kMaxBit64Integer64 = + static_cast(std::numeric_limits::max()); + +// INTERNAL +// @title Wrap \code{C tsk_size_t / uint64_t} to \code{R bit64::integer64} +// @param value \code{C tsk_size_t / uint64_t} value +// @param caller function name +// @details See +// \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.tsk_size_t}, +// an unsigned 64 bit integer with range from 0 to 2^64 - 1 +// (that is, 0 to 18,446,744,073,709,551,615). +// On the other hand, the R bit64::integer64 is +// a signed 64 bit integer with range from -2^63 to 2^63 - 1 +// (that is, -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807) +// @return \code{R bit64::integer64} object. +SEXP rtsk_wrap_tsk_size_t_as_integer64(const tsk_size_t value, + const char *caller) { + const std::string value_str = + std::to_string(static_cast(value)); + if (value > kMaxBit64Integer64) { + const std::string max_str = + std::to_string(static_cast(kMaxBit64Integer64)); + Rcpp::stop("%s returned tsk_size_t value %s, which exceeds " + "bit64::integer64 maximum %s", + caller, value_str.c_str(), max_str.c_str()); + } + static Rcpp::Function as_integer64 = + Rcpp::Environment::namespace_env("bit64")["as.integer64"]; + return as_integer64(value_str); +} + } // namespace +// TEST-ONLY +// @title Test helper for validating tskit flags +// @param options integer options +// @param supported integer bitmask for supported options +// @return Validated flags as integer. +// [[Rcpp::export]] +int test_validate_options(const int options, const int supported) { + const tsk_flags_t out = validate_options( + options, static_cast(supported), "test_validate_options"); + return static_cast(out); +} + +// TEST-ONLY +// @title Test helper for integer64 wrapping of \code{tsk_size_t} +// @param value character representation of unsigned integer +// @param force_range_error logical flag for testing range-check branch +// @return \code{R bit64::integer64} object. +// [[Rcpp::export]] +SEXP test_rtsk_wrap_tsk_size_t_as_integer64( + const std::string value, const bool force_range_error = false) { + unsigned long long parsed = 0; + std::size_t parsed_chars = 0; + try { + parsed = std::stoull(value, &parsed_chars, 10); + } catch (const std::exception &) { + Rcpp::stop("value must be a valid base-10 unsigned integer string"); + } + if (parsed_chars != value.size()) { + Rcpp::stop("value must be a valid base-10 unsigned integer string"); + } + const tsk_size_t tsk_value = static_cast(parsed); + if (force_range_error || + static_cast(tsk_value) != parsed) { + Rcpp::stop("value is out of range for tsk_size_t"); + } + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_value, "test_rtsk_wrap_tsk_size_t_as_integer64"); +} + // PUBLIC //' @title Report the version of installed kastore C API //' @details The version is stored in the installed header \code{kastore.h}. @@ -421,36 +498,44 @@ SEXP rtsk_treeseq_init(const SEXP tc, const int options = 0) { // @describeIn rtsk_treeseq_summary Get the number of provenances in a tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_provenances(const SEXP ts) { +SEXP rtsk_treeseq_get_num_provenances(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_provenances(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_provenances(ts_xptr), + "rtsk_treeseq_get_num_provenances"); } // PUBLIC, wrapper for tsk_treeseq_get_num_populations // @describeIn rtsk_treeseq_summary Get the number of populations in a tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_populations(const SEXP ts) { +SEXP rtsk_treeseq_get_num_populations(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_populations(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_populations(ts_xptr), + "rtsk_treeseq_get_num_populations"); } // PUBLIC, wrapper for tsk_treeseq_get_num_migrations // @describeIn rtsk_treeseq_summary Get the number of migrations in a tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_migrations(const SEXP ts) { +SEXP rtsk_treeseq_get_num_migrations(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_migrations(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_migrations(ts_xptr), + "rtsk_treeseq_get_num_migrations"); } // PUBLIC, wrapper for tsk_treeseq_get_num_individuals // @describeIn rtsk_treeseq_summary Get the number of individuals in a tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_individuals(const SEXP ts) { +SEXP rtsk_treeseq_get_num_individuals(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_individuals(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_individuals(ts_xptr), + "rtsk_treeseq_get_num_individuals"); } // PUBLIC, wrapper for tsk_treeseq_get_num_samples @@ -458,50 +543,56 @@ int rtsk_treeseq_get_num_individuals(const SEXP ts) { // tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_samples(const SEXP ts) { +SEXP rtsk_treeseq_get_num_samples(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_samples(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64(tsk_treeseq_get_num_samples(ts_xptr), + "rtsk_treeseq_get_num_samples"); } // PUBLIC, wrapper for tsk_treeseq_get_num_nodes // @describeIn rtsk_treeseq_summary Get the number of nodes in a tree sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_nodes(const SEXP ts) { +SEXP rtsk_treeseq_get_num_nodes(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_nodes(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64(tsk_treeseq_get_num_nodes(ts_xptr), + "rtsk_treeseq_get_num_nodes"); } // PUBLIC, wrapper for tsk_treeseq_get_num_edges // @describeIn rtsk_treeseq_summary Get the number of edges in a tree sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_edges(const SEXP ts) { +SEXP rtsk_treeseq_get_num_edges(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_edges(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64(tsk_treeseq_get_num_edges(ts_xptr), + "rtsk_treeseq_get_num_edges"); } // PUBLIC, wrapper for tsk_treeseq_get_num_trees // @describeIn rtsk_treeseq_summary Get the number of trees in a tree sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_trees(const SEXP ts) { +SEXP rtsk_treeseq_get_num_trees(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_trees(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64(tsk_treeseq_get_num_trees(ts_xptr), + "rtsk_treeseq_get_num_trees"); } // PUBLIC, wrapper for tsk_treeseq_get_num_sites // @describeIn rtsk_treeseq_summary Get the number of sites in a tree sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_sites(const SEXP ts) { +SEXP rtsk_treeseq_get_num_sites(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_sites(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64(tsk_treeseq_get_num_sites(ts_xptr), + "rtsk_treeseq_get_num_sites"); } // PUBLIC, wrapper for tsk_treeseq_get_num_mutations // @describeIn rtsk_treeseq_summary Get the number of mutations in a tree // sequence // [[Rcpp::export]] -int rtsk_treeseq_get_num_mutations(const SEXP ts) { +SEXP rtsk_treeseq_get_num_mutations(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); - return static_cast(tsk_treeseq_get_num_mutations(ts_xptr)); + return rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_mutations(ts_xptr), "rtsk_treeseq_get_num_mutations"); } // PUBLIC, wrapper for tsk_treeseq_get_sequence_length @@ -615,7 +706,9 @@ Rcpp::String rtsk_treeseq_get_file_uuid(const SEXP ts) { // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.tsk_treeseq_get_file_uuid}, // @return \code{rtsk_treeseq_summary} returns a named list with numbers and // values, while functions \code{rtsk_treeseq_*} return the number or value -// for each item. +// for each item. Count-like values are returned as \code{R bit64::integer64} +// to approach range in \code{C tsk_size_t / uint64_t} +// (see \code{rtsk_wrap_tsk_size_t_as_integer64} for more details). // @examples // ts_file <- system.file("examples/test.trees", package = "RcppTskit") // ts_xptr <- RcppTskit:::rtsk_treeseq_load(ts_file) @@ -643,16 +736,36 @@ Rcpp::String rtsk_treeseq_get_file_uuid(const SEXP ts) { Rcpp::List rtsk_treeseq_summary(const SEXP ts) { rtsk_treeseq_t ts_xptr(ts); return Rcpp::List::create( - Rcpp::_["num_provenances"] = tsk_treeseq_get_num_provenances(ts_xptr), - Rcpp::_["num_populations"] = tsk_treeseq_get_num_populations(ts_xptr), - Rcpp::_["num_migrations"] = tsk_treeseq_get_num_migrations(ts_xptr), - Rcpp::_["num_individuals"] = tsk_treeseq_get_num_individuals(ts_xptr), - Rcpp::_["num_samples"] = tsk_treeseq_get_num_samples(ts_xptr), - Rcpp::_["num_nodes"] = tsk_treeseq_get_num_nodes(ts_xptr), - Rcpp::_["num_edges"] = tsk_treeseq_get_num_edges(ts_xptr), - Rcpp::_["num_trees"] = tsk_treeseq_get_num_trees(ts_xptr), - Rcpp::_["num_sites"] = tsk_treeseq_get_num_sites(ts_xptr), - Rcpp::_["num_mutations"] = tsk_treeseq_get_num_mutations(ts_xptr), + Rcpp::_["num_provenances"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_provenances(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_provenances"), + Rcpp::_["num_populations"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_populations(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_populations"), + Rcpp::_["num_migrations"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_migrations(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_migrations"), + Rcpp::_["num_individuals"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_individuals(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_individuals"), + Rcpp::_["num_samples"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_samples(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_samples"), + Rcpp::_["num_nodes"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_nodes(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_nodes"), + Rcpp::_["num_edges"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_edges(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_edges"), + Rcpp::_["num_trees"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_trees(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_trees"), + Rcpp::_["num_sites"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_sites(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_sites"), + Rcpp::_["num_mutations"] = rtsk_wrap_tsk_size_t_as_integer64( + tsk_treeseq_get_num_mutations(ts_xptr), + "rtsk_treeseq_summary/tsk_treeseq_get_num_mutations"), Rcpp::_["sequence_length"] = tsk_treeseq_get_sequence_length(ts_xptr), Rcpp::_["discrete_genome"] = tsk_treeseq_get_discrete_genome(ts_xptr), Rcpp::_["has_reference_sequence"] = @@ -673,7 +786,8 @@ Rcpp::List rtsk_treeseq_summary(const SEXP ts) { // \code{ts->tables->metadata_length} and // \code{ts->tables->x->metadata_length} on each table \code{x}, e.g., // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.tsk_population_table_t.metadata_length}. -// @return A named list with the length of metadata. +// @return A named list with the length of metadata as \code{R bit64::integer64} +// values. // @examples // ts_file <- system.file("examples/test.trees", package = "RcppTskit") // ts_xptr <- RcppTskit:::rtsk_treeseq_load(ts_file) @@ -688,18 +802,30 @@ Rcpp::List rtsk_treeseq_metadata_length(const SEXP ts) { // Rcpp::_["ts"] = // static_cast(tsk_treeseq_get_metadata_length(ts_xptr)), // hence we just use that here - Rcpp::_["ts"] = static_cast(tables->metadata_length), - Rcpp::_["populations"] = - static_cast(tables->populations.metadata_length), - Rcpp::_["migrations"] = - static_cast(tables->migrations.metadata_length), - Rcpp::_["individuals"] = - static_cast(tables->individuals.metadata_length), - Rcpp::_["nodes"] = static_cast(tables->nodes.metadata_length), - Rcpp::_["edges"] = static_cast(tables->edges.metadata_length), - Rcpp::_["sites"] = static_cast(tables->sites.metadata_length), - Rcpp::_["mutations"] = - static_cast(tables->mutations.metadata_length)); + Rcpp::_["ts"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->metadata_length, + "rtsk_treeseq_metadata_length/tables->metadata_length"), + Rcpp::_["populations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->populations.metadata_length, + "rtsk_treeseq_metadata_length/tables->populations.metadata_length"), + Rcpp::_["migrations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->migrations.metadata_length, + "rtsk_treeseq_metadata_length/tables->migrations.metadata_length"), + Rcpp::_["individuals"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->individuals.metadata_length, + "rtsk_treeseq_metadata_length/tables->individuals.metadata_length"), + Rcpp::_["nodes"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->nodes.metadata_length, + "rtsk_treeseq_metadata_length/tables->nodes.metadata_length"), + Rcpp::_["edges"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->edges.metadata_length, + "rtsk_treeseq_metadata_length/tables->edges.metadata_length"), + Rcpp::_["sites"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->sites.metadata_length, + "rtsk_treeseq_metadata_length/tables->sites.metadata_length"), + Rcpp::_["mutations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->mutations.metadata_length, + "rtsk_treeseq_metadata_length/tables->mutations.metadata_length")); } // INTERNAL (for now) @@ -905,7 +1031,9 @@ void rtsk_table_collection_drop_index(const SEXP tc, const int options = 0) { // \url{https://tskit.dev/tskit/docs/stable/c-api.html#c.tsk_table_collection_has_index} // @return \code{rtsk_table_collection_summary} returns a named list with // numbers and values, while functions \code{rtsk_table_collection_*} return -// the number or value for each item. +// the number or value for each item. Count-like values are returned as +// \code{R bit64::integer64} to approach range in \code{C tsk_size_t / +// uint64_t} (see \code{rtsk_wrap_tsk_size_t_as_integer64} for more details). // @examples // ts_file <- system.file("examples/test.trees", package = "RcppTskit") // tc_xptr <- RcppTskit:::rtsk_table_collection_load(ts_file) @@ -920,14 +1048,30 @@ Rcpp::List rtsk_table_collection_summary(const SEXP tc) { rtsk_table_collection_t tc_xptr(tc); const tsk_table_collection_t *tables = tc_xptr; return Rcpp::List::create( - Rcpp::_["num_provenances"] = tables->provenances.num_rows, - Rcpp::_["num_populations"] = tables->populations.num_rows, - Rcpp::_["num_migrations"] = tables->migrations.num_rows, - Rcpp::_["num_individuals"] = tables->individuals.num_rows, - Rcpp::_["num_nodes"] = tables->nodes.num_rows, - Rcpp::_["num_edges"] = tables->edges.num_rows, - Rcpp::_["num_sites"] = tables->sites.num_rows, - Rcpp::_["num_mutations"] = tables->mutations.num_rows, + Rcpp::_["num_provenances"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->provenances.num_rows, + "rtsk_table_collection_summary/tables->provenances.num_rows"), + Rcpp::_["num_populations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->populations.num_rows, + "rtsk_table_collection_summary/tables->populations.num_rows"), + Rcpp::_["num_migrations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->migrations.num_rows, + "rtsk_table_collection_summary/tables->migrations.num_rows"), + Rcpp::_["num_individuals"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->individuals.num_rows, + "rtsk_table_collection_summary/tables->individuals.num_rows"), + Rcpp::_["num_nodes"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->nodes.num_rows, + "rtsk_table_collection_summary/tables->nodes.num_rows"), + Rcpp::_["num_edges"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->edges.num_rows, + "rtsk_table_collection_summary/tables->edges.num_rows"), + Rcpp::_["num_sites"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->sites.num_rows, + "rtsk_table_collection_summary/tables->sites.num_rows"), + Rcpp::_["num_mutations"] = rtsk_wrap_tsk_size_t_as_integer64( + tables->mutations.num_rows, + "rtsk_table_collection_summary/tables->mutations.num_rows"), Rcpp::_["sequence_length"] = tables->sequence_length, Rcpp::_["has_reference_sequence"] = rtsk_table_collection_has_reference_sequence(tc), @@ -940,7 +1084,9 @@ Rcpp::List rtsk_table_collection_summary(const SEXP tc) { // @title Get the length of metadata in a table collection and its tables // @param tc an external pointer to table collection as a // \code{tsk_table_collection_t} object. -// @return A named list with the length of metadata. +// @return A named list with the length of metadata as \code{R bit64::integer64} +// values to approach range in \code{C tsk_size_t / uint64_t} +// (see \code{rtsk_wrap_tsk_size_t_as_integer64} for more details). // @examples // ts_file <- system.file("examples/test.trees", package = "RcppTskit") // tc_xptr <- RcppTskit:::rtsk_table_collection_load(ts_file) @@ -951,18 +1097,37 @@ Rcpp::List rtsk_table_collection_summary(const SEXP tc) { Rcpp::List rtsk_table_collection_metadata_length(const SEXP tc) { rtsk_table_collection_t tc_xptr(tc); return Rcpp::List::create( - Rcpp::_["tc"] = static_cast(tc_xptr->metadata_length), - Rcpp::_["populations"] = - static_cast(tc_xptr->populations.metadata_length), - Rcpp::_["migrations"] = - static_cast(tc_xptr->migrations.metadata_length), - Rcpp::_["individuals"] = - static_cast(tc_xptr->individuals.metadata_length), - Rcpp::_["nodes"] = static_cast(tc_xptr->nodes.metadata_length), - Rcpp::_["edges"] = static_cast(tc_xptr->edges.metadata_length), - Rcpp::_["sites"] = static_cast(tc_xptr->sites.metadata_length), - Rcpp::_["mutations"] = - static_cast(tc_xptr->mutations.metadata_length)); + Rcpp::_["tc"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->metadata_length, "rtsk_table_collection_metadata_length/" + "tc_xptr->metadata_length"), + Rcpp::_["populations"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->populations.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->populations.metadata_length"), + Rcpp::_["migrations"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->migrations.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->migrations.metadata_length"), + Rcpp::_["individuals"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->individuals.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->individuals.metadata_length"), + Rcpp::_["nodes"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->nodes.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->nodes.metadata_length"), + Rcpp::_["edges"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->edges.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->edges.metadata_length"), + Rcpp::_["sites"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->sites.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->sites.metadata_length"), + Rcpp::_["mutations"] = rtsk_wrap_tsk_size_t_as_integer64( + tc_xptr->mutations.metadata_length, + "rtsk_table_collection_metadata_length/" + "tc_xptr->mutations.metadata_length")); } // TODO: Metadata notes if we do anything with metadata #36 diff --git a/RcppTskit/tests/testthat/test_TableCollection.R b/RcppTskit/tests/testthat/test_TableCollection.R index 430c8eb..bbbe1fb 100644 --- a/RcppTskit/tests/testthat/test_TableCollection.R +++ b/RcppTskit/tests/testthat/test_TableCollection.R @@ -52,6 +52,10 @@ test_that("TableCollection and TreeSequence round-trip works", { unsupported_options <- bitwShiftL(1L, 27) supported_copy_option <- bitwShiftL(1L, 0) supported_init_options <- bitwOr(bitwShiftL(1L, 0), bitwShiftL(1L, 1)) + expect_error( + rtsk_treeseq_copy_tables(ts_xptr, options = -1), + regexp = "rtsk_treeseq_copy_tables does not support negative options" + ) expect_error( rtsk_treeseq_copy_tables(ts_xptr, options = bitwShiftL(1L, 30)), regexp = "does not support TSK_NO_INIT" @@ -82,7 +86,14 @@ test_that("TableCollection and TreeSequence round-trip works", { "file_uuid", "has_index" ), - value = c(100, FALSE, "generations", FALSE, NA_character_, TRUE) + value = as.character(c( + 100, + FALSE, + "generations", + FALSE, + NA_character_, + TRUE + )) ), tables = data.frame( table = c( @@ -95,8 +106,8 @@ test_that("TableCollection and TreeSequence round-trip works", { "sites", "mutations" ), - number = c(2, 1, 0, 8, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 8, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -105,10 +116,14 @@ test_that("TableCollection and TreeSequence round-trip works", { FALSE, FALSE, FALSE - ) + )) ) ) ) + expect_error( + rtsk_treeseq_init(tc_xptr, options = -1), + regexp = "rtsk_treeseq_init does not support negative options" + ) expect_error( rtsk_treeseq_init(tc_xptr, options = bitwShiftL(1L, 28)), regexp = "does not support TSK_TAKE_OWNERSHIP" @@ -154,7 +169,14 @@ test_that("TableCollection and TreeSequence round-trip works", { "file_uuid", "has_index" ), - value = c(100, FALSE, "generations", FALSE, NA_character_, TRUE) + value = as.character(c( + 100, + FALSE, + "generations", + FALSE, + NA_character_, + TRUE + )) ), tables = data.frame( table = c( @@ -167,8 +189,8 @@ test_that("TableCollection and TreeSequence round-trip works", { "sites", "mutations" ), - number = c(2, 1, 0, 8, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 8, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -177,7 +199,7 @@ test_that("TableCollection and TreeSequence round-trip works", { FALSE, FALSE, FALSE - ) + )) ) ) ) diff --git a/RcppTskit/tests/testthat/test_load_summary_and_dump.R b/RcppTskit/tests/testthat/test_load_summary_and_dump.R index 0596648..ee75415 100644 --- a/RcppTskit/tests/testthat/test_load_summary_and_dump.R +++ b/RcppTskit/tests/testthat/test_load_summary_and_dump.R @@ -12,6 +12,10 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { expect_error(ts_load("nonexistent_ts")) ts_file <- system.file("examples/test.trees", package = "RcppTskit") + expect_error( + rtsk_treeseq_load(ts_file, options = -1), + regexp = "rtsk_treeseq_load does not support negative options" + ) expect_error( rtsk_treeseq_load(ts_file, options = bitwShiftL(1L, 30)), regexp = "rtsk_treeseq_load only supports load options" @@ -167,70 +171,70 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { expect_error(rtsk_treeseq_get_num_provenances()) expect_error(rtsk_treeseq_get_num_provenances(ts)) n_xptr <- rtsk_treeseq_get_num_provenances(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 2L) expect_equal(ts$num_provenances(), 2L) expect_error(rtsk_treeseq_get_num_populations()) expect_error(rtsk_treeseq_get_num_populations(ts)) n_xptr <- rtsk_treeseq_get_num_populations(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 1L) expect_equal(ts$num_populations(), 1L) expect_error(rtsk_treeseq_get_num_migrations()) expect_error(rtsk_treeseq_get_num_migrations(ts)) n_xptr <- rtsk_treeseq_get_num_migrations(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 0L) expect_equal(ts$num_migrations(), 0L) expect_error(rtsk_treeseq_get_num_individuals()) expect_error(rtsk_treeseq_get_num_individuals(ts)) n_xptr <- rtsk_treeseq_get_num_individuals(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 8L) expect_equal(ts$num_individuals(), 8L) expect_error(rtsk_treeseq_get_num_samples()) expect_error(rtsk_treeseq_get_num_samples(ts)) n_xptr <- rtsk_treeseq_get_num_samples(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 16L) expect_equal(ts$num_samples(), 16L) expect_error(rtsk_treeseq_get_num_nodes()) expect_error(rtsk_treeseq_get_num_nodes(ts)) n_xptr <- rtsk_treeseq_get_num_nodes(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 39L) expect_equal(ts$num_nodes(), 39L) expect_error(rtsk_treeseq_get_num_edges()) expect_error(rtsk_treeseq_get_num_edges(ts)) n_xptr <- rtsk_treeseq_get_num_edges(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 59L) expect_equal(ts$num_edges(), 59L) expect_error(rtsk_treeseq_get_num_trees()) expect_error(rtsk_treeseq_get_num_trees(ts)) n_xptr <- rtsk_treeseq_get_num_trees(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 9L) expect_equal(ts$num_trees(), 9L) expect_error(rtsk_treeseq_get_num_sites()) expect_error(rtsk_treeseq_get_num_sites(ts)) n_xptr <- rtsk_treeseq_get_num_sites(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 25L) expect_equal(ts$num_sites(), 25L) expect_error(rtsk_treeseq_get_num_mutations()) expect_error(rtsk_treeseq_get_num_mutations(ts)) n_xptr <- rtsk_treeseq_get_num_mutations(ts_xptr) - expect_true(is.integer(n_xptr)) + expect_true(bit64::is.integer64(n_xptr)) expect_equal(n_xptr, 30L) expect_equal(ts$num_mutations(), 30L) @@ -431,7 +435,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "has_metadata", "file_uuid" ), - value = c( + value = as.character(c( 16, 9, 100, @@ -443,7 +447,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { 6.9619933371908083, FALSE, test_trees_file_uuid - ) + )) ), tables = data.frame( table = c( @@ -456,8 +460,8 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "sites", "mutations" ), - number = c(2, 1, 0, 8, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 8, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -466,7 +470,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { FALSE, FALSE, FALSE - ) + )) ) ) ) @@ -494,14 +498,14 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "file_uuid", "has_index" ), - value = c( + value = as.character(c( 100, FALSE, "generations", FALSE, test_trees_file_uuid, TRUE - ) + )) ), tables = data.frame( table = c( @@ -514,8 +518,8 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "sites", "mutations" ), - number = c(2, 1, 0, 8, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 8, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -524,7 +528,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { FALSE, FALSE, FALSE - ) + )) ) ) ) @@ -821,7 +825,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "has_metadata", "file_uuid" ), - value = c( + value = as.character(c( 16, 9, 100, @@ -833,7 +837,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { 6.9619933371908083, TRUE, test2_trees_file_uuid - ) + )) ), tables = data.frame( table = c( @@ -846,8 +850,8 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "sites", "mutations" ), - number = c(2, 1, 0, 9, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 9, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -856,7 +860,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { FALSE, FALSE, FALSE - ) + )) ) ) ) @@ -916,14 +920,14 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "file_uuid", "has_index" ), - value = c( + value = as.character(c( 100, FALSE, "generations", TRUE, test2_trees_file_uuid, TRUE - ) + )) ), tables = data.frame( table = c( @@ -936,8 +940,8 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { "sites", "mutations" ), - number = c(2, 1, 0, 9, 39, 59, 25, 30), - has_metadata = c( + number = as.character(c(2, 1, 0, 9, 39, 59, 25, 30)), + has_metadata = as.character(c( NA, # provenances have no metadata TRUE, FALSE, @@ -946,7 +950,7 @@ test_that("ts/tc_load(), ts/tc_summary*(), and ts/tc_dump(x) work", { FALSE, FALSE, FALSE - ) + )) ) ) ) diff --git a/RcppTskit/tests/testthat/test_misc.R b/RcppTskit/tests/testthat/test_misc.R index 4544bb6..c6d2dc0 100644 --- a/RcppTskit/tests/testthat/test_misc.R +++ b/RcppTskit/tests/testthat/test_misc.R @@ -26,3 +26,76 @@ test_that("tsk_trace_error() works", { # jarl-ignore internal_function: it's just a test expect_warning(RcppTskit:::test_tsk_trace_error_cpp()) }) + +test_that("validate_options() branches are covered", { + # jarl-ignore internal_function: it's just a test + expect_equal(RcppTskit:::test_validate_options(0L, 0L), 0L) + + # negative options branch + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_validate_options(-1L, 0L), + regexp = "test_validate_options does not support negative options" + ) + + # unsupported bits branch + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_validate_options(1L, 0L), + regexp = "test_validate_options only supports options" + ) + + # non-zero supported flags branch + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_validate_options(1L, 1L), + regexp = "test_validate_options does not support non-zero options" + ) +}) + +test_that("rtsk_wrap_tsk_size_t_as_integer64() works", { + # jarl-ignore internal_function: it's just a test + x <- RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("0") + expect_true(bit64::is.integer64(x)) + expect_equal(x, bit64::as.integer64("0")) + + # jarl-ignore internal_function: it's just a test + x <- RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("42") + expect_true(bit64::is.integer64(x)) + expect_equal(as.character(x), "42") + + # max signed 64-bit integer (limit of bit64::integer64) + max_i64 <- "9223372036854775807" + # jarl-ignore internal_function: it's just a test + x <- RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64(max_i64) + expect_true(bit64::is.integer64(x)) + expect_equal(as.character(x), max_i64) + + # first value above signed 64-bit range + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("9223372036854775808"), + regexp = "exceeds bit64::integer64 maximum" + ) + + # invalid numeric format + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("not_a_number"), + regexp = "base-10 unsigned integer string" + ) + + # parsed prefix only; remaining characters should fail strict parse check + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("123abc"), + regexp = "base-10 unsigned integer string" + ) + + # force range-check branch (test-only path) + # jarl-ignore internal_function: it's just a test + expect_error( + RcppTskit:::test_rtsk_wrap_tsk_size_t_as_integer64("1", TRUE), + regexp = "value is out of range for tsk_size_t" + ) +})