From ce8185de780899ba60348ba33e9c21fb1797a5c9 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Tue, 28 Apr 2026 22:25:42 -0700 Subject: [PATCH 01/18] => S7_generics --- R/{0_S7_init.R => 01_S7_generics.R} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename R/{0_S7_init.R => 01_S7_generics.R} (100%) diff --git a/R/0_S7_init.R b/R/01_S7_generics.R similarity index 100% rename from R/0_S7_init.R rename to R/01_S7_generics.R From 4952059e8df07c2d6797cffaa477384fd280a9aa Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Tue, 28 Apr 2026 22:25:49 -0700 Subject: [PATCH 02/18] init S7 properties --- R/00_S7_properties.R | 323 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 R/00_S7_properties.R diff --git a/R/00_S7_properties.R b/R/00_S7_properties.R new file mode 100644 index 0000000..c4aef6c --- /dev/null +++ b/R/00_S7_properties.R @@ -0,0 +1,323 @@ +# 2026- EDG rtemis.org + +# %% Character ---- +#' Non-empty character scalar S7 property +#' +#' S7 property accepting a single non-NA, non-empty (after trimming whitespace) string. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +character_scalar <- new_property( + class_character, + validator = function(value) { + if (length(value) != 1L || is.na(value) || !nzchar(trimws(value))) { + return("must be a non-empty character scalar") + } + NULL + } +) + +#' Optional non-empty character scalar S7 property +#' +#' S7 property accepting `NULL` or a single non-NA, non-empty (after trimming whitespace) string. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_character_scalar <- new_property( + class = new_union(class_character, NULL), + default = NULL, + validator = function(value) { + if ( + !is.null(value) && + (length(value) != 1L || is.na(value) || !nzchar(trimws(value))) + ) { + return("must be NULL or a non-empty character scalar") + } + NULL + } +) + +# %% Double ---- +#' Double scalar S7 property +#' +#' S7 property accepting a single non-NA double value. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +double_scalar <- new_property( + class_double, + validator = function(value) { + if (length(value) != 1L || is.na(value)) { + return("must be a double scalar") + } + NULL + } +) + +#' Optional double scalar S7 property +#' +#' S7 property accepting `NULL` or a single non-NA double value. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_double_scalar <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value))) { + return("must be NULL or a double scalar") + } + NULL + } +) + +# %% Integer ---- +#' Integer scalar S7 property +#' +#' S7 property accepting a single non-NA integer value (must be `integer` type, e.g. `1L`). +#' +#' @return An S7 property object. +#' @author EDG +#' @export +integer_scalar <- new_property( + class_integer, + validator = function(value) { + if (length(value) != 1L || is.na(value)) { + return("must be an integer scalar (e.g. 1L)") + } + NULL + } +) + +#' Optional integer scalar S7 property +#' +#' S7 property accepting `NULL` or a single non-NA integer value (must be `integer` type, e.g. `1L`). +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_integer_scalar <- new_property( + class = new_union(class_integer, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value))) { + return("must be NULL or an integer scalar (e.g. 1L)") + } + NULL + } +) + +# %% Logical ---- +#' Logical scalar S7 property +#' +#' S7 property accepting a single non-NA logical value. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +logical_scalar <- new_property( + class_logical, + validator = function(value) { + if (length(value) != 1L || is.na(value)) { + return("must be a logical scalar (TRUE or FALSE)") + } + NULL + } +) + +#' Optional logical scalar S7 property +#' +#' S7 property accepting `NULL` or a single non-NA logical value. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_logical_scalar <- new_property( + class = new_union(class_logical, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value))) { + return("must be NULL or a logical scalar (TRUE or FALSE)") + } + NULL + } +) + +# %% Bounded double ---- +#' Probability scalar S7 property +#' +#' S7 property accepting a single finite double in \eqn{[0, 1]}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +prob_scalar <- new_property( + class_double, + validator = function(value) { + if (length(value) != 1L || is.na(value) || value < 0 || value > 1) { + return("must be a finite double in [0, 1]") + } + NULL + } +) + +#' Optional probability scalar S7 property +#' +#' S7 property accepting `NULL` or a single finite double in \eqn{[0, 1]}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_prob_scalar <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value) || value < 0 || value > 1)) { + return("must be NULL or a finite double in [0, 1]") + } + NULL + } +) + +#' Open-unit-interval scalar S7 property +#' +#' S7 property accepting a single finite double strictly in \eqn{(0, 1)}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +unit_open_scalar <- new_property( + class_double, + validator = function(value) { + if (length(value) != 1L || is.na(value) || value <= 0 || value >= 1) { + return("must be a finite double in (0, 1)") + } + NULL + } +) + +#' Positive double scalar S7 property +#' +#' S7 property accepting a single finite double strictly greater than zero, i.e. in \eqn{(0, \infty)}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +pos_double_scalar <- new_property( + class_double, + validator = function(value) { + if (length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0) { + return("must be a finite positive double (> 0)") + } + NULL + } +) + +#' Optional positive double scalar S7 property +#' +#' S7 property accepting `NULL` or a single finite double strictly greater than zero. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_pos_double_scalar <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0)) { + return("must be NULL or a finite positive double (> 0)") + } + NULL + } +) + +#' Non-negative double scalar S7 property +#' +#' S7 property accepting a single finite double greater than or equal to zero, i.e. in \eqn{[0, \infty)}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +nonneg_double_scalar <- new_property( + class_double, + validator = function(value) { + if (length(value) != 1L || is.na(value) || !is.finite(value) || value < 0) { + return("must be a finite non-negative double (>= 0)") + } + NULL + } +) + +#' Optional non-negative double scalar S7 property +#' +#' S7 property accepting `NULL` or a single finite double greater than or equal to zero. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_nonneg_double_scalar <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (!is.null(value) && (length(value) != 1L || is.na(value) || !is.finite(value) || value < 0)) { + return("must be NULL or a finite non-negative double (>= 0)") + } + NULL + } +) + +# %% Factory ---- +#' Create a bounded double S7 property +#' +#' Returns a `new_property()` for a double scalar constrained to a given interval. +#' Useful for bounds not covered by the pre-built properties. +#' +#' @param lower Numeric scalar. Lower bound. Default `-Inf`. +#' @param upper Numeric scalar. Upper bound. Default `Inf`. +#' @param lower_open Logical scalar. If `TRUE`, lower bound is exclusive `(lower, ...]`. +#' Default `FALSE`. +#' @param upper_open Logical scalar. If `TRUE`, upper bound is exclusive `[..., upper)`. +#' Default `FALSE`. +#' @param nullable Logical scalar. If `TRUE`, `NULL` is also accepted. Default `FALSE`. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +#' +#' @examples +#' # Learning rate in (0, 1] +#' lr_prop <- bounded_double_property(0, 1, lower_open = TRUE) +bounded_double_property <- function( + lower = -Inf, + upper = Inf, + lower_open = FALSE, + upper_open = FALSE, + nullable = FALSE +) { + lower_sym <- if (lower_open) "(" else "[" + upper_sym <- if (upper_open) ")" else "]" + bound_desc <- paste0("must be a finite double in ", lower_sym, lower, ", ", upper, upper_sym) + + check_lower <- if (lower_open) function(v) v > lower else function(v) v >= lower + check_upper <- if (upper_open) function(v) v < upper else function(v) v <= upper + + cls <- if (nullable) new_union(class_double, NULL) else class_double + + new_property( + class = cls, + validator = function(value) { + if (is.null(value)) return(NULL) + if (length(value) != 1L || is.na(value) || !is.finite(value)) { + return(paste0(bound_desc, " (must be a finite scalar)")) + } + if (!check_lower(value) || !check_upper(value)) { + return(bound_desc) + } + NULL + } + ) +} From 609fcb29f8760733032a1abd46575ddac6c69fc4 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:18:06 -0700 Subject: [PATCH 03/18] add S7 properties++ --- R/00_S7_properties.R | 426 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 417 insertions(+), 9 deletions(-) diff --git a/R/00_S7_properties.R b/R/00_S7_properties.R index c4aef6c..d8d5e58 100644 --- a/R/00_S7_properties.R +++ b/R/00_S7_properties.R @@ -1,5 +1,28 @@ # 2026- EDG rtemis.org +# TOC ---- +# Character ------------------------------------------------------------------- +# character_scalar / optional_character_scalar +# Double ---------------------------------------------------------------------- +# double_scalar / optional_double_scalar +# Integer --------------------------------------------------------------------- +# integer_scalar / optional_integer_scalar +# pos_integer_scalar / optional_pos_integer_scalar (0, Inf) +# Logical --------------------------------------------------------------------- +# logical_scalar / optional_logical_scalar +# Bounded double scalars ------------------------------------------------------ +# prob_scalar / optional_prob_scalar [0, 1] +# unit_open_scalar / optional_unit_open_scalar (0, 1) +# pos_double_scalar / optional_pos_double_scalar (0, Inf) +# nonneg_double_scalar / optional_nonneg_double_scalar [0, Inf) +# Bounded double vectors ------------------------------------------------------ +# prob_vector / optional_prob_vector [0, 1] +# unit_open_vector / optional_unit_open_vector (0, 1) +# pos_double_vector / optional_pos_double_vector (0, Inf) +# nonneg_double_vector / optional_nonneg_double_vector [0, Inf) +# Factory --------------------------------------------------------------------- +# bounded_double_property + # %% Character ---- #' Non-empty character scalar S7 property #' @@ -18,6 +41,7 @@ character_scalar <- new_property( } ) + #' Optional non-empty character scalar S7 property #' #' S7 property accepting `NULL` or a single non-NA, non-empty (after trimming whitespace) string. @@ -39,6 +63,7 @@ optional_character_scalar <- new_property( } ) + # %% Double ---- #' Double scalar S7 property #' @@ -57,6 +82,7 @@ double_scalar <- new_property( } ) + #' Optional double scalar S7 property #' #' S7 property accepting `NULL` or a single non-NA double value. @@ -75,6 +101,7 @@ optional_double_scalar <- new_property( } ) + # %% Integer ---- #' Integer scalar S7 property #' @@ -93,6 +120,7 @@ integer_scalar <- new_property( } ) + #' Optional integer scalar S7 property #' #' S7 property accepting `NULL` or a single non-NA integer value (must be `integer` type, e.g. `1L`). @@ -111,6 +139,46 @@ optional_integer_scalar <- new_property( } ) + +#' Positive integer scalar S7 property +#' +#' S7 property accepting a single non-NA integer value strictly greater than zero (e.g. `1L`). +#' +#' @return An S7 property object. +#' @author EDG +#' @export +pos_integer_scalar <- new_property( + class_integer, + validator = function(value) { + if (length(value) != 1L || is.na(value) || value <= 0L) { + return("must be a positive integer scalar (> 0, e.g. 1L)") + } + NULL + } +) + + +#' Optional positive integer scalar S7 property +#' +#' S7 property accepting `NULL` or a single non-NA integer value strictly greater than zero. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_pos_integer_scalar <- new_property( + class = new_union(class_integer, NULL), + default = NULL, + validator = function(value) { + if ( + !is.null(value) && (length(value) != 1L || is.na(value) || value <= 0L) + ) { + return("must be NULL or a positive integer scalar (> 0, e.g. 1L)") + } + NULL + } +) + + # %% Logical ---- #' Logical scalar S7 property #' @@ -129,6 +197,7 @@ logical_scalar <- new_property( } ) + #' Optional logical scalar S7 property #' #' S7 property accepting `NULL` or a single non-NA logical value. @@ -147,7 +216,8 @@ optional_logical_scalar <- new_property( } ) -# %% Bounded double ---- + +# %% Bounded double scalars ---- #' Probability scalar S7 property #' #' S7 property accepting a single finite double in \eqn{[0, 1]}. @@ -165,6 +235,7 @@ prob_scalar <- new_property( } ) + #' Optional probability scalar S7 property #' #' S7 property accepting `NULL` or a single finite double in \eqn{[0, 1]}. @@ -176,13 +247,17 @@ optional_prob_scalar <- new_property( class = new_union(class_double, NULL), default = NULL, validator = function(value) { - if (!is.null(value) && (length(value) != 1L || is.na(value) || value < 0 || value > 1)) { + if ( + !is.null(value) && + (length(value) != 1L || is.na(value) || value < 0 || value > 1) + ) { return("must be NULL or a finite double in [0, 1]") } NULL } ) + #' Open-unit-interval scalar S7 property #' #' S7 property accepting a single finite double strictly in \eqn{(0, 1)}. @@ -200,6 +275,29 @@ unit_open_scalar <- new_property( } ) + +#' Optional open-unit-interval scalar S7 property +#' +#' S7 property accepting `NULL` or a single finite double strictly in \eqn{(0, 1)}. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_unit_open_scalar <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if ( + !is.null(value) && + (length(value) != 1L || is.na(value) || value <= 0 || value >= 1) + ) { + return("must be NULL or a finite double in (0, 1)") + } + NULL + } +) + + #' Positive double scalar S7 property #' #' S7 property accepting a single finite double strictly greater than zero, i.e. in \eqn{(0, \infty)}. @@ -210,13 +308,16 @@ unit_open_scalar <- new_property( pos_double_scalar <- new_property( class_double, validator = function(value) { - if (length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0) { + if ( + length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0 + ) { return("must be a finite positive double (> 0)") } NULL } ) + #' Optional positive double scalar S7 property #' #' S7 property accepting `NULL` or a single finite double strictly greater than zero. @@ -228,13 +329,17 @@ optional_pos_double_scalar <- new_property( class = new_union(class_double, NULL), default = NULL, validator = function(value) { - if (!is.null(value) && (length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0)) { + if ( + !is.null(value) && + (length(value) != 1L || is.na(value) || !is.finite(value) || value <= 0) + ) { return("must be NULL or a finite positive double (> 0)") } NULL } ) + #' Non-negative double scalar S7 property #' #' S7 property accepting a single finite double greater than or equal to zero, i.e. in \eqn{[0, \infty)}. @@ -252,6 +357,7 @@ nonneg_double_scalar <- new_property( } ) + #' Optional non-negative double scalar S7 property #' #' S7 property accepting `NULL` or a single finite double greater than or equal to zero. @@ -263,13 +369,233 @@ optional_nonneg_double_scalar <- new_property( class = new_union(class_double, NULL), default = NULL, validator = function(value) { - if (!is.null(value) && (length(value) != 1L || is.na(value) || !is.finite(value) || value < 0)) { + if ( + !is.null(value) && + (length(value) != 1L || is.na(value) || !is.finite(value) || value < 0) + ) { return("must be NULL or a finite non-negative double (>= 0)") } NULL } ) + +# %% Bounded double vectors ---- +#' Probability vector S7 property +#' +#' S7 property accepting a non-empty double vector with all elements in \eqn{[0, 1]} and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +prob_vector <- new_property( + class_double, + validator = function(value) { + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (any(value < 0) || any(value > 1)) { + return("all elements must be in [0, 1]") + } + NULL + } +) + + +#' Optional probability vector S7 property +#' +#' S7 property accepting `NULL` or a non-empty double vector with all elements in \eqn{[0, 1]} +#' and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_prob_vector <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (any(value < 0) || any(value > 1)) { + return("all elements must be in [0, 1]") + } + NULL + } +) + + +#' Open-unit-interval vector S7 property +#' +#' S7 property accepting a non-empty double vector with all elements strictly in \eqn{(0, 1)} +#' and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +unit_open_vector <- new_property( + class_double, + validator = function(value) { + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (any(value <= 0) || any(value >= 1)) { + return("all elements must be in (0, 1)") + } + NULL + } +) + + +#' Optional open-unit-interval vector S7 property +#' +#' S7 property accepting `NULL` or a non-empty double vector with all elements strictly in +#' \eqn{(0, 1)} and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_unit_open_vector <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (any(value <= 0) || any(value >= 1)) { + return("all elements must be in (0, 1)") + } + NULL + } +) + + +#' Positive double vector S7 property +#' +#' S7 property accepting a non-empty double vector with all elements finite, strictly greater +#' than zero, and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +pos_double_vector <- new_property( + class_double, + validator = function(value) { + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (!all(is.finite(value)) || any(value <= 0)) { + return("all elements must be finite and > 0") + } + NULL + } +) + + +#' Optional positive double vector S7 property +#' +#' S7 property accepting `NULL` or a non-empty double vector with all elements finite, +#' strictly greater than zero, and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_pos_double_vector <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (!all(is.finite(value)) || any(value <= 0)) { + return("all elements must be finite and > 0") + } + NULL + } +) + + +#' Non-negative double vector S7 property +#' +#' S7 property accepting a non-empty double vector with all elements finite, greater than or +#' equal to zero, and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +nonneg_double_vector <- new_property( + class_double, + validator = function(value) { + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (!all(is.finite(value)) || any(value < 0)) { + return("all elements must be finite and >= 0") + } + NULL + } +) + + +#' Optional non-negative double vector S7 property +#' +#' S7 property accepting `NULL` or a non-empty double vector with all elements finite, +#' greater than or equal to zero, and no NAs. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +optional_nonneg_double_vector <- new_property( + class = new_union(class_double, NULL), + default = NULL, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (length(value) == 0L) { + return("must be a non-empty vector") + } + if (anyNA(value)) { + return("must not contain NAs") + } + if (!all(is.finite(value)) || any(value < 0)) { + return("all elements must be finite and >= 0") + } + NULL + } +) + + # %% Factory ---- #' Create a bounded double S7 property #' @@ -300,17 +626,34 @@ bounded_double_property <- function( ) { lower_sym <- if (lower_open) "(" else "[" upper_sym <- if (upper_open) ")" else "]" - bound_desc <- paste0("must be a finite double in ", lower_sym, lower, ", ", upper, upper_sym) + bound_desc <- paste0( + "must be a finite double in ", + lower_sym, + lower, + ", ", + upper, + upper_sym + ) - check_lower <- if (lower_open) function(v) v > lower else function(v) v >= lower - check_upper <- if (upper_open) function(v) v < upper else function(v) v <= upper + check_lower <- if (lower_open) { + function(v) v > lower + } else { + function(v) v >= lower + } + check_upper <- if (upper_open) { + function(v) v < upper + } else { + function(v) v <= upper + } cls <- if (nullable) new_union(class_double, NULL) else class_double new_property( class = cls, validator = function(value) { - if (is.null(value)) return(NULL) + if (is.null(value)) { + return(NULL) + } if (length(value) != 1L || is.na(value) || !is.finite(value)) { return(paste0(bound_desc, " (must be a finite scalar)")) } @@ -321,3 +664,68 @@ bounded_double_property <- function( } ) } + + +# %% enum() ---- +#' Create an enum S7 property +#' +#' Returns a `new_property()` for a character scalar constrained to a fixed set of allowed values. +#' +#' @param values Character: Allowed values. +#' @param default Optional Character: Default value. +#' @param nullable Logical scalar. If `TRUE`, `NULL` is also accepted. Default `FALSE`. +#' +#' @return An S7 property object. +#' @author EDG +#' @export +#' +#' @examples +#' type_prop <- enum(c("string", "number", "boolean"), default = "string") +enum <- function(values, default = NULL, nullable = FALSE) { + cls <- if (nullable) new_union(class_character, NULL) else class_character + new_property( + class = cls, + validator = function(value) { + if (is.null(value)) { + return(NULL) + } + if (length(value) != 1L || is.na(value)) { + return("must be a single non-NA character scalar") + } + if (!value %in% values) { + return(paste0( + "must be one of ", + paste(paste0('"', values, '"'), collapse = ", ") + )) + } + NULL + }, + default = default + ) +} + + +# %% optional ---- +#' Create an optional S7 type +#' +#' Creates an S7 union type that allows for the specified type or `NULL`. +#' +#' This should be used when the S7 class already includes all the necessary validation for the +#' non-NULL case. Otherwise, create a new S7 property with appropriate validator using +#' `S7::new_property()`. +#' +#' @param type S7 class. +#' @return An S7 union type that allows for the specified type or `NULL`. +#' @author EDG +#' @export +#' @examples +#' # Create an optional character type +#' optional(S7::class_character) +optional <- function(type) { + if (!inherits(type, "S7_base_class") && !inherits(type, "S7_class")) { + cli::cli_abort( + "{.var type} must be an S7 base class or S7 class." + ) + } + S7::new_union(type, NULL) +} From 222a614602a6e173c2eda6a79be2a0512cd94824 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:18:25 -0700 Subject: [PATCH 04/18] mirror S7 properties --- R/check.R | 815 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 800 insertions(+), 15 deletions(-) diff --git a/R/check.R b/R/check.R index 80bd4ba..e6874f4 100644 --- a/R/check.R +++ b/R/check.R @@ -3,6 +3,53 @@ # check_* functions perform checks and throw error using cli::cli_abort if checks fail; # do not return a value. +# TOC ---- +# General checks +# check_inherits +# check_logical +# check_character +# check_floatpos +# check_float01exc +# check_float01inc +# check_floatpos1 +# check_float0pos +# check_float_neg1_1 +# check_dependencies +# check_data.table +# check_tabular +# check_enum +# S7-parallel scalar checks +# check_character_scalar +# check_optional_character_scalar +# check_double_scalar +# check_optional_double_scalar +# check_integer_scalar +# check_optional_integer_scalar +# check_pos_integer_scalar +# check_optional_pos_integer_scalar +# check_logical_scalar +# check_optional_logical_scalar +# check_prob_scalar +# check_optional_prob_scalar +# check_unit_open_scalar +# check_pos_double_scalar +# check_optional_pos_double_scalar +# check_nonneg_double_scalar +# check_optional_nonneg_double_scalar +# S7-parallel vector checks +# check_prob_vector +# check_optional_prob_vector +# check_unit_open_vector +# check_optional_unit_open_vector +# check_pos_double_vector +# check_optional_pos_double_vector +# check_nonneg_double_vector +# check_optional_nonneg_double_vector +# Legacy (superseded by check_*_scalar equivalents above) +# check_scalar_logical +# check_scalar_character +# check_optional_scalar_character + # %% check_inherits ---- #' Check class of object #' @@ -534,6 +581,744 @@ check_enum <- function(x, allowed_values, arg_name = deparse(substitute(x))) { } # /rtemis.core::check_enum +# %% S7-parallel scalar checks ---- +# Naming mirrors the S7 properties in 00_S7_properties.R one-to-one. +# Use these in regular function bodies for early, user-friendly argument validation. + +# %% check_character_scalar ---- +#' Check character scalar +#' +#' @param x Character: Value to check. Must be a single non-NA, non-empty string. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_character_scalar("hello") +#' # Throw error: +#' try(check_character_scalar("")) +#' try(check_character_scalar(NA_character_)) +#' try(check_character_scalar(c("a", "b"))) +check_character_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (!is.character(x)) { + cli::cli_abort("{.var {arg_name}} must be a character string.") + } + if (length(x) != 1L || is.na(x) || !nzchar(trimws(x))) { + cli::cli_abort("{.var {arg_name}} must be a single non-empty string.") + } + invisible() +} # /rtemis.core::check_character_scalar + + +# %% check_optional_character_scalar ---- +#' Check optional character scalar +#' +#' @param x Optional Character: Value to check. Must be `NULL` or a single non-NA, non-empty string. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_character_scalar(NULL) +#' check_optional_character_scalar("hello") +#' # Throw error: +#' try(check_optional_character_scalar("")) +#' try(check_optional_character_scalar(c("a", "b"))) +check_optional_character_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_character_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_character_scalar + + +# %% check_double_scalar ---- +#' Check double scalar +#' +#' @param x Numeric: Value to check. Must be a single non-NA number (integer inputs are accepted). +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_double_scalar(3.14) +#' check_double_scalar(1L) +#' # Throw error: +#' try(check_double_scalar(NA_real_)) +#' try(check_double_scalar(c(1.0, 2.0))) +check_double_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) != 1L || is.na(x)) { + cli::cli_abort("{.var {arg_name}} must be a single non-NA number.") + } + invisible() +} # /rtemis.core::check_double_scalar + + +# %% check_optional_double_scalar ---- +#' Check optional double scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single non-NA number. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_double_scalar(NULL) +#' check_optional_double_scalar(2.5) +#' # Throw error: +#' try(check_optional_double_scalar(NA_real_)) +check_optional_double_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (is.null(x)) { + return(invisible()) + } + check_double_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_double_scalar + + +# %% check_integer_scalar ---- +#' Check integer scalar +#' +#' @details +#' Accepts any single numeric value that is a whole number. Integer-typed inputs (`1L`) and +#' double-typed whole numbers (`1`, `100`) are both accepted for user convenience. +#' +#' @param x Numeric: Value to check. Must be a single non-NA whole number. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_integer_scalar(5L) +#' check_integer_scalar(100) +#' # Throw error: +#' try(check_integer_scalar(1.5)) +#' try(check_integer_scalar(NA_integer_)) +check_integer_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) != 1L || is.na(x)) { + cli::cli_abort("{.var {arg_name}} must be a single non-NA number.") + } + if (x != round(x)) { + cli::cli_abort("{.var {arg_name}} must be a whole number.") + } + invisible() +} # /rtemis.core::check_integer_scalar + + +# %% check_optional_integer_scalar ---- +#' Check optional integer scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single non-NA whole number. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_integer_scalar(NULL) +#' check_optional_integer_scalar(10L) +#' # Throw error: +#' try(check_optional_integer_scalar(1.5)) +check_optional_integer_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_integer_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_integer_scalar + + +# %% check_pos_integer_scalar ---- +#' Check positive integer scalar +#' +#' @details +#' Accepts any single numeric value that is a whole number strictly greater than zero. +#' Integer-typed inputs (`1L`) and double-typed whole numbers (`1`, `100`) are both accepted for +#' user convenience. +#' +#' @param x Numeric: Value to check. Must be a single non-NA whole number greater than zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_pos_integer_scalar(1L) +#' check_pos_integer_scalar(10) +#' # Throw error: +#' try(check_pos_integer_scalar(0)) +#' try(check_pos_integer_scalar(-1L)) +#' try(check_pos_integer_scalar(1.5)) +check_pos_integer_scalar <- function(x, arg_name = deparse(substitute(x))) { + check_integer_scalar(x, arg_name = arg_name) + if (x <= 0) { + cli::cli_abort("{.var {arg_name}} must be a whole number greater than 0.") + } + invisible() +} # /rtemis.core::check_pos_integer_scalar + + +# %% check_optional_pos_integer_scalar ---- +#' Check optional positive integer scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single non-NA whole number +#' greater than zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_pos_integer_scalar(NULL) +#' check_optional_pos_integer_scalar(5L) +#' # Throw error: +#' try(check_optional_pos_integer_scalar(0)) +check_optional_pos_integer_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_pos_integer_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_pos_integer_scalar + + +# %% check_logical_scalar ---- +#' Check logical scalar +#' +#' @param x Logical: Value to check. Must be a single non-NA `TRUE` or `FALSE`. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_logical_scalar(TRUE) +#' check_logical_scalar(FALSE) +#' # Throw error: +#' try(check_logical_scalar(NA)) +#' try(check_logical_scalar(1L)) +#' try(check_logical_scalar(c(TRUE, FALSE))) +check_logical_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (!is.logical(x)) { + cli::cli_abort("{.var {arg_name}} must be TRUE or FALSE.") + } + if (length(x) != 1L || is.na(x)) { + cli::cli_abort("{.var {arg_name}} must be a single TRUE or FALSE.") + } + invisible() +} # /rtemis.core::check_logical_scalar + + +# %% check_optional_logical_scalar ---- +#' Check optional logical scalar +#' +#' @param x Optional Logical: Value to check. Must be `NULL` or a single non-NA `TRUE` or `FALSE`. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_logical_scalar(NULL) +#' check_optional_logical_scalar(FALSE) +#' # Throw error: +#' try(check_optional_logical_scalar(NA)) +check_optional_logical_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_logical_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_logical_scalar + + +# %% check_prob_scalar ---- +#' Check probability scalar +#' +#' @param x Numeric: Value to check. Must be a single finite number in \eqn{[0, 1]}. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_prob_scalar(0) +#' check_prob_scalar(0.5) +#' check_prob_scalar(1) +#' # Throw error: +#' try(check_prob_scalar(1.5)) +#' try(check_prob_scalar(-0.1)) +check_prob_scalar <- function(x, arg_name = deparse(substitute(x))) { + check_double_scalar(x, arg_name = arg_name) + if (x < 0 || x > 1) { + cli::cli_abort("{.var {arg_name}} must be in [0, 1].") + } + invisible() +} # /rtemis.core::check_prob_scalar + + +# %% check_optional_prob_scalar ---- +#' Check optional probability scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single finite number in \eqn{[0, 1]}. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_prob_scalar(NULL) +#' check_optional_prob_scalar(0.5) +#' # Throw error: +#' try(check_optional_prob_scalar(2.0)) +check_optional_prob_scalar <- function(x, arg_name = deparse(substitute(x))) { + if (is.null(x)) { + return(invisible()) + } + check_prob_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_prob_scalar + + +# %% check_unit_open_scalar ---- +#' Check open-unit-interval scalar +#' +#' @param x Numeric: Value to check. Must be a single finite number strictly in \eqn{(0, 1)}. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_unit_open_scalar(0.5) +#' # Throw error: +#' try(check_unit_open_scalar(0)) +#' try(check_unit_open_scalar(1)) +check_unit_open_scalar <- function(x, arg_name = deparse(substitute(x))) { + check_double_scalar(x, arg_name = arg_name) + if (x <= 0 || x >= 1) { + cli::cli_abort("{.var {arg_name}} must be strictly in (0, 1).") + } + invisible() +} # /rtemis.core::check_unit_open_scalar + + +# %% check_pos_double_scalar ---- +#' Check positive double scalar +#' +#' @param x Numeric: Value to check. Must be a single finite number strictly greater than zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_pos_double_scalar(0.001) +#' check_pos_double_scalar(100) +#' # Throw error: +#' try(check_pos_double_scalar(0)) +#' try(check_pos_double_scalar(-1)) +#' try(check_pos_double_scalar(Inf)) +check_pos_double_scalar <- function(x, arg_name = deparse(substitute(x))) { + check_double_scalar(x, arg_name = arg_name) + if (!is.finite(x) || x <= 0) { + cli::cli_abort("{.var {arg_name}} must be a finite number greater than 0.") + } + invisible() +} # /rtemis.core::check_pos_double_scalar + + +# %% check_optional_pos_double_scalar ---- +#' Check optional positive double scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single finite number +#' strictly greater than zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_pos_double_scalar(NULL) +#' check_optional_pos_double_scalar(2.5) +#' # Throw error: +#' try(check_optional_pos_double_scalar(0)) +check_optional_pos_double_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_pos_double_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_pos_double_scalar + + +# %% check_nonneg_double_scalar ---- +#' Check non-negative double scalar +#' +#' @param x Numeric: Value to check. Must be a single finite number greater than or equal to zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_nonneg_double_scalar(0) +#' check_nonneg_double_scalar(5) +#' # Throw error: +#' try(check_nonneg_double_scalar(-0.001)) +#' try(check_nonneg_double_scalar(Inf)) +check_nonneg_double_scalar <- function(x, arg_name = deparse(substitute(x))) { + check_double_scalar(x, arg_name = arg_name) + if (!is.finite(x) || x < 0) { + cli::cli_abort("{.var {arg_name}} must be a finite number >= 0.") + } + invisible() +} # /rtemis.core::check_nonneg_double_scalar + + +# %% check_optional_nonneg_double_scalar ---- +#' Check optional non-negative double scalar +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a single finite number +#' greater than or equal to zero. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_nonneg_double_scalar(NULL) +#' check_optional_nonneg_double_scalar(0) +#' # Throw error: +#' try(check_optional_nonneg_double_scalar(-1)) +check_optional_nonneg_double_scalar <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_nonneg_double_scalar(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_nonneg_double_scalar + + +# %% S7-parallel vector checks ---- + +# %% check_prob_vector ---- +#' Check probability vector +#' +#' @param x Numeric: Value to check. Must be a non-empty vector with all elements in \eqn{[0, 1]} +#' and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_prob_vector(c(0, 0.5, 1)) +#' # Throw error: +#' try(check_prob_vector(c(0.5, 1.5))) +#' try(check_prob_vector(c(0.5, NA))) +check_prob_vector <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) == 0L) { + cli::cli_abort("{.var {arg_name}} must be non-empty.") + } + if (anyNA(x)) { + cli::cli_abort("{.var {arg_name}} must not contain NAs.") + } + if (any(x < 0) || any(x > 1)) { + cli::cli_abort("{.var {arg_name}} all elements must be in [0, 1].") + } + invisible() +} # /rtemis.core::check_prob_vector + + +# %% check_optional_prob_vector ---- +#' Check optional probability vector +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a non-empty vector with all +#' elements in \eqn{[0, 1]} and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_prob_vector(NULL) +#' check_optional_prob_vector(c(0.2, 0.8)) +#' # Throw error: +#' try(check_optional_prob_vector(c(0.5, 2))) +check_optional_prob_vector <- function(x, arg_name = deparse(substitute(x))) { + if (is.null(x)) { + return(invisible()) + } + check_prob_vector(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_prob_vector + + +# %% check_unit_open_vector ---- +#' Check open-unit-interval vector +#' +#' @param x Numeric: Value to check. Must be a non-empty vector with all elements strictly in +#' \eqn{(0, 1)} and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_unit_open_vector(c(0.2, 0.5, 0.9)) +#' # Throw error: +#' try(check_unit_open_vector(c(0, 0.5))) +#' try(check_unit_open_vector(c(0.5, 1))) +check_unit_open_vector <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) == 0L) { + cli::cli_abort("{.var {arg_name}} must be non-empty.") + } + if (anyNA(x)) { + cli::cli_abort("{.var {arg_name}} must not contain NAs.") + } + if (any(x <= 0) || any(x >= 1)) { + cli::cli_abort("{.var {arg_name}} all elements must be strictly in (0, 1).") + } + invisible() +} # /rtemis.core::check_unit_open_vector + + +# %% check_optional_unit_open_vector ---- +#' Check optional open-unit-interval vector +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a non-empty vector with all +#' elements strictly in \eqn{(0, 1)} and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_unit_open_vector(NULL) +#' check_optional_unit_open_vector(c(0.1, 0.9)) +#' # Throw error: +#' try(check_optional_unit_open_vector(c(0, 0.5))) +check_optional_unit_open_vector <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_unit_open_vector(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_unit_open_vector + + +# %% check_pos_double_vector ---- +#' Check positive double vector +#' +#' @param x Numeric: Value to check. Must be a non-empty vector with all elements finite, +#' strictly greater than zero, and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_pos_double_vector(c(0.1, 1, 10)) +#' # Throw error: +#' try(check_pos_double_vector(c(0, 1))) +#' try(check_pos_double_vector(c(1, Inf))) +check_pos_double_vector <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) == 0L) { + cli::cli_abort("{.var {arg_name}} must be non-empty.") + } + if (anyNA(x)) { + cli::cli_abort("{.var {arg_name}} must not contain NAs.") + } + if (!all(is.finite(x)) || any(x <= 0)) { + cli::cli_abort("{.var {arg_name}} all elements must be finite and > 0.") + } + invisible() +} # /rtemis.core::check_pos_double_vector + + +# %% check_optional_pos_double_vector ---- +#' Check optional positive double vector +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a non-empty vector with all +#' elements finite, strictly greater than zero, and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_pos_double_vector(NULL) +#' check_optional_pos_double_vector(c(0.5, 2)) +#' # Throw error: +#' try(check_optional_pos_double_vector(c(0, 1))) +check_optional_pos_double_vector <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_pos_double_vector(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_pos_double_vector + + +# %% check_nonneg_double_vector ---- +#' Check non-negative double vector +#' +#' @param x Numeric: Value to check. Must be a non-empty vector with all elements finite, +#' greater than or equal to zero, and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_nonneg_double_vector(c(0, 1, 2.5)) +#' # Throw error: +#' try(check_nonneg_double_vector(c(-1, 0, 1))) +#' try(check_nonneg_double_vector(c(1, Inf))) +check_nonneg_double_vector <- function(x, arg_name = deparse(substitute(x))) { + if (!is.numeric(x)) { + cli::cli_abort("{.var {arg_name}} must be numeric.") + } + if (length(x) == 0L) { + cli::cli_abort("{.var {arg_name}} must be non-empty.") + } + if (anyNA(x)) { + cli::cli_abort("{.var {arg_name}} must not contain NAs.") + } + if (!all(is.finite(x)) || any(x < 0)) { + cli::cli_abort("{.var {arg_name}} all elements must be finite and >= 0.") + } + invisible() +} # /rtemis.core::check_nonneg_double_vector + + +# %% check_optional_nonneg_double_vector ---- +#' Check optional non-negative double vector +#' +#' @param x Optional Numeric: Value to check. Must be `NULL` or a non-empty vector with all +#' elements finite, greater than or equal to zero, and no NAs. +#' @param arg_name Character: Argument name to use in error messages. +#' +#' @return Called for side effects. Throws an error if checks fail. +#' +#' @author EDG +#' @export +#' +#' @examples +#' check_optional_nonneg_double_vector(NULL) +#' check_optional_nonneg_double_vector(c(0, 1, 5)) +#' # Throw error: +#' try(check_optional_nonneg_double_vector(c(-1, 0))) +check_optional_nonneg_double_vector <- function( + x, + arg_name = deparse(substitute(x)) +) { + if (is.null(x)) { + return(invisible()) + } + check_nonneg_double_vector(x, arg_name = arg_name) + invisible() +} # /rtemis.core::check_optional_nonneg_double_vector + + +# %% Legacy - to be removed ---- +# check_scalar_* are superseded by the check_*_scalar equivalents above. + +# %% check_scalar_logical ---- #' Check Scalar Logical #' #' @param x Logical: Value to check. @@ -545,21 +1330,20 @@ check_enum <- function(x, allowed_values, arg_name = deparse(substitute(x))) { #' @export #' #' @examples -#' check_scalar_logical(TRUE, "my_arg") # Passes -#' check_scalar_logical(FALSE, "my_arg") # Passes +#' check_scalar_logical(TRUE, "my_arg") #' # Throw error: #' try(check_scalar_logical(c(TRUE, FALSE), "my_arg")) #' try(check_scalar_logical(NA, "my_arg")) -#' try(check_scalar_logical("TRUE", "my_arg")) -check_scalar_logical <- function(x, arg_name) { +check_scalar_logical <- function(x, arg_name = deparse(substitute(x))) { check_logical(x, allow_null = FALSE, arg_name = arg_name) if (length(x) != 1L) { cli::cli_abort("{.var {arg_name}} must be a single TRUE or FALSE value.") } invisible() -} +} # /rtemis.core::check_scalar_logical +# %% check_scalar_character ---- #' Check Scalar Character #' #' @param x Character: Value to check. @@ -571,17 +1355,17 @@ check_scalar_logical <- function(x, arg_name) { #' @export #' #' @examples -#' check_scalar_character("hello", "my_arg") # Passes +#' check_scalar_character("hello", "my_arg") #' # Throw error: #' try(check_scalar_character(c("hello", "world"), "my_arg")) #' try(check_scalar_character(123, "my_arg")) -check_scalar_character <- function(x, arg_name) { +check_scalar_character <- function(x, arg_name = deparse(substitute(x))) { check_character(x, allow_null = FALSE, arg_name = arg_name) if (length(x) != 1L) { cli::cli_abort("{.var {arg_name}} must be a single string.") } invisible() -} +} # /rtemis.core::check_scalar_character # %% check_optional_scalar_character ---- @@ -596,17 +1380,18 @@ check_scalar_character <- function(x, arg_name) { #' @export #' #' @examples -#' check_optional_scalar_character(NULL, "my_arg") # Passes -#' check_optional_scalar_character("hello", "my_arg") # Passes +#' check_optional_scalar_character(NULL, "my_arg") +#' check_optional_scalar_character("hello", "my_arg") #' # Throw error: #' try(check_optional_scalar_character(c("hello", "world"), "my_arg")) #' try(check_optional_scalar_character(123, "my_arg")) -check_optional_scalar_character <- function(x, arg_name) { +check_optional_scalar_character <- function( + x, + arg_name = deparse(substitute(x)) +) { check_character(x, allow_null = TRUE, arg_name = arg_name) if (!is.null(x) && length(x) != 1L) { - cli::cli_abort( - "{.var {arg_name}} must be NULL or a single string." - ) + cli::cli_abort("{.var {arg_name}} must be NULL or a single string.") } invisible() -} +} # /rtemis.core::check_optional_scalar_character From 459677af1ebca18473fb21f8de56d0a4c1bbea7a Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:18:49 -0700 Subject: [PATCH 05/18] cleanup --- R/clean.R | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/R/clean.R b/R/clean.R index 806d00c..2248eff 100644 --- a/R/clean.R +++ b/R/clean.R @@ -12,9 +12,8 @@ #' otherwise an error is thrown. #' #' @param x Double or integer vector to check. -#' @param xname Character: Name of the variable for error messages. #' -#' @return Integer vector. +#' @return Integer vector #' @author EDG #' #' @export @@ -30,7 +29,8 @@ clean_int <- function(x, xname = deparse(substitute(x))) { return(x) } else if (is.numeric(x)) { if (all(x %% 1 == 0)) { - return(as.integer(x)) + storage.mode(x) <- "integer" + return(x) } else { cli::cli_abort("{.var {xname}} must be integer.") } @@ -38,7 +38,7 @@ clean_int <- function(x, xname = deparse(substitute(x))) { return(NULL) } cli::cli_abort("{.var {xname}} must be integer.") -} # /rtemis.core::clean_int +} # %% clean_posint ---- @@ -64,7 +64,7 @@ clean_posint <- function(x, allow_na = FALSE, xname = deparse(substitute(x))) { if (!allow_na && anyNA(x)) { cli::cli_abort("{.var {xname}} must not contain NAs.") } else { - x <- na.exclude(x) + x <- x[!is.na(x)] } if (any(x <= 0)) { @@ -72,7 +72,7 @@ clean_posint <- function(x, allow_na = FALSE, xname = deparse(substitute(x))) { } clean_int(x, xname = xname) -} # /rtemis.core::clean_posint +} #' Clean names From 1b2ebc3dbd5f4151bf0ca4e938737ee71a54b390 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:19:02 -0700 Subject: [PATCH 06/18] test S7 properties --- tests/testthat/test-00_S7_properties.R | 452 +++++++++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 tests/testthat/test-00_S7_properties.R diff --git a/tests/testthat/test-00_S7_properties.R b/tests/testthat/test-00_S7_properties.R new file mode 100644 index 0000000..621785f --- /dev/null +++ b/tests/testthat/test-00_S7_properties.R @@ -0,0 +1,452 @@ +# test-00_S7_properties.R + +library(S7) + +# Define one test class per property so S7 validation fires on construction. +.TC <- local({ + list( + chr = new_class("TC_chr", properties = list(x = character_scalar)), + opt_chr = new_class("TC_opt_chr", properties = list(x = optional_character_scalar)), + dbl = new_class("TC_dbl", properties = list(x = double_scalar)), + opt_dbl = new_class("TC_opt_dbl", properties = list(x = optional_double_scalar)), + int = new_class("TC_int", properties = list(x = integer_scalar)), + opt_int = new_class("TC_opt_int", properties = list(x = optional_integer_scalar)), + lgl = new_class("TC_lgl", properties = list(x = logical_scalar)), + opt_lgl = new_class("TC_opt_lgl", properties = list(x = optional_logical_scalar)), + prob = new_class("TC_prob", properties = list(x = prob_scalar)), + opt_prb = new_class("TC_opt_prb", properties = list(x = optional_prob_scalar)), + unit_o = new_class("TC_unit_o", properties = list(x = unit_open_scalar)), + pos_dbl = new_class("TC_pos_dbl", properties = list(x = pos_double_scalar)), + opt_pos = new_class("TC_opt_pos", properties = list(x = optional_pos_double_scalar)), + nng_dbl = new_class("TC_nng_dbl", properties = list(x = nonneg_double_scalar)), + opt_nng = new_class("TC_opt_nng", properties = list(x = optional_nonneg_double_scalar)), + pvec = new_class("TC_pvec", properties = list(x = prob_vector)), + opt_pvec = new_class("TC_opt_pvec", properties = list(x = optional_prob_vector)), + uovec = new_class("TC_uovec", properties = list(x = unit_open_vector)), + opt_uov = new_class("TC_opt_uov", properties = list(x = optional_unit_open_vector)), + posvec = new_class("TC_posvec", properties = list(x = pos_double_vector)), + opt_posv = new_class("TC_opt_posv", properties = list(x = optional_pos_double_vector)), + nngvec = new_class("TC_nngvec", properties = list(x = nonneg_double_vector)), + opt_nngv = new_class("TC_opt_nngv", properties = list(x = optional_nonneg_double_vector)) + ) +}) + +# character_scalar ---- +test_that("character_scalar accepts a non-empty string", { + expect_no_error(.TC$chr(x = "hello")) +}) + +test_that("character_scalar rejects empty string", { + expect_error(.TC$chr(x = "")) +}) + +test_that("character_scalar rejects whitespace-only string", { + expect_error(.TC$chr(x = " ")) +}) + +test_that("character_scalar rejects NA character", { + expect_error(.TC$chr(x = NA_character_)) +}) + +test_that("character_scalar rejects length > 1", { + expect_error(.TC$chr(x = c("a", "b"))) +}) + +# optional_character_scalar ---- +test_that("optional_character_scalar accepts NULL", { + expect_no_error(.TC$opt_chr(x = NULL)) +}) + +test_that("optional_character_scalar accepts a non-empty string", { + expect_no_error(.TC$opt_chr(x = "hello")) +}) + +test_that("optional_character_scalar rejects empty string", { + expect_error(.TC$opt_chr(x = "")) +}) + +test_that("optional_character_scalar rejects NA character", { + expect_error(.TC$opt_chr(x = NA_character_)) +}) + +# double_scalar ---- +test_that("double_scalar accepts a finite double", { + expect_no_error(.TC$dbl(x = 3.14)) +}) + +test_that("double_scalar accepts Inf", { + expect_no_error(.TC$dbl(x = Inf)) +}) + +test_that("double_scalar rejects NA", { + expect_error(.TC$dbl(x = NA_real_)) +}) + +test_that("double_scalar rejects length > 1", { + expect_error(.TC$dbl(x = c(1.0, 2.0))) +}) + +# optional_double_scalar ---- +test_that("optional_double_scalar accepts NULL", { + expect_no_error(.TC$opt_dbl(x = NULL)) +}) + +test_that("optional_double_scalar accepts a double", { + expect_no_error(.TC$opt_dbl(x = -1.5)) +}) + +test_that("optional_double_scalar rejects NA", { + expect_error(.TC$opt_dbl(x = NA_real_)) +}) + +# integer_scalar ---- +test_that("integer_scalar accepts an integer", { + expect_no_error(.TC$int(x = 5L)) +}) + +test_that("integer_scalar rejects NA_integer_", { + expect_error(.TC$int(x = NA_integer_)) +}) + +test_that("integer_scalar rejects a plain double", { + expect_error(.TC$int(x = 5.0)) +}) + +# optional_integer_scalar ---- +test_that("optional_integer_scalar accepts NULL", { + expect_no_error(.TC$opt_int(x = NULL)) +}) + +test_that("optional_integer_scalar accepts an integer", { + expect_no_error(.TC$opt_int(x = 2L)) +}) + +test_that("optional_integer_scalar rejects NA_integer_", { + expect_error(.TC$opt_int(x = NA_integer_)) +}) + +# logical_scalar ---- +test_that("logical_scalar accepts TRUE", { + expect_no_error(.TC$lgl(x = TRUE)) +}) + +test_that("logical_scalar accepts FALSE", { + expect_no_error(.TC$lgl(x = FALSE)) +}) + +test_that("logical_scalar rejects NA", { + expect_error(.TC$lgl(x = NA)) +}) + +test_that("logical_scalar rejects length > 1", { + expect_error(.TC$lgl(x = c(TRUE, FALSE))) +}) + +test_that("logical_scalar rejects integer 1L", { + expect_error(.TC$lgl(x = 1L)) +}) + +# optional_logical_scalar ---- +test_that("optional_logical_scalar accepts NULL", { + expect_no_error(.TC$opt_lgl(x = NULL)) +}) + +test_that("optional_logical_scalar accepts FALSE", { + expect_no_error(.TC$opt_lgl(x = FALSE)) +}) + +test_that("optional_logical_scalar rejects NA", { + expect_error(.TC$opt_lgl(x = NA)) +}) + +# prob_scalar ---- +test_that("prob_scalar accepts 0", { + expect_no_error(.TC$prob(x = 0)) +}) + +test_that("prob_scalar accepts 0.5", { + expect_no_error(.TC$prob(x = 0.5)) +}) + +test_that("prob_scalar accepts 1", { + expect_no_error(.TC$prob(x = 1)) +}) + +test_that("prob_scalar rejects value > 1", { + expect_error(.TC$prob(x = 1.01)) +}) + +test_that("prob_scalar rejects negative value", { + expect_error(.TC$prob(x = -0.01)) +}) + +test_that("prob_scalar rejects NA", { + expect_error(.TC$prob(x = NA_real_)) +}) + +# optional_prob_scalar ---- +test_that("optional_prob_scalar accepts NULL", { + expect_no_error(.TC$opt_prb(x = NULL)) +}) + +test_that("optional_prob_scalar accepts 0.5", { + expect_no_error(.TC$opt_prb(x = 0.5)) +}) + +test_that("optional_prob_scalar rejects value > 1", { + expect_error(.TC$opt_prb(x = 2.0)) +}) + +# unit_open_scalar ---- +test_that("unit_open_scalar accepts strictly interior value", { + expect_no_error(.TC$unit_o(x = 0.5)) +}) + +test_that("unit_open_scalar rejects 0", { + expect_error(.TC$unit_o(x = 0)) +}) + +test_that("unit_open_scalar rejects 1", { + expect_error(.TC$unit_o(x = 1)) +}) + +test_that("unit_open_scalar rejects value outside (0, 1)", { + expect_error(.TC$unit_o(x = 1.5)) +}) + +# pos_double_scalar ---- +test_that("pos_double_scalar accepts positive value", { + expect_no_error(.TC$pos_dbl(x = 0.001)) +}) + +test_that("pos_double_scalar rejects 0", { + expect_error(.TC$pos_dbl(x = 0)) +}) + +test_that("pos_double_scalar rejects negative value", { + expect_error(.TC$pos_dbl(x = -1.0)) +}) + +test_that("pos_double_scalar rejects Inf", { + expect_error(.TC$pos_dbl(x = Inf)) +}) + +# optional_pos_double_scalar ---- +test_that("optional_pos_double_scalar accepts NULL", { + expect_no_error(.TC$opt_pos(x = NULL)) +}) + +test_that("optional_pos_double_scalar accepts positive value", { + expect_no_error(.TC$opt_pos(x = 2.5)) +}) + +test_that("optional_pos_double_scalar rejects 0", { + expect_error(.TC$opt_pos(x = 0)) +}) + +# nonneg_double_scalar ---- +test_that("nonneg_double_scalar accepts 0", { + expect_no_error(.TC$nng_dbl(x = 0)) +}) + +test_that("nonneg_double_scalar accepts positive value", { + expect_no_error(.TC$nng_dbl(x = 5.0)) +}) + +test_that("nonneg_double_scalar rejects negative value", { + expect_error(.TC$nng_dbl(x = -0.001)) +}) + +test_that("nonneg_double_scalar rejects Inf", { + expect_error(.TC$nng_dbl(x = Inf)) +}) + +# optional_nonneg_double_scalar ---- +test_that("optional_nonneg_double_scalar accepts NULL", { + expect_no_error(.TC$opt_nng(x = NULL)) +}) + +test_that("optional_nonneg_double_scalar accepts 0", { + expect_no_error(.TC$opt_nng(x = 0)) +}) + +test_that("optional_nonneg_double_scalar rejects negative value", { + expect_error(.TC$opt_nng(x = -1.0)) +}) + +# bounded_double_property ---- +test_that("bounded_double_property closed interval accepts endpoints", { + TC <- new_class("TC_bnd1", properties = list(x = bounded_double_property(0, 10))) + expect_no_error(TC(x = 0)) + expect_no_error(TC(x = 10)) + expect_no_error(TC(x = 5)) +}) + +test_that("bounded_double_property closed interval rejects outside", { + TC <- new_class("TC_bnd2", properties = list(x = bounded_double_property(0, 10))) + expect_error(TC(x = -0.1)) + expect_error(TC(x = 10.1)) +}) + +test_that("bounded_double_property lower_open rejects lower endpoint", { + TC <- new_class("TC_bnd3", properties = list(x = bounded_double_property(0, 1, lower_open = TRUE))) + expect_error(TC(x = 0)) + expect_no_error(TC(x = 0.001)) + expect_no_error(TC(x = 1)) +}) + +test_that("bounded_double_property upper_open rejects upper endpoint", { + TC <- new_class("TC_bnd4", properties = list(x = bounded_double_property(0, 1, upper_open = TRUE))) + expect_no_error(TC(x = 0)) + expect_error(TC(x = 1)) +}) + +test_that("bounded_double_property nullable accepts NULL", { + TC <- new_class("TC_bnd5", properties = list(x = bounded_double_property(0, 1, nullable = TRUE))) + expect_no_error(TC(x = NULL)) + expect_no_error(TC(x = 0.5)) + expect_error(TC(x = 2.0)) +}) + +test_that("bounded_double_property rejects Inf", { + TC <- new_class("TC_bnd6", properties = list(x = bounded_double_property(0, 100))) + expect_error(TC(x = Inf)) +}) + +# prob_vector ---- +test_that("prob_vector accepts valid vector", { + expect_no_error(.TC$pvec(x = c(0, 0.5, 1))) +}) + +test_that("prob_vector rejects empty vector", { + expect_error(.TC$pvec(x = double(0))) +}) + +test_that("prob_vector rejects NA", { + expect_error(.TC$pvec(x = c(0.5, NA))) +}) + +test_that("prob_vector rejects out-of-range values", { + expect_error(.TC$pvec(x = c(0.5, 1.5))) + expect_error(.TC$pvec(x = c(-0.1, 0.5))) +}) + +# optional_prob_vector ---- +test_that("optional_prob_vector accepts NULL", { + expect_no_error(.TC$opt_pvec(x = NULL)) +}) + +test_that("optional_prob_vector accepts valid vector", { + expect_no_error(.TC$opt_pvec(x = c(0.2, 0.8))) +}) + +test_that("optional_prob_vector rejects out-of-range values", { + expect_error(.TC$opt_pvec(x = c(0.5, 2))) +}) + +# unit_open_vector ---- +test_that("unit_open_vector accepts strictly interior values", { + expect_no_error(.TC$uovec(x = c(0.1, 0.5, 0.9))) +}) + +test_that("unit_open_vector rejects 0", { + expect_error(.TC$uovec(x = c(0, 0.5))) +}) + +test_that("unit_open_vector rejects 1", { + expect_error(.TC$uovec(x = c(0.5, 1))) +}) + +test_that("unit_open_vector rejects empty vector", { + expect_error(.TC$uovec(x = double(0))) +}) + +test_that("unit_open_vector rejects NA", { + expect_error(.TC$uovec(x = c(0.5, NA))) +}) + +# optional_unit_open_vector ---- +test_that("optional_unit_open_vector accepts NULL", { + expect_no_error(.TC$opt_uov(x = NULL)) +}) + +test_that("optional_unit_open_vector accepts valid vector", { + expect_no_error(.TC$opt_uov(x = c(0.2, 0.8))) +}) + +test_that("optional_unit_open_vector rejects endpoints", { + expect_error(.TC$opt_uov(x = c(0, 0.5))) + expect_error(.TC$opt_uov(x = c(0.5, 1))) +}) + +# pos_double_vector ---- +test_that("pos_double_vector accepts positive values", { + expect_no_error(.TC$posvec(x = c(0.001, 1, 100))) +}) + +test_that("pos_double_vector rejects 0", { + expect_error(.TC$posvec(x = c(0, 1))) +}) + +test_that("pos_double_vector rejects negative values", { + expect_error(.TC$posvec(x = c(-1, 1))) +}) + +test_that("pos_double_vector rejects Inf", { + expect_error(.TC$posvec(x = c(1, Inf))) +}) + +test_that("pos_double_vector rejects empty vector", { + expect_error(.TC$posvec(x = double(0))) +}) + +test_that("pos_double_vector rejects NA", { + expect_error(.TC$posvec(x = c(1, NA))) +}) + +# optional_pos_double_vector ---- +test_that("optional_pos_double_vector accepts NULL", { + expect_no_error(.TC$opt_posv(x = NULL)) +}) + +test_that("optional_pos_double_vector accepts positive values", { + expect_no_error(.TC$opt_posv(x = c(0.5, 2))) +}) + +test_that("optional_pos_double_vector rejects 0", { + expect_error(.TC$opt_posv(x = c(0, 1))) +}) + +# nonneg_double_vector ---- +test_that("nonneg_double_vector accepts 0 and positive values", { + expect_no_error(.TC$nngvec(x = c(0, 1, 2.5))) +}) + +test_that("nonneg_double_vector rejects negative values", { + expect_error(.TC$nngvec(x = c(-1, 0, 1))) +}) + +test_that("nonneg_double_vector rejects Inf", { + expect_error(.TC$nngvec(x = c(1, Inf))) +}) + +test_that("nonneg_double_vector rejects empty vector", { + expect_error(.TC$nngvec(x = double(0))) +}) + +test_that("nonneg_double_vector rejects NA", { + expect_error(.TC$nngvec(x = c(0, NA))) +}) + +# optional_nonneg_double_vector ---- +test_that("optional_nonneg_double_vector accepts NULL", { + expect_no_error(.TC$opt_nngv(x = NULL)) +}) + +test_that("optional_nonneg_double_vector accepts 0 and positive values", { + expect_no_error(.TC$opt_nngv(x = c(0, 1, 5))) +}) + +test_that("optional_nonneg_double_vector rejects negative values", { + expect_error(.TC$opt_nngv(x = c(-1, 0))) +}) From 50d85dbb61d5b361fdec08d525a8c254ce8734ab Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:19:33 -0700 Subject: [PATCH 07/18] move types.R to 00_S7_properties.R --- R/types.R | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 R/types.R diff --git a/R/types.R b/R/types.R deleted file mode 100644 index f232397..0000000 --- a/R/types.R +++ /dev/null @@ -1,22 +0,0 @@ -# 2026- EDG rtemis.org - -# %% optional ---- -#' Create an optional S7 type -#' -#' Creates an S7 union type that allows for the specified type or `NULL`. -#' -#' @param type S7 base class or S7 class. -#' @return An S7 union type that allows for the specified type or `NULL`. -#' @author EDG -#' @export -#' @examples -#' # Create an optional character type -#' optional(S7::class_character) -optional <- function(type) { - if (!inherits(type, "S7_base_class") && !inherits(type, "S7_class")) { - cli::cli_abort( - "{.var type} must be an S7 base class or S7 class." - ) - } - S7::new_union(type, NULL) -} From 614b7549b245e4d9c772c6d02b72679915bc25cc Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:19:45 -0700 Subject: [PATCH 08/18] add tests for new check functions --- tests/testthat/test-check.R | 172 ++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/tests/testthat/test-check.R b/tests/testthat/test-check.R index 6ebf804..85ee24e 100644 --- a/tests/testthat/test-check.R +++ b/tests/testthat/test-check.R @@ -180,3 +180,175 @@ test_that("check_tabular errors for non-tabular objects", { expect_error(check_tabular(list(a = 1))) expect_error(check_tabular(matrix(1:4, 2, 2))) }) + +# check_prob_vector ---- +test_that("check_prob_vector passes for valid vector", { + expect_invisible(check_prob_vector(c(0, 0.5, 1))) +}) + +test_that("check_prob_vector rejects empty vector", { + expect_error(check_prob_vector(double(0))) +}) + +test_that("check_prob_vector rejects NA", { + expect_error(check_prob_vector(c(0.5, NA))) +}) + +test_that("check_prob_vector rejects out-of-range values", { + expect_error(check_prob_vector(c(0.5, 1.5))) + expect_error(check_prob_vector(c(-0.1, 0.5))) +}) + +test_that("check_prob_vector rejects non-numeric", { + expect_error(check_prob_vector("0.5")) +}) + +test_that("check_optional_prob_vector accepts NULL", { + expect_invisible(check_optional_prob_vector(NULL)) +}) + +test_that("check_optional_prob_vector passes for valid vector", { + expect_invisible(check_optional_prob_vector(c(0.2, 0.8))) +}) + +test_that("check_optional_prob_vector rejects out-of-range values", { + expect_error(check_optional_prob_vector(c(0.5, 2))) +}) + +# check_unit_open_vector ---- +test_that("check_unit_open_vector passes for strictly interior values", { + expect_invisible(check_unit_open_vector(c(0.1, 0.5, 0.9))) +}) + +test_that("check_unit_open_vector rejects 0", { + expect_error(check_unit_open_vector(c(0, 0.5))) +}) + +test_that("check_unit_open_vector rejects 1", { + expect_error(check_unit_open_vector(c(0.5, 1))) +}) + +test_that("check_unit_open_vector rejects empty vector", { + expect_error(check_unit_open_vector(double(0))) +}) + +test_that("check_unit_open_vector rejects NA", { + expect_error(check_unit_open_vector(c(0.5, NA))) +}) + +test_that("check_optional_unit_open_vector accepts NULL", { + expect_invisible(check_optional_unit_open_vector(NULL)) +}) + +test_that("check_optional_unit_open_vector passes for valid vector", { + expect_invisible(check_optional_unit_open_vector(c(0.2, 0.8))) +}) + +test_that("check_optional_unit_open_vector rejects endpoints", { + expect_error(check_optional_unit_open_vector(c(0, 0.5))) + expect_error(check_optional_unit_open_vector(c(0.5, 1))) +}) + +# check_pos_double_vector ---- +test_that("check_pos_double_vector passes for positive values", { + expect_invisible(check_pos_double_vector(c(0.001, 1, 100))) +}) + +test_that("check_pos_double_vector rejects 0", { + expect_error(check_pos_double_vector(c(0, 1))) +}) + +test_that("check_pos_double_vector rejects negative values", { + expect_error(check_pos_double_vector(c(-1, 1))) +}) + +test_that("check_pos_double_vector rejects Inf", { + expect_error(check_pos_double_vector(c(1, Inf))) +}) + +test_that("check_pos_double_vector rejects empty vector", { + expect_error(check_pos_double_vector(double(0))) +}) + +test_that("check_pos_double_vector rejects NA", { + expect_error(check_pos_double_vector(c(1, NA))) +}) + +test_that("check_optional_pos_double_vector accepts NULL", { + expect_invisible(check_optional_pos_double_vector(NULL)) +}) + +test_that("check_optional_pos_double_vector passes for positive values", { + expect_invisible(check_optional_pos_double_vector(c(0.5, 2))) +}) + +test_that("check_optional_pos_double_vector rejects 0", { + expect_error(check_optional_pos_double_vector(c(0, 1))) +}) + +# check_nonneg_double_vector ---- +test_that("check_nonneg_double_vector passes for 0 and positive values", { + expect_invisible(check_nonneg_double_vector(c(0, 1, 2.5))) +}) + +test_that("check_nonneg_double_vector rejects negative values", { + expect_error(check_nonneg_double_vector(c(-1, 0, 1))) +}) + +test_that("check_nonneg_double_vector rejects Inf", { + expect_error(check_nonneg_double_vector(c(1, Inf))) +}) + +test_that("check_nonneg_double_vector rejects empty vector", { + expect_error(check_nonneg_double_vector(double(0))) +}) + +test_that("check_nonneg_double_vector rejects NA", { + expect_error(check_nonneg_double_vector(c(0, NA))) +}) + +test_that("check_optional_nonneg_double_vector accepts NULL", { + expect_invisible(check_optional_nonneg_double_vector(NULL)) +}) + +test_that("check_optional_nonneg_double_vector passes for valid values", { + expect_invisible(check_optional_nonneg_double_vector(c(0, 1, 5))) +}) + +test_that("check_optional_nonneg_double_vector rejects negative values", { + expect_error(check_optional_nonneg_double_vector(c(-1, 0))) +}) + +# check_pos_integer_scalar ---- +test_that("check_pos_integer_scalar passes for positive integers", { + expect_invisible(check_pos_integer_scalar(1L)) + expect_invisible(check_pos_integer_scalar(10)) +}) + +test_that("check_pos_integer_scalar rejects 0", { + expect_error(check_pos_integer_scalar(0L)) +}) + +test_that("check_pos_integer_scalar rejects negative values", { + expect_error(check_pos_integer_scalar(-1L)) +}) + +test_that("check_pos_integer_scalar rejects non-whole numbers", { + expect_error(check_pos_integer_scalar(1.5)) +}) + +test_that("check_pos_integer_scalar rejects NA", { + expect_error(check_pos_integer_scalar(NA_integer_)) +}) + +test_that("check_optional_pos_integer_scalar accepts NULL", { + expect_invisible(check_optional_pos_integer_scalar(NULL)) +}) + +test_that("check_optional_pos_integer_scalar passes for valid values", { + expect_invisible(check_optional_pos_integer_scalar(5L)) +}) + +test_that("check_optional_pos_integer_scalar rejects 0", { + expect_error(check_optional_pos_integer_scalar(0L)) +}) From af1d029fec782aa5dae7747a7c372eccc0a14685 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:20:07 -0700 Subject: [PATCH 09/18] add exports --- NAMESPACE | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index 26fe792..e2e2951 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,9 +3,13 @@ export(abbreviate_class) export(ansi256_to_hex) export(bold) +export(bounded_double_property) +export(character_scalar) export(check_character) +export(check_character_scalar) export(check_data.table) export(check_dependencies) +export(check_double_scalar) export(check_enum) export(check_float01exc) export(check_float01inc) @@ -14,36 +18,85 @@ export(check_float_neg1_1) export(check_floatpos) export(check_floatpos1) export(check_inherits) +export(check_integer_scalar) export(check_logical) +export(check_logical_scalar) +export(check_nonneg_double_scalar) +export(check_nonneg_double_vector) +export(check_optional_character_scalar) +export(check_optional_double_scalar) +export(check_optional_integer_scalar) +export(check_optional_logical_scalar) +export(check_optional_nonneg_double_scalar) +export(check_optional_nonneg_double_vector) +export(check_optional_pos_double_scalar) +export(check_optional_pos_double_vector) +export(check_optional_pos_integer_scalar) +export(check_optional_prob_scalar) +export(check_optional_prob_vector) export(check_optional_scalar_character) +export(check_optional_unit_open_vector) +export(check_pos_double_scalar) +export(check_pos_double_vector) +export(check_pos_integer_scalar) +export(check_prob_scalar) +export(check_prob_vector) export(check_scalar_character) export(check_scalar_logical) export(check_tabular) +export(check_unit_open_scalar) +export(check_unit_open_vector) export(clean_colnames) export(clean_int) export(clean_names) export(clean_posint) export(col256) export(ddSci) +export(double_scalar) +export(enum) export(fmt) export(fmt_gradient) export(get_output_type) export(highlight) +export(integer_scalar) export(labelify) +export(logical_scalar) export(match_arg) export(msg) export(msg0) export(msgdone) export(msgstart) +export(nonneg_double_scalar) +export(nonneg_double_vector) export(optional) +export(optional_character_scalar) +export(optional_double_scalar) +export(optional_integer_scalar) +export(optional_logical_scalar) +export(optional_nonneg_double_scalar) +export(optional_nonneg_double_vector) +export(optional_pos_double_scalar) +export(optional_pos_double_vector) +export(optional_pos_integer_scalar) +export(optional_prob_scalar) +export(optional_prob_vector) +export(optional_unit_open_scalar) +export(optional_unit_open_vector) export(plain) +export(pos_double_scalar) +export(pos_double_vector) +export(pos_integer_scalar) export(printdf) export(printls) +export(prob_scalar) +export(prob_vector) export(repr) export(repr_S7name) export(repr_ls) export(rtemis_colors) export(test_inherits) +export(unit_open_scalar) +export(unit_open_vector) import(S7) import(data.table) import(grDevices) From 32d5ef3d0df5ff99583e9f84d503414a83c0e1d8 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:20:41 -0700 Subject: [PATCH 10/18] up --- NEWS.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 NEWS.md diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..b929652 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,11 @@ +# rtemis.core NEWS + +## Version 0.0.4 + +- Added S7 property library +- Expanded check_ function set +- Updated rtemis_colors + +## Version 0.0.3 + +- Initial CRAN release From 06a1b0ef69e67a2ae5a5e09150a084ca844957e4 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:20:49 -0700 Subject: [PATCH 11/18] docs --- DESCRIPTION | 12 +++---- man/bounded_double_property.Rd | 41 ++++++++++++++++++++++ man/character_scalar.Rd | 22 ++++++++++++ man/check_character_scalar.Rd | 29 +++++++++++++++ man/check_double_scalar.Rd | 29 +++++++++++++++ man/check_integer_scalar.Rd | 33 +++++++++++++++++ man/check_logical_scalar.Rd | 30 ++++++++++++++++ man/check_nonneg_double_scalar.Rd | 29 +++++++++++++++ man/check_nonneg_double_vector.Rd | 29 +++++++++++++++ man/check_optional_character_scalar.Rd | 29 +++++++++++++++ man/check_optional_double_scalar.Rd | 28 +++++++++++++++ man/check_optional_integer_scalar.Rd | 28 +++++++++++++++ man/check_optional_logical_scalar.Rd | 28 +++++++++++++++ man/check_optional_nonneg_double_scalar.Rd | 29 +++++++++++++++ man/check_optional_nonneg_double_vector.Rd | 29 +++++++++++++++ man/check_optional_pos_double_scalar.Rd | 29 +++++++++++++++ man/check_optional_pos_double_vector.Rd | 29 +++++++++++++++ man/check_optional_pos_integer_scalar.Rd | 29 +++++++++++++++ man/check_optional_prob_scalar.Rd | 28 +++++++++++++++ man/check_optional_prob_vector.Rd | 29 +++++++++++++++ man/check_optional_scalar_character.Rd | 6 ++-- man/check_optional_unit_open_vector.Rd | 29 +++++++++++++++ man/check_pos_double_scalar.Rd | 30 ++++++++++++++++ man/check_pos_double_vector.Rd | 29 +++++++++++++++ man/check_pos_integer_scalar.Rd | 35 ++++++++++++++++++ man/check_prob_scalar.Rd | 30 ++++++++++++++++ man/check_prob_vector.Rd | 29 +++++++++++++++ man/check_scalar_character.Rd | 4 +-- man/check_scalar_logical.Rd | 6 ++-- man/check_unit_open_scalar.Rd | 28 +++++++++++++++ man/check_unit_open_vector.Rd | 29 +++++++++++++++ man/clean_int.Rd | 4 +-- man/double_scalar.Rd | 22 ++++++++++++ man/enum.Rd | 27 ++++++++++++++ man/integer_scalar.Rd | 22 ++++++++++++ man/logical_scalar.Rd | 22 ++++++++++++ man/nonneg_double_scalar.Rd | 22 ++++++++++++ man/nonneg_double_vector.Rd | 23 ++++++++++++ man/optional.Rd | 9 +++-- man/optional_character_scalar.Rd | 22 ++++++++++++ man/optional_double_scalar.Rd | 22 ++++++++++++ man/optional_integer_scalar.Rd | 22 ++++++++++++ man/optional_logical_scalar.Rd | 22 ++++++++++++ man/optional_nonneg_double_scalar.Rd | 22 ++++++++++++ man/optional_nonneg_double_vector.Rd | 23 ++++++++++++ man/optional_pos_double_scalar.Rd | 22 ++++++++++++ man/optional_pos_double_vector.Rd | 23 ++++++++++++ man/optional_pos_integer_scalar.Rd | 22 ++++++++++++ man/optional_prob_scalar.Rd | 22 ++++++++++++ man/optional_prob_vector.Rd | 23 ++++++++++++ man/optional_unit_open_scalar.Rd | 22 ++++++++++++ man/optional_unit_open_vector.Rd | 23 ++++++++++++ man/pos_double_scalar.Rd | 22 ++++++++++++ man/pos_double_vector.Rd | 23 ++++++++++++ man/pos_integer_scalar.Rd | 22 ++++++++++++ man/prob_scalar.Rd | 22 ++++++++++++ man/prob_vector.Rd | 22 ++++++++++++ man/repr.Rd | 2 +- man/unit_open_scalar.Rd | 22 ++++++++++++ man/unit_open_vector.Rd | 23 ++++++++++++ 60 files changed, 1402 insertions(+), 21 deletions(-) create mode 100644 man/bounded_double_property.Rd create mode 100644 man/character_scalar.Rd create mode 100644 man/check_character_scalar.Rd create mode 100644 man/check_double_scalar.Rd create mode 100644 man/check_integer_scalar.Rd create mode 100644 man/check_logical_scalar.Rd create mode 100644 man/check_nonneg_double_scalar.Rd create mode 100644 man/check_nonneg_double_vector.Rd create mode 100644 man/check_optional_character_scalar.Rd create mode 100644 man/check_optional_double_scalar.Rd create mode 100644 man/check_optional_integer_scalar.Rd create mode 100644 man/check_optional_logical_scalar.Rd create mode 100644 man/check_optional_nonneg_double_scalar.Rd create mode 100644 man/check_optional_nonneg_double_vector.Rd create mode 100644 man/check_optional_pos_double_scalar.Rd create mode 100644 man/check_optional_pos_double_vector.Rd create mode 100644 man/check_optional_pos_integer_scalar.Rd create mode 100644 man/check_optional_prob_scalar.Rd create mode 100644 man/check_optional_prob_vector.Rd create mode 100644 man/check_optional_unit_open_vector.Rd create mode 100644 man/check_pos_double_scalar.Rd create mode 100644 man/check_pos_double_vector.Rd create mode 100644 man/check_pos_integer_scalar.Rd create mode 100644 man/check_prob_scalar.Rd create mode 100644 man/check_prob_vector.Rd create mode 100644 man/check_unit_open_scalar.Rd create mode 100644 man/check_unit_open_vector.Rd create mode 100644 man/double_scalar.Rd create mode 100644 man/enum.Rd create mode 100644 man/integer_scalar.Rd create mode 100644 man/logical_scalar.Rd create mode 100644 man/nonneg_double_scalar.Rd create mode 100644 man/nonneg_double_vector.Rd create mode 100644 man/optional_character_scalar.Rd create mode 100644 man/optional_double_scalar.Rd create mode 100644 man/optional_integer_scalar.Rd create mode 100644 man/optional_logical_scalar.Rd create mode 100644 man/optional_nonneg_double_scalar.Rd create mode 100644 man/optional_nonneg_double_vector.Rd create mode 100644 man/optional_pos_double_scalar.Rd create mode 100644 man/optional_pos_double_vector.Rd create mode 100644 man/optional_pos_integer_scalar.Rd create mode 100644 man/optional_prob_scalar.Rd create mode 100644 man/optional_prob_vector.Rd create mode 100644 man/optional_unit_open_scalar.Rd create mode 100644 man/optional_unit_open_vector.Rd create mode 100644 man/pos_double_scalar.Rd create mode 100644 man/pos_double_vector.Rd create mode 100644 man/pos_integer_scalar.Rd create mode 100644 man/prob_scalar.Rd create mode 100644 man/prob_vector.Rd create mode 100644 man/unit_open_scalar.Rd create mode 100644 man/unit_open_vector.Rd diff --git a/DESCRIPTION b/DESCRIPTION index c1250fb..e187dc1 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,14 +1,14 @@ Package: rtemis.core Title: Core Utilities for the 'rtemis' Ecosystem -Version: 0.0.3 -Date: 2026-04-19 +Version: 0.1.0 +Date: 2026-04-29 Authors@R: person(given = "E.D.", family = "Gennatas", role = c("aut", "cre", "cph"), email = "gennatas@gmail.com", comment = c(ORCID = "0000-0001-9280-3609")) Description: Utilities used across packages of the 'rtemis' ecosystem. Includes the msg() - messaging system and the fmt() formatting system. Provides test_* functions that return logical - values, check_* functions that throw informative errors, and clean_* functions that return - validated and coerced values. This code began as part of the 'rtemis' package - (). + messaging system and the fmt() formatting system. Provides a library of 'S7' properties, + test_* functions that return logical values, check_* functions that throw informative errors, + and clean_* functions that return validated and coerced values. This code began as part of the + 'rtemis' package (). License: GPL (>= 3) URL: https://www.rtemis.org, https://github.com/rtemis-org/rtemis.core BugReports: https://github.com/rtemis-org/rtemis.core/issues diff --git a/man/bounded_double_property.Rd b/man/bounded_double_property.Rd new file mode 100644 index 0000000..e552dba --- /dev/null +++ b/man/bounded_double_property.Rd @@ -0,0 +1,41 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\name{bounded_double_property} +\alias{bounded_double_property} +\title{Create a bounded double S7 property} +\usage{ +bounded_double_property( + lower = -Inf, + upper = Inf, + lower_open = FALSE, + upper_open = FALSE, + nullable = FALSE +) +} +\arguments{ +\item{lower}{Numeric scalar. Lower bound. Default \code{-Inf}.} + +\item{upper}{Numeric scalar. Upper bound. Default \code{Inf}.} + +\item{lower_open}{Logical scalar. If \code{TRUE}, lower bound is exclusive \verb{(lower, ...]}. +Default \code{FALSE}.} + +\item{upper_open}{Logical scalar. If \code{TRUE}, upper bound is exclusive \verb{[..., upper)}. +Default \code{FALSE}.} + +\item{nullable}{Logical scalar. If \code{TRUE}, \code{NULL} is also accepted. Default \code{FALSE}.} +} +\value{ +An S7 property object. +} +\description{ +Returns a \code{new_property()} for a double scalar constrained to a given interval. +Useful for bounds not covered by the pre-built properties. +} +\examples{ +# Learning rate in (0, 1] +lr_prop <- bounded_double_property(0, 1, lower_open = TRUE) +} +\author{ +EDG +} diff --git a/man/character_scalar.Rd b/man/character_scalar.Rd new file mode 100644 index 0000000..6406f19 --- /dev/null +++ b/man/character_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{character_scalar} +\alias{character_scalar} +\title{Non-empty character scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +character_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single non-NA, non-empty (after trimming whitespace) string. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/check_character_scalar.Rd b/man/check_character_scalar.Rd new file mode 100644 index 0000000..17ff9b9 --- /dev/null +++ b/man/check_character_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_character_scalar} +\alias{check_character_scalar} +\title{Check character scalar} +\usage{ +check_character_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Character: Value to check. Must be a single non-NA, non-empty string.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check character scalar +} +\examples{ +check_character_scalar("hello") +# Throw error: +try(check_character_scalar("")) +try(check_character_scalar(NA_character_)) +try(check_character_scalar(c("a", "b"))) +} +\author{ +EDG +} diff --git a/man/check_double_scalar.Rd b/man/check_double_scalar.Rd new file mode 100644 index 0000000..79e7e84 --- /dev/null +++ b/man/check_double_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_double_scalar} +\alias{check_double_scalar} +\title{Check double scalar} +\usage{ +check_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single non-NA number (integer inputs are accepted).} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check double scalar +} +\examples{ +check_double_scalar(3.14) +check_double_scalar(1L) +# Throw error: +try(check_double_scalar(NA_real_)) +try(check_double_scalar(c(1.0, 2.0))) +} +\author{ +EDG +} diff --git a/man/check_integer_scalar.Rd b/man/check_integer_scalar.Rd new file mode 100644 index 0000000..634afad --- /dev/null +++ b/man/check_integer_scalar.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_integer_scalar} +\alias{check_integer_scalar} +\title{Check integer scalar} +\usage{ +check_integer_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single non-NA whole number.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check integer scalar +} +\details{ +Accepts any single numeric value that is a whole number. Integer-typed inputs (\code{1L}) and +double-typed whole numbers (\code{1}, \code{100}) are both accepted for user convenience. +} +\examples{ +check_integer_scalar(5L) +check_integer_scalar(100) +# Throw error: +try(check_integer_scalar(1.5)) +try(check_integer_scalar(NA_integer_)) +} +\author{ +EDG +} diff --git a/man/check_logical_scalar.Rd b/man/check_logical_scalar.Rd new file mode 100644 index 0000000..42fe2e7 --- /dev/null +++ b/man/check_logical_scalar.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_logical_scalar} +\alias{check_logical_scalar} +\title{Check logical scalar} +\usage{ +check_logical_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Logical: Value to check. Must be a single non-NA \code{TRUE} or \code{FALSE}.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check logical scalar +} +\examples{ +check_logical_scalar(TRUE) +check_logical_scalar(FALSE) +# Throw error: +try(check_logical_scalar(NA)) +try(check_logical_scalar(1L)) +try(check_logical_scalar(c(TRUE, FALSE))) +} +\author{ +EDG +} diff --git a/man/check_nonneg_double_scalar.Rd b/man/check_nonneg_double_scalar.Rd new file mode 100644 index 0000000..4b40df3 --- /dev/null +++ b/man/check_nonneg_double_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_nonneg_double_scalar} +\alias{check_nonneg_double_scalar} +\title{Check non-negative double scalar} +\usage{ +check_nonneg_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single finite number greater than or equal to zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check non-negative double scalar +} +\examples{ +check_nonneg_double_scalar(0) +check_nonneg_double_scalar(5) +# Throw error: +try(check_nonneg_double_scalar(-0.001)) +try(check_nonneg_double_scalar(Inf)) +} +\author{ +EDG +} diff --git a/man/check_nonneg_double_vector.Rd b/man/check_nonneg_double_vector.Rd new file mode 100644 index 0000000..a1ce661 --- /dev/null +++ b/man/check_nonneg_double_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_nonneg_double_vector} +\alias{check_nonneg_double_vector} +\title{Check non-negative double vector} +\usage{ +check_nonneg_double_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a non-empty vector with all elements finite, +greater than or equal to zero, and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check non-negative double vector +} +\examples{ +check_nonneg_double_vector(c(0, 1, 2.5)) +# Throw error: +try(check_nonneg_double_vector(c(-1, 0, 1))) +try(check_nonneg_double_vector(c(1, Inf))) +} +\author{ +EDG +} diff --git a/man/check_optional_character_scalar.Rd b/man/check_optional_character_scalar.Rd new file mode 100644 index 0000000..e03821e --- /dev/null +++ b/man/check_optional_character_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_character_scalar} +\alias{check_optional_character_scalar} +\title{Check optional character scalar} +\usage{ +check_optional_character_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Character: Value to check. Must be \code{NULL} or a single non-NA, non-empty string.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional character scalar +} +\examples{ +check_optional_character_scalar(NULL) +check_optional_character_scalar("hello") +# Throw error: +try(check_optional_character_scalar("")) +try(check_optional_character_scalar(c("a", "b"))) +} +\author{ +EDG +} diff --git a/man/check_optional_double_scalar.Rd b/man/check_optional_double_scalar.Rd new file mode 100644 index 0000000..0593e71 --- /dev/null +++ b/man/check_optional_double_scalar.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_double_scalar} +\alias{check_optional_double_scalar} +\title{Check optional double scalar} +\usage{ +check_optional_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single non-NA number.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional double scalar +} +\examples{ +check_optional_double_scalar(NULL) +check_optional_double_scalar(2.5) +# Throw error: +try(check_optional_double_scalar(NA_real_)) +} +\author{ +EDG +} diff --git a/man/check_optional_integer_scalar.Rd b/man/check_optional_integer_scalar.Rd new file mode 100644 index 0000000..6be7014 --- /dev/null +++ b/man/check_optional_integer_scalar.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_integer_scalar} +\alias{check_optional_integer_scalar} +\title{Check optional integer scalar} +\usage{ +check_optional_integer_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single non-NA whole number.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional integer scalar +} +\examples{ +check_optional_integer_scalar(NULL) +check_optional_integer_scalar(10L) +# Throw error: +try(check_optional_integer_scalar(1.5)) +} +\author{ +EDG +} diff --git a/man/check_optional_logical_scalar.Rd b/man/check_optional_logical_scalar.Rd new file mode 100644 index 0000000..6fe96f1 --- /dev/null +++ b/man/check_optional_logical_scalar.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_logical_scalar} +\alias{check_optional_logical_scalar} +\title{Check optional logical scalar} +\usage{ +check_optional_logical_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Logical: Value to check. Must be \code{NULL} or a single non-NA \code{TRUE} or \code{FALSE}.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional logical scalar +} +\examples{ +check_optional_logical_scalar(NULL) +check_optional_logical_scalar(FALSE) +# Throw error: +try(check_optional_logical_scalar(NA)) +} +\author{ +EDG +} diff --git a/man/check_optional_nonneg_double_scalar.Rd b/man/check_optional_nonneg_double_scalar.Rd new file mode 100644 index 0000000..72c800c --- /dev/null +++ b/man/check_optional_nonneg_double_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_nonneg_double_scalar} +\alias{check_optional_nonneg_double_scalar} +\title{Check optional non-negative double scalar} +\usage{ +check_optional_nonneg_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single finite number +greater than or equal to zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional non-negative double scalar +} +\examples{ +check_optional_nonneg_double_scalar(NULL) +check_optional_nonneg_double_scalar(0) +# Throw error: +try(check_optional_nonneg_double_scalar(-1)) +} +\author{ +EDG +} diff --git a/man/check_optional_nonneg_double_vector.Rd b/man/check_optional_nonneg_double_vector.Rd new file mode 100644 index 0000000..3d39e87 --- /dev/null +++ b/man/check_optional_nonneg_double_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_nonneg_double_vector} +\alias{check_optional_nonneg_double_vector} +\title{Check optional non-negative double vector} +\usage{ +check_optional_nonneg_double_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a non-empty vector with all +elements finite, greater than or equal to zero, and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional non-negative double vector +} +\examples{ +check_optional_nonneg_double_vector(NULL) +check_optional_nonneg_double_vector(c(0, 1, 5)) +# Throw error: +try(check_optional_nonneg_double_vector(c(-1, 0))) +} +\author{ +EDG +} diff --git a/man/check_optional_pos_double_scalar.Rd b/man/check_optional_pos_double_scalar.Rd new file mode 100644 index 0000000..a31b132 --- /dev/null +++ b/man/check_optional_pos_double_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_pos_double_scalar} +\alias{check_optional_pos_double_scalar} +\title{Check optional positive double scalar} +\usage{ +check_optional_pos_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single finite number +strictly greater than zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional positive double scalar +} +\examples{ +check_optional_pos_double_scalar(NULL) +check_optional_pos_double_scalar(2.5) +# Throw error: +try(check_optional_pos_double_scalar(0)) +} +\author{ +EDG +} diff --git a/man/check_optional_pos_double_vector.Rd b/man/check_optional_pos_double_vector.Rd new file mode 100644 index 0000000..71dee66 --- /dev/null +++ b/man/check_optional_pos_double_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_pos_double_vector} +\alias{check_optional_pos_double_vector} +\title{Check optional positive double vector} +\usage{ +check_optional_pos_double_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a non-empty vector with all +elements finite, strictly greater than zero, and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional positive double vector +} +\examples{ +check_optional_pos_double_vector(NULL) +check_optional_pos_double_vector(c(0.5, 2)) +# Throw error: +try(check_optional_pos_double_vector(c(0, 1))) +} +\author{ +EDG +} diff --git a/man/check_optional_pos_integer_scalar.Rd b/man/check_optional_pos_integer_scalar.Rd new file mode 100644 index 0000000..5b24229 --- /dev/null +++ b/man/check_optional_pos_integer_scalar.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_pos_integer_scalar} +\alias{check_optional_pos_integer_scalar} +\title{Check optional positive integer scalar} +\usage{ +check_optional_pos_integer_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single non-NA whole number +greater than zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional positive integer scalar +} +\examples{ +check_optional_pos_integer_scalar(NULL) +check_optional_pos_integer_scalar(5L) +# Throw error: +try(check_optional_pos_integer_scalar(0)) +} +\author{ +EDG +} diff --git a/man/check_optional_prob_scalar.Rd b/man/check_optional_prob_scalar.Rd new file mode 100644 index 0000000..1ad124c --- /dev/null +++ b/man/check_optional_prob_scalar.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_prob_scalar} +\alias{check_optional_prob_scalar} +\title{Check optional probability scalar} +\usage{ +check_optional_prob_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a single finite number in \eqn{[0, 1]}.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional probability scalar +} +\examples{ +check_optional_prob_scalar(NULL) +check_optional_prob_scalar(0.5) +# Throw error: +try(check_optional_prob_scalar(2.0)) +} +\author{ +EDG +} diff --git a/man/check_optional_prob_vector.Rd b/man/check_optional_prob_vector.Rd new file mode 100644 index 0000000..db6cbc1 --- /dev/null +++ b/man/check_optional_prob_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_prob_vector} +\alias{check_optional_prob_vector} +\title{Check optional probability vector} +\usage{ +check_optional_prob_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a non-empty vector with all +elements in \eqn{[0, 1]} and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional probability vector +} +\examples{ +check_optional_prob_vector(NULL) +check_optional_prob_vector(c(0.2, 0.8)) +# Throw error: +try(check_optional_prob_vector(c(0.5, 2))) +} +\author{ +EDG +} diff --git a/man/check_optional_scalar_character.Rd b/man/check_optional_scalar_character.Rd index 6da1e6c..15b23c7 100644 --- a/man/check_optional_scalar_character.Rd +++ b/man/check_optional_scalar_character.Rd @@ -4,7 +4,7 @@ \alias{check_optional_scalar_character} \title{Check Optional Scalar Character} \usage{ -check_optional_scalar_character(x, arg_name) +check_optional_scalar_character(x, arg_name = deparse(substitute(x))) } \arguments{ \item{x}{Optional Character: Value to check.} @@ -18,8 +18,8 @@ Called for side effects. Check Optional Scalar Character } \examples{ -check_optional_scalar_character(NULL, "my_arg") # Passes -check_optional_scalar_character("hello", "my_arg") # Passes +check_optional_scalar_character(NULL, "my_arg") +check_optional_scalar_character("hello", "my_arg") # Throw error: try(check_optional_scalar_character(c("hello", "world"), "my_arg")) try(check_optional_scalar_character(123, "my_arg")) diff --git a/man/check_optional_unit_open_vector.Rd b/man/check_optional_unit_open_vector.Rd new file mode 100644 index 0000000..b3220a7 --- /dev/null +++ b/man/check_optional_unit_open_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_optional_unit_open_vector} +\alias{check_optional_unit_open_vector} +\title{Check optional open-unit-interval vector} +\usage{ +check_optional_unit_open_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Optional Numeric: Value to check. Must be \code{NULL} or a non-empty vector with all +elements strictly in \eqn{(0, 1)} and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check optional open-unit-interval vector +} +\examples{ +check_optional_unit_open_vector(NULL) +check_optional_unit_open_vector(c(0.1, 0.9)) +# Throw error: +try(check_optional_unit_open_vector(c(0, 0.5))) +} +\author{ +EDG +} diff --git a/man/check_pos_double_scalar.Rd b/man/check_pos_double_scalar.Rd new file mode 100644 index 0000000..811c798 --- /dev/null +++ b/man/check_pos_double_scalar.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_pos_double_scalar} +\alias{check_pos_double_scalar} +\title{Check positive double scalar} +\usage{ +check_pos_double_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single finite number strictly greater than zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check positive double scalar +} +\examples{ +check_pos_double_scalar(0.001) +check_pos_double_scalar(100) +# Throw error: +try(check_pos_double_scalar(0)) +try(check_pos_double_scalar(-1)) +try(check_pos_double_scalar(Inf)) +} +\author{ +EDG +} diff --git a/man/check_pos_double_vector.Rd b/man/check_pos_double_vector.Rd new file mode 100644 index 0000000..35bfd05 --- /dev/null +++ b/man/check_pos_double_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_pos_double_vector} +\alias{check_pos_double_vector} +\title{Check positive double vector} +\usage{ +check_pos_double_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a non-empty vector with all elements finite, +strictly greater than zero, and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check positive double vector +} +\examples{ +check_pos_double_vector(c(0.1, 1, 10)) +# Throw error: +try(check_pos_double_vector(c(0, 1))) +try(check_pos_double_vector(c(1, Inf))) +} +\author{ +EDG +} diff --git a/man/check_pos_integer_scalar.Rd b/man/check_pos_integer_scalar.Rd new file mode 100644 index 0000000..8ef698a --- /dev/null +++ b/man/check_pos_integer_scalar.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_pos_integer_scalar} +\alias{check_pos_integer_scalar} +\title{Check positive integer scalar} +\usage{ +check_pos_integer_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single non-NA whole number greater than zero.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check positive integer scalar +} +\details{ +Accepts any single numeric value that is a whole number strictly greater than zero. +Integer-typed inputs (\code{1L}) and double-typed whole numbers (\code{1}, \code{100}) are both accepted for +user convenience. +} +\examples{ +check_pos_integer_scalar(1L) +check_pos_integer_scalar(10) +# Throw error: +try(check_pos_integer_scalar(0)) +try(check_pos_integer_scalar(-1L)) +try(check_pos_integer_scalar(1.5)) +} +\author{ +EDG +} diff --git a/man/check_prob_scalar.Rd b/man/check_prob_scalar.Rd new file mode 100644 index 0000000..745bfdf --- /dev/null +++ b/man/check_prob_scalar.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_prob_scalar} +\alias{check_prob_scalar} +\title{Check probability scalar} +\usage{ +check_prob_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single finite number in \eqn{[0, 1]}.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check probability scalar +} +\examples{ +check_prob_scalar(0) +check_prob_scalar(0.5) +check_prob_scalar(1) +# Throw error: +try(check_prob_scalar(1.5)) +try(check_prob_scalar(-0.1)) +} +\author{ +EDG +} diff --git a/man/check_prob_vector.Rd b/man/check_prob_vector.Rd new file mode 100644 index 0000000..b393faa --- /dev/null +++ b/man/check_prob_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_prob_vector} +\alias{check_prob_vector} +\title{Check probability vector} +\usage{ +check_prob_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a non-empty vector with all elements in \eqn{[0, 1]} +and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check probability vector +} +\examples{ +check_prob_vector(c(0, 0.5, 1)) +# Throw error: +try(check_prob_vector(c(0.5, 1.5))) +try(check_prob_vector(c(0.5, NA))) +} +\author{ +EDG +} diff --git a/man/check_scalar_character.Rd b/man/check_scalar_character.Rd index 1ed1b66..82b51a3 100644 --- a/man/check_scalar_character.Rd +++ b/man/check_scalar_character.Rd @@ -4,7 +4,7 @@ \alias{check_scalar_character} \title{Check Scalar Character} \usage{ -check_scalar_character(x, arg_name) +check_scalar_character(x, arg_name = deparse(substitute(x))) } \arguments{ \item{x}{Character: Value to check.} @@ -18,7 +18,7 @@ Called for side effects. Check Scalar Character } \examples{ -check_scalar_character("hello", "my_arg") # Passes +check_scalar_character("hello", "my_arg") # Throw error: try(check_scalar_character(c("hello", "world"), "my_arg")) try(check_scalar_character(123, "my_arg")) diff --git a/man/check_scalar_logical.Rd b/man/check_scalar_logical.Rd index 365ad4f..60c663b 100644 --- a/man/check_scalar_logical.Rd +++ b/man/check_scalar_logical.Rd @@ -4,7 +4,7 @@ \alias{check_scalar_logical} \title{Check Scalar Logical} \usage{ -check_scalar_logical(x, arg_name) +check_scalar_logical(x, arg_name = deparse(substitute(x))) } \arguments{ \item{x}{Logical: Value to check.} @@ -18,12 +18,10 @@ Called for side effects. Check Scalar Logical } \examples{ -check_scalar_logical(TRUE, "my_arg") # Passes -check_scalar_logical(FALSE, "my_arg") # Passes +check_scalar_logical(TRUE, "my_arg") # Throw error: try(check_scalar_logical(c(TRUE, FALSE), "my_arg")) try(check_scalar_logical(NA, "my_arg")) -try(check_scalar_logical("TRUE", "my_arg")) } \author{ EDG diff --git a/man/check_unit_open_scalar.Rd b/man/check_unit_open_scalar.Rd new file mode 100644 index 0000000..efd8a71 --- /dev/null +++ b/man/check_unit_open_scalar.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_unit_open_scalar} +\alias{check_unit_open_scalar} +\title{Check open-unit-interval scalar} +\usage{ +check_unit_open_scalar(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a single finite number strictly in \eqn{(0, 1)}.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check open-unit-interval scalar +} +\examples{ +check_unit_open_scalar(0.5) +# Throw error: +try(check_unit_open_scalar(0)) +try(check_unit_open_scalar(1)) +} +\author{ +EDG +} diff --git a/man/check_unit_open_vector.Rd b/man/check_unit_open_vector.Rd new file mode 100644 index 0000000..39f7d79 --- /dev/null +++ b/man/check_unit_open_vector.Rd @@ -0,0 +1,29 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/check.R +\name{check_unit_open_vector} +\alias{check_unit_open_vector} +\title{Check open-unit-interval vector} +\usage{ +check_unit_open_vector(x, arg_name = deparse(substitute(x))) +} +\arguments{ +\item{x}{Numeric: Value to check. Must be a non-empty vector with all elements strictly in +\eqn{(0, 1)} and no NAs.} + +\item{arg_name}{Character: Argument name to use in error messages.} +} +\value{ +Called for side effects. Throws an error if checks fail. +} +\description{ +Check open-unit-interval vector +} +\examples{ +check_unit_open_vector(c(0.2, 0.5, 0.9)) +# Throw error: +try(check_unit_open_vector(c(0, 0.5))) +try(check_unit_open_vector(c(0.5, 1))) +} +\author{ +EDG +} diff --git a/man/clean_int.Rd b/man/clean_int.Rd index 6a34fdc..b905c87 100644 --- a/man/clean_int.Rd +++ b/man/clean_int.Rd @@ -8,11 +8,9 @@ clean_int(x, xname = deparse(substitute(x))) } \arguments{ \item{x}{Double or integer vector to check.} - -\item{xname}{Character: Name of the variable for error messages.} } \value{ -Integer vector. +Integer vector } \description{ Clean integer input diff --git a/man/double_scalar.Rd b/man/double_scalar.Rd new file mode 100644 index 0000000..fbdaf45 --- /dev/null +++ b/man/double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{double_scalar} +\alias{double_scalar} +\title{Double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single non-NA double value. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/enum.Rd b/man/enum.Rd new file mode 100644 index 0000000..b8b96f6 --- /dev/null +++ b/man/enum.Rd @@ -0,0 +1,27 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\name{enum} +\alias{enum} +\title{Create an enum S7 property} +\usage{ +enum(values, default = NULL, nullable = FALSE) +} +\arguments{ +\item{values}{Character: Allowed values.} + +\item{default}{Optional Character: Default value.} + +\item{nullable}{Logical scalar. If \code{TRUE}, \code{NULL} is also accepted. Default \code{FALSE}.} +} +\value{ +An S7 property object. +} +\description{ +Returns a \code{new_property()} for a character scalar constrained to a fixed set of allowed values. +} +\examples{ +type_prop <- enum(c("string", "number", "boolean"), default = "string") +} +\author{ +EDG +} diff --git a/man/integer_scalar.Rd b/man/integer_scalar.Rd new file mode 100644 index 0000000..8ae8c0b --- /dev/null +++ b/man/integer_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{integer_scalar} +\alias{integer_scalar} +\title{Integer scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +integer_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single non-NA integer value (must be \code{integer} type, e.g. \code{1L}). +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/logical_scalar.Rd b/man/logical_scalar.Rd new file mode 100644 index 0000000..d50f292 --- /dev/null +++ b/man/logical_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{logical_scalar} +\alias{logical_scalar} +\title{Logical scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +logical_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single non-NA logical value. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/nonneg_double_scalar.Rd b/man/nonneg_double_scalar.Rd new file mode 100644 index 0000000..af0c420 --- /dev/null +++ b/man/nonneg_double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{nonneg_double_scalar} +\alias{nonneg_double_scalar} +\title{Non-negative double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +nonneg_double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single finite double greater than or equal to zero, i.e. in \eqn{[0, \infty)}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/nonneg_double_vector.Rd b/man/nonneg_double_vector.Rd new file mode 100644 index 0000000..156fe64 --- /dev/null +++ b/man/nonneg_double_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{nonneg_double_vector} +\alias{nonneg_double_vector} +\title{Non-negative double vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +nonneg_double_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a non-empty double vector with all elements finite, greater than or +equal to zero, and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional.Rd b/man/optional.Rd index e3a4663..7febf0b 100644 --- a/man/optional.Rd +++ b/man/optional.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/types.R +% Please edit documentation in R/00_S7_properties.R \name{optional} \alias{optional} \title{Create an optional S7 type} @@ -7,7 +7,7 @@ optional(type) } \arguments{ -\item{type}{S7 base class or S7 class.} +\item{type}{S7 class.} } \value{ An S7 union type that allows for the specified type or \code{NULL}. @@ -15,6 +15,11 @@ An S7 union type that allows for the specified type or \code{NULL}. \description{ Creates an S7 union type that allows for the specified type or \code{NULL}. } +\details{ +This should be used when the S7 class already includes all the necessary validation for the +non-NULL case. Otherwise, create a new S7 property with appropriate validator using +\code{S7::new_property()}. +} \examples{ # Create an optional character type optional(S7::class_character) diff --git a/man/optional_character_scalar.Rd b/man/optional_character_scalar.Rd new file mode 100644 index 0000000..4fca8cc --- /dev/null +++ b/man/optional_character_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_character_scalar} +\alias{optional_character_scalar} +\title{Optional non-empty character scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_character_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single non-NA, non-empty (after trimming whitespace) string. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_double_scalar.Rd b/man/optional_double_scalar.Rd new file mode 100644 index 0000000..c1169e0 --- /dev/null +++ b/man/optional_double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_double_scalar} +\alias{optional_double_scalar} +\title{Optional double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single non-NA double value. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_integer_scalar.Rd b/man/optional_integer_scalar.Rd new file mode 100644 index 0000000..869e121 --- /dev/null +++ b/man/optional_integer_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_integer_scalar} +\alias{optional_integer_scalar} +\title{Optional integer scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_integer_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single non-NA integer value (must be \code{integer} type, e.g. \code{1L}). +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_logical_scalar.Rd b/man/optional_logical_scalar.Rd new file mode 100644 index 0000000..7c43bb7 --- /dev/null +++ b/man/optional_logical_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_logical_scalar} +\alias{optional_logical_scalar} +\title{Optional logical scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_logical_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single non-NA logical value. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_nonneg_double_scalar.Rd b/man/optional_nonneg_double_scalar.Rd new file mode 100644 index 0000000..58ec7cd --- /dev/null +++ b/man/optional_nonneg_double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_nonneg_double_scalar} +\alias{optional_nonneg_double_scalar} +\title{Optional non-negative double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_nonneg_double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single finite double greater than or equal to zero. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_nonneg_double_vector.Rd b/man/optional_nonneg_double_vector.Rd new file mode 100644 index 0000000..ad96eac --- /dev/null +++ b/man/optional_nonneg_double_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_nonneg_double_vector} +\alias{optional_nonneg_double_vector} +\title{Optional non-negative double vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_nonneg_double_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a non-empty double vector with all elements finite, +greater than or equal to zero, and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_pos_double_scalar.Rd b/man/optional_pos_double_scalar.Rd new file mode 100644 index 0000000..cec67a4 --- /dev/null +++ b/man/optional_pos_double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_pos_double_scalar} +\alias{optional_pos_double_scalar} +\title{Optional positive double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_pos_double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single finite double strictly greater than zero. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_pos_double_vector.Rd b/man/optional_pos_double_vector.Rd new file mode 100644 index 0000000..fc8b4bd --- /dev/null +++ b/man/optional_pos_double_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_pos_double_vector} +\alias{optional_pos_double_vector} +\title{Optional positive double vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_pos_double_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a non-empty double vector with all elements finite, +strictly greater than zero, and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_pos_integer_scalar.Rd b/man/optional_pos_integer_scalar.Rd new file mode 100644 index 0000000..00592c9 --- /dev/null +++ b/man/optional_pos_integer_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_pos_integer_scalar} +\alias{optional_pos_integer_scalar} +\title{Optional positive integer scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_pos_integer_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single non-NA integer value strictly greater than zero. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_prob_scalar.Rd b/man/optional_prob_scalar.Rd new file mode 100644 index 0000000..e7bb7ff --- /dev/null +++ b/man/optional_prob_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_prob_scalar} +\alias{optional_prob_scalar} +\title{Optional probability scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_prob_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single finite double in \eqn{[0, 1]}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_prob_vector.Rd b/man/optional_prob_vector.Rd new file mode 100644 index 0000000..932588c --- /dev/null +++ b/man/optional_prob_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_prob_vector} +\alias{optional_prob_vector} +\title{Optional probability vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_prob_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a non-empty double vector with all elements in \eqn{[0, 1]} +and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_unit_open_scalar.Rd b/man/optional_unit_open_scalar.Rd new file mode 100644 index 0000000..a874eb9 --- /dev/null +++ b/man/optional_unit_open_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_unit_open_scalar} +\alias{optional_unit_open_scalar} +\title{Optional open-unit-interval scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_unit_open_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a single finite double strictly in \eqn{(0, 1)}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/optional_unit_open_vector.Rd b/man/optional_unit_open_vector.Rd new file mode 100644 index 0000000..d8355a3 --- /dev/null +++ b/man/optional_unit_open_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{optional_unit_open_vector} +\alias{optional_unit_open_vector} +\title{Optional open-unit-interval vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +optional_unit_open_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting \code{NULL} or a non-empty double vector with all elements strictly in +\eqn{(0, 1)} and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/pos_double_scalar.Rd b/man/pos_double_scalar.Rd new file mode 100644 index 0000000..decc966 --- /dev/null +++ b/man/pos_double_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{pos_double_scalar} +\alias{pos_double_scalar} +\title{Positive double scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +pos_double_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single finite double strictly greater than zero, i.e. in \eqn{(0, \infty)}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/pos_double_vector.Rd b/man/pos_double_vector.Rd new file mode 100644 index 0000000..5b19732 --- /dev/null +++ b/man/pos_double_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{pos_double_vector} +\alias{pos_double_vector} +\title{Positive double vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +pos_double_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a non-empty double vector with all elements finite, strictly greater +than zero, and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/pos_integer_scalar.Rd b/man/pos_integer_scalar.Rd new file mode 100644 index 0000000..e3790d2 --- /dev/null +++ b/man/pos_integer_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{pos_integer_scalar} +\alias{pos_integer_scalar} +\title{Positive integer scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +pos_integer_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single non-NA integer value strictly greater than zero (e.g. \code{1L}). +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/prob_scalar.Rd b/man/prob_scalar.Rd new file mode 100644 index 0000000..f40eaf6 --- /dev/null +++ b/man/prob_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{prob_scalar} +\alias{prob_scalar} +\title{Probability scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +prob_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single finite double in \eqn{[0, 1]}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/prob_vector.Rd b/man/prob_vector.Rd new file mode 100644 index 0000000..06e7180 --- /dev/null +++ b/man/prob_vector.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{prob_vector} +\alias{prob_vector} +\title{Probability vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +prob_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a non-empty double vector with all elements in \eqn{[0, 1]} and no NAs. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/repr.Rd b/man/repr.Rd index 533dcf9..4b4283e 100644 --- a/man/repr.Rd +++ b/man/repr.Rd @@ -1,5 +1,5 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/0_S7_init.R +% Please edit documentation in R/01_S7_generics.R \name{repr} \alias{repr} \title{String representation} diff --git a/man/unit_open_scalar.Rd b/man/unit_open_scalar.Rd new file mode 100644 index 0000000..cfaddbf --- /dev/null +++ b/man/unit_open_scalar.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{unit_open_scalar} +\alias{unit_open_scalar} +\title{Open-unit-interval scalar S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +unit_open_scalar +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a single finite double strictly in \eqn{(0, 1)}. +} +\author{ +EDG +} +\keyword{datasets} diff --git a/man/unit_open_vector.Rd b/man/unit_open_vector.Rd new file mode 100644 index 0000000..91e91b4 --- /dev/null +++ b/man/unit_open_vector.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/00_S7_properties.R +\docType{data} +\name{unit_open_vector} +\alias{unit_open_vector} +\title{Open-unit-interval vector S7 property} +\format{ +An object of class \code{S7_property} of length 6. +} +\usage{ +unit_open_vector +} +\value{ +An S7 property object. +} +\description{ +S7 property accepting a non-empty double vector with all elements strictly in \eqn{(0, 1)} +and no NAs. +} +\author{ +EDG +} +\keyword{datasets} From 471465886e404d52281b3d6b039584ab29bf9dad Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:23:47 -0700 Subject: [PATCH 12/18] air format --- tests/testthat/test-00_S7_properties.R | 113 +++++++++++++++++++------ 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/tests/testthat/test-00_S7_properties.R b/tests/testthat/test-00_S7_properties.R index 621785f..6c56778 100644 --- a/tests/testthat/test-00_S7_properties.R +++ b/tests/testthat/test-00_S7_properties.R @@ -5,29 +5,68 @@ library(S7) # Define one test class per property so S7 validation fires on construction. .TC <- local({ list( - chr = new_class("TC_chr", properties = list(x = character_scalar)), - opt_chr = new_class("TC_opt_chr", properties = list(x = optional_character_scalar)), - dbl = new_class("TC_dbl", properties = list(x = double_scalar)), - opt_dbl = new_class("TC_opt_dbl", properties = list(x = optional_double_scalar)), - int = new_class("TC_int", properties = list(x = integer_scalar)), - opt_int = new_class("TC_opt_int", properties = list(x = optional_integer_scalar)), - lgl = new_class("TC_lgl", properties = list(x = logical_scalar)), - opt_lgl = new_class("TC_opt_lgl", properties = list(x = optional_logical_scalar)), - prob = new_class("TC_prob", properties = list(x = prob_scalar)), - opt_prb = new_class("TC_opt_prb", properties = list(x = optional_prob_scalar)), - unit_o = new_class("TC_unit_o", properties = list(x = unit_open_scalar)), + chr = new_class("TC_chr", properties = list(x = character_scalar)), + opt_chr = new_class( + "TC_opt_chr", + properties = list(x = optional_character_scalar) + ), + dbl = new_class("TC_dbl", properties = list(x = double_scalar)), + opt_dbl = new_class( + "TC_opt_dbl", + properties = list(x = optional_double_scalar) + ), + int = new_class("TC_int", properties = list(x = integer_scalar)), + opt_int = new_class( + "TC_opt_int", + properties = list(x = optional_integer_scalar) + ), + lgl = new_class("TC_lgl", properties = list(x = logical_scalar)), + opt_lgl = new_class( + "TC_opt_lgl", + properties = list(x = optional_logical_scalar) + ), + prob = new_class("TC_prob", properties = list(x = prob_scalar)), + opt_prb = new_class( + "TC_opt_prb", + properties = list(x = optional_prob_scalar) + ), + unit_o = new_class("TC_unit_o", properties = list(x = unit_open_scalar)), pos_dbl = new_class("TC_pos_dbl", properties = list(x = pos_double_scalar)), - opt_pos = new_class("TC_opt_pos", properties = list(x = optional_pos_double_scalar)), - nng_dbl = new_class("TC_nng_dbl", properties = list(x = nonneg_double_scalar)), - opt_nng = new_class("TC_opt_nng", properties = list(x = optional_nonneg_double_scalar)), - pvec = new_class("TC_pvec", properties = list(x = prob_vector)), - opt_pvec = new_class("TC_opt_pvec", properties = list(x = optional_prob_vector)), - uovec = new_class("TC_uovec", properties = list(x = unit_open_vector)), - opt_uov = new_class("TC_opt_uov", properties = list(x = optional_unit_open_vector)), - posvec = new_class("TC_posvec", properties = list(x = pos_double_vector)), - opt_posv = new_class("TC_opt_posv", properties = list(x = optional_pos_double_vector)), - nngvec = new_class("TC_nngvec", properties = list(x = nonneg_double_vector)), - opt_nngv = new_class("TC_opt_nngv", properties = list(x = optional_nonneg_double_vector)) + opt_pos = new_class( + "TC_opt_pos", + properties = list(x = optional_pos_double_scalar) + ), + nng_dbl = new_class( + "TC_nng_dbl", + properties = list(x = nonneg_double_scalar) + ), + opt_nng = new_class( + "TC_opt_nng", + properties = list(x = optional_nonneg_double_scalar) + ), + pvec = new_class("TC_pvec", properties = list(x = prob_vector)), + opt_pvec = new_class( + "TC_opt_pvec", + properties = list(x = optional_prob_vector) + ), + uovec = new_class("TC_uovec", properties = list(x = unit_open_vector)), + opt_uov = new_class( + "TC_opt_uov", + properties = list(x = optional_unit_open_vector) + ), + posvec = new_class("TC_posvec", properties = list(x = pos_double_vector)), + opt_posv = new_class( + "TC_opt_posv", + properties = list(x = optional_pos_double_vector) + ), + nngvec = new_class( + "TC_nngvec", + properties = list(x = nonneg_double_vector) + ), + opt_nngv = new_class( + "TC_opt_nngv", + properties = list(x = optional_nonneg_double_vector) + ) ) }) @@ -276,40 +315,58 @@ test_that("optional_nonneg_double_scalar rejects negative value", { # bounded_double_property ---- test_that("bounded_double_property closed interval accepts endpoints", { - TC <- new_class("TC_bnd1", properties = list(x = bounded_double_property(0, 10))) + TC <- new_class( + "TC_bnd1", + properties = list(x = bounded_double_property(0, 10)) + ) expect_no_error(TC(x = 0)) expect_no_error(TC(x = 10)) expect_no_error(TC(x = 5)) }) test_that("bounded_double_property closed interval rejects outside", { - TC <- new_class("TC_bnd2", properties = list(x = bounded_double_property(0, 10))) + TC <- new_class( + "TC_bnd2", + properties = list(x = bounded_double_property(0, 10)) + ) expect_error(TC(x = -0.1)) expect_error(TC(x = 10.1)) }) test_that("bounded_double_property lower_open rejects lower endpoint", { - TC <- new_class("TC_bnd3", properties = list(x = bounded_double_property(0, 1, lower_open = TRUE))) + TC <- new_class( + "TC_bnd3", + properties = list(x = bounded_double_property(0, 1, lower_open = TRUE)) + ) expect_error(TC(x = 0)) expect_no_error(TC(x = 0.001)) expect_no_error(TC(x = 1)) }) test_that("bounded_double_property upper_open rejects upper endpoint", { - TC <- new_class("TC_bnd4", properties = list(x = bounded_double_property(0, 1, upper_open = TRUE))) + TC <- new_class( + "TC_bnd4", + properties = list(x = bounded_double_property(0, 1, upper_open = TRUE)) + ) expect_no_error(TC(x = 0)) expect_error(TC(x = 1)) }) test_that("bounded_double_property nullable accepts NULL", { - TC <- new_class("TC_bnd5", properties = list(x = bounded_double_property(0, 1, nullable = TRUE))) + TC <- new_class( + "TC_bnd5", + properties = list(x = bounded_double_property(0, 1, nullable = TRUE)) + ) expect_no_error(TC(x = NULL)) expect_no_error(TC(x = 0.5)) expect_error(TC(x = 2.0)) }) test_that("bounded_double_property rejects Inf", { - TC <- new_class("TC_bnd6", properties = list(x = bounded_double_property(0, 100))) + TC <- new_class( + "TC_bnd6", + properties = list(x = bounded_double_property(0, 100)) + ) expect_error(TC(x = Inf)) }) From 881edf81dafe392f0e6b338beb96724022e568ca Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 02:26:04 -0700 Subject: [PATCH 13/18] docs --- R/clean.R | 21 +++++++++++++-------- man/clean_int.Rd | 4 +++- man/clean_posint.Rd | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/R/clean.R b/R/clean.R index 2248eff..99f44c5 100644 --- a/R/clean.R +++ b/R/clean.R @@ -12,6 +12,7 @@ #' otherwise an error is thrown. #' #' @param x Double or integer vector to check. +#' @param arg_name Character: Name of the variable for error messages. #' #' @return Integer vector #' @author EDG @@ -24,7 +25,7 @@ #' # clean_int(12.1) # Error #' clean_int(c(3, 5, 7)) #' # clean_int(c(3, 5, 7.01)) # Error -clean_int <- function(x, xname = deparse(substitute(x))) { +clean_int <- function(x, arg_name = deparse(substitute(x))) { if (is.integer(x)) { return(x) } else if (is.numeric(x)) { @@ -32,12 +33,12 @@ clean_int <- function(x, xname = deparse(substitute(x))) { storage.mode(x) <- "integer" return(x) } else { - cli::cli_abort("{.var {xname}} must be integer.") + cli::cli_abort("{.var {arg_name}} must be integer.") } } else if (is.null(x)) { return(NULL) } - cli::cli_abort("{.var {xname}} must be integer.") + cli::cli_abort("{.var {arg_name}} must be integer.") } @@ -47,7 +48,7 @@ clean_int <- function(x, xname = deparse(substitute(x))) { #' @param x Integer vector. #' @param allow_na Logical: If TRUE, NAs are excluded before checking. If FALSE (default), #' NAs trigger an error. -#' @param xname Character: Name of the variable for error messages. +#' @param arg_name Character: Name of the variable for error messages. #' #' @return Integer vector of positive values. #' @@ -56,22 +57,26 @@ clean_int <- function(x, xname = deparse(substitute(x))) { #' #' @examples #' clean_posint(5) -clean_posint <- function(x, allow_na = FALSE, xname = deparse(substitute(x))) { +clean_posint <- function( + x, + allow_na = FALSE, + arg_name = deparse(substitute(x)) +) { if (is.null(x)) { return(NULL) } if (!allow_na && anyNA(x)) { - cli::cli_abort("{.var {xname}} must not contain NAs.") + cli::cli_abort("{.var {arg_name}} must not contain NAs.") } else { x <- x[!is.na(x)] } if (any(x <= 0)) { - cli::cli_abort("{.var {xname}} must contain only positive integers.") + cli::cli_abort("{.var {arg_name}} must contain only positive integers.") } - clean_int(x, xname = xname) + clean_int(x, arg_name = arg_name) } diff --git a/man/clean_int.Rd b/man/clean_int.Rd index b905c87..1146acf 100644 --- a/man/clean_int.Rd +++ b/man/clean_int.Rd @@ -4,10 +4,12 @@ \alias{clean_int} \title{Clean integer input} \usage{ -clean_int(x, xname = deparse(substitute(x))) +clean_int(x, arg_name = deparse(substitute(x))) } \arguments{ \item{x}{Double or integer vector to check.} + +\item{arg_name}{Character: Name of the variable for error messages.} } \value{ Integer vector diff --git a/man/clean_posint.Rd b/man/clean_posint.Rd index 13abea1..7fe704a 100644 --- a/man/clean_posint.Rd +++ b/man/clean_posint.Rd @@ -4,7 +4,7 @@ \alias{clean_posint} \title{Check positive integer} \usage{ -clean_posint(x, allow_na = FALSE, xname = deparse(substitute(x))) +clean_posint(x, allow_na = FALSE, arg_name = deparse(substitute(x))) } \arguments{ \item{x}{Integer vector.} @@ -12,7 +12,7 @@ clean_posint(x, allow_na = FALSE, xname = deparse(substitute(x))) \item{allow_na}{Logical: If TRUE, NAs are excluded before checking. If FALSE (default), NAs trigger an error.} -\item{xname}{Character: Name of the variable for error messages.} +\item{arg_name}{Character: Name of the variable for error messages.} } \value{ Integer vector of positive values. From 85da1851d00f14a0524f95394d9394fe0cecce49 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 04:05:54 -0700 Subject: [PATCH 14/18] docs --- R/00_S7_properties.R | 2 +- man/optional.Rd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/R/00_S7_properties.R b/R/00_S7_properties.R index d8d5e58..1b3bcc5 100644 --- a/R/00_S7_properties.R +++ b/R/00_S7_properties.R @@ -714,7 +714,7 @@ enum <- function(values, default = NULL, nullable = FALSE) { #' non-NULL case. Otherwise, create a new S7 property with appropriate validator using #' `S7::new_property()`. #' -#' @param type S7 class. +#' @param type S7 base class or S7 class. #' @return An S7 union type that allows for the specified type or `NULL`. #' @author EDG #' @export diff --git a/man/optional.Rd b/man/optional.Rd index 7febf0b..4f06aec 100644 --- a/man/optional.Rd +++ b/man/optional.Rd @@ -7,7 +7,7 @@ optional(type) } \arguments{ -\item{type}{S7 class.} +\item{type}{S7 base class or S7 class.} } \value{ An S7 union type that allows for the specified type or \code{NULL}. From 7bad59d465f179599dccec2501a066ceb4f2067e Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 04:05:59 -0700 Subject: [PATCH 15/18] up --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b929652..c7d1207 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # rtemis.core NEWS -## Version 0.0.4 +## Version 0.1.0 - Added S7 property library - Expanded check_ function set From af7c2ea0e8dd7eb284d7fbf8b19beb47dce074fc Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 04:06:31 -0700 Subject: [PATCH 16/18] expand tests --- tests/testthat/test-check.R | 228 ++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) diff --git a/tests/testthat/test-check.R b/tests/testthat/test-check.R index 85ee24e..0cb9397 100644 --- a/tests/testthat/test-check.R +++ b/tests/testthat/test-check.R @@ -352,3 +352,231 @@ test_that("check_optional_pos_integer_scalar passes for valid values", { test_that("check_optional_pos_integer_scalar rejects 0", { expect_error(check_optional_pos_integer_scalar(0L)) }) + +# check_character_scalar ---- +test_that("check_character_scalar passes for non-empty string", { + expect_invisible(check_character_scalar("hello")) +}) + +test_that("check_character_scalar rejects empty string", { + expect_error(check_character_scalar("")) +}) + +test_that("check_character_scalar rejects NA", { + expect_error(check_character_scalar(NA_character_)) +}) + +test_that("check_character_scalar rejects length > 1", { + expect_error(check_character_scalar(c("a", "b"))) +}) + +test_that("check_character_scalar rejects non-character", { + expect_error(check_character_scalar(1)) +}) + +test_that("check_optional_character_scalar accepts NULL", { + expect_invisible(check_optional_character_scalar(NULL)) +}) + +test_that("check_optional_character_scalar passes for non-empty string", { + expect_invisible(check_optional_character_scalar("hello")) +}) + +test_that("check_optional_character_scalar rejects empty string", { + expect_error(check_optional_character_scalar("")) +}) + +# check_double_scalar ---- +test_that("check_double_scalar passes for single numeric", { + expect_invisible(check_double_scalar(3.14)) + expect_invisible(check_double_scalar(1L)) +}) + +test_that("check_double_scalar rejects NA", { + expect_error(check_double_scalar(NA_real_)) +}) + +test_that("check_double_scalar rejects length > 1", { + expect_error(check_double_scalar(c(1.0, 2.0))) +}) + +test_that("check_double_scalar rejects non-numeric", { + expect_error(check_double_scalar("a")) +}) + +test_that("check_optional_double_scalar accepts NULL", { + expect_invisible(check_optional_double_scalar(NULL)) +}) + +test_that("check_optional_double_scalar passes for single numeric", { + expect_invisible(check_optional_double_scalar(2.5)) +}) + +test_that("check_optional_double_scalar rejects NA", { + expect_error(check_optional_double_scalar(NA_real_)) +}) + +# check_integer_scalar ---- +test_that("check_integer_scalar passes for whole numbers", { + expect_invisible(check_integer_scalar(5L)) + expect_invisible(check_integer_scalar(100)) +}) + +test_that("check_integer_scalar rejects non-whole number", { + expect_error(check_integer_scalar(1.5)) +}) + +test_that("check_integer_scalar rejects NA", { + expect_error(check_integer_scalar(NA_integer_)) +}) + +test_that("check_integer_scalar rejects Inf", { + expect_error(check_integer_scalar(Inf)) + expect_error(check_integer_scalar(-Inf)) +}) + +test_that("check_optional_integer_scalar accepts NULL", { + expect_invisible(check_optional_integer_scalar(NULL)) +}) + +test_that("check_optional_integer_scalar passes for whole number", { + expect_invisible(check_optional_integer_scalar(10L)) +}) + +test_that("check_optional_integer_scalar rejects non-whole number", { + expect_error(check_optional_integer_scalar(1.5)) +}) + +# check_logical_scalar ---- +test_that("check_logical_scalar passes for TRUE and FALSE", { + expect_invisible(check_logical_scalar(TRUE)) + expect_invisible(check_logical_scalar(FALSE)) +}) + +test_that("check_logical_scalar rejects NA", { + expect_error(check_logical_scalar(NA)) +}) + +test_that("check_logical_scalar rejects non-logical", { + expect_error(check_logical_scalar(1L)) +}) + +test_that("check_logical_scalar rejects length > 1", { + expect_error(check_logical_scalar(c(TRUE, FALSE))) +}) + +test_that("check_optional_logical_scalar accepts NULL", { + expect_invisible(check_optional_logical_scalar(NULL)) +}) + +test_that("check_optional_logical_scalar passes for FALSE", { + expect_invisible(check_optional_logical_scalar(FALSE)) +}) + +test_that("check_optional_logical_scalar rejects NA", { + expect_error(check_optional_logical_scalar(NA)) +}) + +# check_prob_scalar ---- +test_that("check_prob_scalar passes for values in [0, 1]", { + expect_invisible(check_prob_scalar(0)) + expect_invisible(check_prob_scalar(0.5)) + expect_invisible(check_prob_scalar(1)) +}) + +test_that("check_prob_scalar rejects values outside [0, 1]", { + expect_error(check_prob_scalar(1.5)) + expect_error(check_prob_scalar(-0.1)) +}) + +test_that("check_prob_scalar rejects NA", { + expect_error(check_prob_scalar(NA_real_)) +}) + +test_that("check_optional_prob_scalar accepts NULL", { + expect_invisible(check_optional_prob_scalar(NULL)) +}) + +test_that("check_optional_prob_scalar passes for 0.5", { + expect_invisible(check_optional_prob_scalar(0.5)) +}) + +test_that("check_optional_prob_scalar rejects values > 1", { + expect_error(check_optional_prob_scalar(2.0)) +}) + +# check_unit_open_scalar ---- +test_that("check_unit_open_scalar passes for strictly interior values", { + expect_invisible(check_unit_open_scalar(0.5)) + expect_invisible(check_unit_open_scalar(0.001)) +}) + +test_that("check_unit_open_scalar rejects 0 and 1", { + expect_error(check_unit_open_scalar(0)) + expect_error(check_unit_open_scalar(1)) +}) + +test_that("check_unit_open_scalar rejects NA", { + expect_error(check_unit_open_scalar(NA_real_)) +}) + +# check_pos_double_scalar ---- +test_that("check_pos_double_scalar passes for positive finite values", { + expect_invisible(check_pos_double_scalar(0.001)) + expect_invisible(check_pos_double_scalar(100)) +}) + +test_that("check_pos_double_scalar rejects 0 and negative values", { + expect_error(check_pos_double_scalar(0)) + expect_error(check_pos_double_scalar(-1)) +}) + +test_that("check_pos_double_scalar rejects Inf", { + expect_error(check_pos_double_scalar(Inf)) +}) + +test_that("check_pos_double_scalar rejects NA", { + expect_error(check_pos_double_scalar(NA_real_)) +}) + +test_that("check_optional_pos_double_scalar accepts NULL", { + expect_invisible(check_optional_pos_double_scalar(NULL)) +}) + +test_that("check_optional_pos_double_scalar passes for positive value", { + expect_invisible(check_optional_pos_double_scalar(2.5)) +}) + +test_that("check_optional_pos_double_scalar rejects 0", { + expect_error(check_optional_pos_double_scalar(0)) +}) + +# check_nonneg_double_scalar ---- +test_that("check_nonneg_double_scalar passes for 0 and positive values", { + expect_invisible(check_nonneg_double_scalar(0)) + expect_invisible(check_nonneg_double_scalar(5)) +}) + +test_that("check_nonneg_double_scalar rejects negative values", { + expect_error(check_nonneg_double_scalar(-0.001)) +}) + +test_that("check_nonneg_double_scalar rejects Inf", { + expect_error(check_nonneg_double_scalar(Inf)) +}) + +test_that("check_nonneg_double_scalar rejects NA", { + expect_error(check_nonneg_double_scalar(NA_real_)) +}) + +test_that("check_optional_nonneg_double_scalar accepts NULL", { + expect_invisible(check_optional_nonneg_double_scalar(NULL)) +}) + +test_that("check_optional_nonneg_double_scalar passes for 0", { + expect_invisible(check_optional_nonneg_double_scalar(0)) +}) + +test_that("check_optional_nonneg_double_scalar rejects negative values", { + expect_error(check_optional_nonneg_double_scalar(-1)) +}) From a1d6b68d37fc522a24d38f196179ca5d5f212744 Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 04:07:03 -0700 Subject: [PATCH 17/18] up --- NEWS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index c7d1207..174d858 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,8 +2,8 @@ ## Version 0.1.0 -- Added S7 property library -- Expanded check_ function set +- Added S7 property library + tests +- Expanded check_ function set + tests - Updated rtemis_colors ## Version 0.0.3 From 9749a9ab987c58ef708f0c704d052e53f273357e Mon Sep 17 00:00:00 2001 From: Stathis Gennatas Date: Thu, 30 Apr 2026 04:07:21 -0700 Subject: [PATCH 18/18] check for inf --- R/check.R | 7 ++++--- R/clean.R | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/R/check.R b/R/check.R index e6874f4..8d90b8c 100644 --- a/R/check.R +++ b/R/check.R @@ -582,7 +582,8 @@ check_enum <- function(x, allowed_values, arg_name = deparse(substitute(x))) { # %% S7-parallel scalar checks ---- -# Naming mirrors the S7 properties in 00_S7_properties.R one-to-one. +# Naming aligns with the S7 properties in 00_S7_properties.R where matching +# scalar check helpers are implemented. # Use these in regular function bodies for early, user-friendly argument validation. # %% check_character_scalar ---- @@ -720,8 +721,8 @@ check_integer_scalar <- function(x, arg_name = deparse(substitute(x))) { if (!is.numeric(x)) { cli::cli_abort("{.var {arg_name}} must be numeric.") } - if (length(x) != 1L || is.na(x)) { - cli::cli_abort("{.var {arg_name}} must be a single non-NA number.") + if (length(x) != 1L || is.na(x) || !is.finite(x)) { + cli::cli_abort("{.var {arg_name}} must be a single finite non-NA number.") } if (x != round(x)) { cli::cli_abort("{.var {arg_name}} must be a whole number.") diff --git a/R/clean.R b/R/clean.R index 99f44c5..da5140e 100644 --- a/R/clean.R +++ b/R/clean.R @@ -29,7 +29,7 @@ clean_int <- function(x, arg_name = deparse(substitute(x))) { if (is.integer(x)) { return(x) } else if (is.numeric(x)) { - if (all(x %% 1 == 0)) { + if (all(is.finite(x)) && all(x %% 1 == 0)) { storage.mode(x) <- "integer" return(x) } else {