diff --git a/R/bitset.R b/R/bitset.R
index e600680..556c560 100644
--- a/R/bitset.R
+++ b/R/bitset.R
@@ -1,3 +1,41 @@
+#' Generate documentation for an R6-like method
+#'
+#' The Bitset class is implemented as a named list with closures that capture
+#' the environment. By default, roxygen2 generates terrible documentation for
+#' it since it isn't a typical way of doing things.
+#'
+#' This method generates a snippet of Rd code for a method, in a way that
+#' resembles the code roxygen2 generates for R6 methods.
+#'
+#' @noRd
+bitset_method_doc <- function(name, description, static = FALSE, ...) {
+ # nocov start: this is only used for documentation generation
+ lines <- character()
+ push <- function(...) lines <<- c(lines, ...)
+
+ arguments <- list(...)
+ argnames <- paste(names(arguments), collapse=", ")
+ receiver <- if (static) "Bitset" else "b"
+
+ push("\\if{html}{\\out{
}}")
+ push(paste0("\\subsection{Method \\code{", name, "()}}{"))
+ push(description)
+ push("\\subsection{Usage}{")
+ push(sprintf("\\preformatted{%s$%s(%s)}", receiver, name, argnames))
+ push("}")
+ if (length(arguments) > 0) {
+ push("\\subsection{Arguments}{")
+ push("\\describe{")
+ push(sprintf("\\item{\\code{%s}}{%s}", names(arguments), arguments))
+ push("}")
+ push("}")
+ }
+ push("}")
+
+ cat(paste(lines, collapse="\n"))
+ # nocov end
+}
+
#' @title A Bitset Class
#' @description This is a data structure that compactly stores the presence of
#' integers in some finite set (\code{max_size}), and can
@@ -5,126 +43,188 @@
#' difference, set difference).
#' WARNING: All operations are in-place so please use \code{$copy}
#' if you would like to perform an operation without destroying your current bitset.
-#' @importFrom R6 R6Class
+#'
+#' This class is defined as a named list for performance reasons, but for most
+#' intents and purposes it behaves just like an R6 class.
+#' @format NULL
+#' @usage NULL
+#' @docType NULL
+#' @keywords NULL
#' @export
-Bitset <- R6Class(
- 'Bitset',
- public = list(
- #' @field .bitset a pointer to the underlying IterableBitset.
- .bitset = NULL,
-
- #' @field max_size the maximum size of the bitset.
- max_size = 0,
-
- #' @description create a bitset.
- #' @param size the size of the bitset.
- #' @param from pointer to an existing IterableBitset to use; if \code{NULL}
- #' make empty bitset, otherwise copy existing bitset.
- initialize = function(size, from = NULL) {
- if (is.null(from)) {
- self$.bitset <- create_bitset(size)
- } else {
- stopifnot(inherits(from, "externalptr"))
- self$.bitset <- from
- }
- self$max_size <- bitset_max_size(self$.bitset)
- },
-
- #' @description insert into the bitset.
- #' @param v an integer vector of elements to insert.
- insert = function(v) {
- bitset_insert(self$.bitset, v)
- self
- },
-
- #' @description remove from the bitset.
- #' @param v an integer vector of elements (not indices) to remove.
- remove = function(v) {
- bitset_remove(self$.bitset, v)
- self
- },
-
- #' @description clear the bitset.
- clear = function() {
- bitset_clear(self$.bitset)
- self
- },
-
- #' @description get the number of elements in the set.
- size = function() bitset_size(self$.bitset),
-
- #' @description to "bitwise or" or union two bitsets.
- #' @param other the other bitset.
- or = function(other) {
- bitset_or(self$.bitset, other$.bitset)
- self
- },
-
- #' @description to "bitwise and" or intersect two bitsets.
- #' @param other the other bitset.
- and = function(other) {
- bitset_and(self$.bitset, other$.bitset)
- self
- },
-
- #' @description to "bitwise not" or complement a bitset.
- #' @param inplace whether to overwrite the current bitset, default = TRUE
- not = function(inplace = TRUE) {
- Bitset$new(from = bitset_not(self$.bitset, inplace))
- },
-
- #' @description to "bitwise xor" or get the symmetric difference of two bitset
- #' (keep elements in either bitset but not in their intersection).
- #' @param other the other bitset.
- xor = function(other){
- bitset_xor(self$.bitset, other$.bitset)
- self
- },
-
- #' @description Take the set difference of this bitset with another
- #' (keep elements of this bitset which are not in \code{other}).
- #' @param other the other bitset.
- set_difference = function(other){
- bitset_set_difference(self$.bitset, other$.bitset)
- self
- },
-
- #' @description sample a bitset.
- #' @param rate the success probability for keeping each element, can be
- #' a single value for all elements or a vector of unique
- #' probabilities for keeping each element.
- sample = function(rate) {
- stopifnot(is.finite(rate), !is.null(rate))
- if (length(rate) == 1) {
- bitset_sample(self$.bitset, rate)
- } else {
- bitset_sample_vector(self$.bitset, rate)
- }
- self
- },
-
- #' @description choose k random items in the bitset
- #' @param k the number of items in the bitset to keep. The selection of
- #' these k items from N total items in the bitset is random, and
- #' k should be chosen such that \eqn{0 \le k \le N}.
- choose = function(k) {
- stopifnot(is.finite(k))
- stopifnot(k <= bitset_size(self$.bitset))
- stopifnot(k >= 0)
- if (k < self$max_size) {
- bitset_choose(self$.bitset, as.integer(k))
- }
- self
- },
-
- #' @description returns a copy the bitset.
- copy = function() Bitset$new(from = bitset_copy(self$.bitset)),
-
- #' @description return an integer vector of the elements
- #' stored in this bitset.
- to_vector = function() bitset_to_vector(self$.bitset)
-
- )
+#' @section Methods:
+Bitset <- list(
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "new",
+ #' "create a bitset.",
+ #' static = TRUE,
+ #' size = "the size of the bitset.",
+ #' from = "pointer to an existing IterableBitset to use; if \\code{NULL}
+ #' make empty bitset, otherwise copy existing bitset."
+ #' )
+ #' ```
+ new = function(size, from = NULL) {
+ if (is.null(from)) {
+ bitset <- create_bitset(size)
+ } else {
+ stopifnot(inherits(from, "externalptr"))
+ bitset <- from
+ }
+ max_size <- bitset_max_size(bitset)
+
+ self <- list(
+ .bitset = bitset,
+ max_size = max_size,
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "insert",
+ #' "insert into the bitset.",
+ #' v = "an integer vector of elements to insert.")
+ #' ```
+ insert = function(v) {
+ bitset_insert(self$.bitset, v)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "remove",
+ #' "remove from the bitset.",
+ #' v = "an integer vector of elements (not indices) to remove.")
+ #' ```
+ remove = function(v) {
+ bitset_remove(self$.bitset, v)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "clear",
+ #' "clear the bitset.")
+ #' ```
+ clear = function() {
+ bitset_clear(self$.bitset)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "size",
+ #' "get the number of elements in the set.")
+ #' ```
+ size = function() bitset_size(self$.bitset),
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "or",
+ #' "to \"bitwise or\" or union two bitsets.",
+ #' other = "the other bitset.")
+ #' ```
+ or = function(other) {
+ bitset_or(self$.bitset, other$.bitset)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "and",
+ #' "to \"bitwise and\" or intersect two bitsets.",
+ #' other = "the other bitset.")
+ #' ```
+ and = function(other) {
+ bitset_and(self$.bitset, other$.bitset)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "not",
+ #' "to \"bitwise not\" or complement a bitset.",
+ #' inplace = "whether to overwrite the current bitset, default = TRUE")
+ #' ```
+ not = function(inplace = TRUE) {
+ Bitset$new(from = bitset_not(self$.bitset, inplace))
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "xor",
+ #' "to \"bitwise xor\" get the symmetric difference of two bitset
+ #' (keep elements in either bitset but not in their intersection).",
+ #' other = "the other bitset.")
+ #' ```
+ xor = function(other){
+ bitset_xor(self$.bitset, other$.bitset)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "set_difference",
+ #' "Take the set difference of this bitset with another
+ #' (keep elements of this bitset which are not in \\code{other})",
+ #' other = "the other bitset.")
+ #' ```
+ set_difference = function(other){
+ bitset_set_difference(self$.bitset, other$.bitset)
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "sample",
+ #' "sample a bitset.",
+ #' rate = "the success probability for keeping each element, can be
+ #' a single value for all elements or a vector of unique
+ #' probabilities for keeping each element.")
+ #' ```
+ sample = function(rate) {
+ stopifnot(is.finite(rate), !is.null(rate))
+ if (length(rate) == 1) {
+ bitset_sample(self$.bitset, rate)
+ } else {
+ bitset_sample_vector(self$.bitset, rate)
+ }
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "choose",
+ #' "choose k random items in the bitset.",
+ #' k = "the number of items in the bitset to keep. The selection of
+ #' these k items from N total items in the bitset is random, and
+ #' k should be chosen such that \\eqn{0 \\le k \\le N}.")
+ #' ```
+ choose = function(k) {
+ stopifnot(is.finite(k))
+ stopifnot(k <= bitset_size(self$.bitset))
+ stopifnot(k >= 0)
+ if (k < self$max_size) {
+ bitset_choose(self$.bitset, as.integer(k))
+ }
+ self
+ },
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "copy",
+ #' "returns a copy of the bitset.")
+ #' ```
+ copy = function() Bitset$new(from = bitset_copy(self$.bitset)),
+
+ #' ```{r echo=FALSE, results="asis"}
+ #' bitset_method_doc(
+ #' "to_vector",
+ #' "return an integer vector of the elements stored in this bitset.")
+ #' ```
+ to_vector = function() bitset_to_vector(self$.bitset)
+ )
+
+ class(self) <- 'Bitset'
+ self
+ }
)
#' @title Filter a bitset
diff --git a/man/Bitset.Rd b/man/Bitset.Rd
index cf9a03e..9955742 100644
--- a/man/Bitset.Rd
+++ b/man/Bitset.Rd
@@ -10,271 +10,166 @@ efficiently perform set operations (union, intersection, complement, symmetric
difference, set difference).
WARNING: All operations are in-place so please use \code{$copy}
if you would like to perform an operation without destroying your current bitset.
-}
-\section{Public fields}{
-\if{html}{\out{}}
-\describe{
-\item{\code{.bitset}}{a pointer to the underlying IterableBitset.}
-\item{\code{max_size}}{the maximum size of the bitset.}
-}
-\if{html}{\out{
}}
+This class is defined as a named list for performance reasons, but for most
+intents and purposes it behaves just like an R6 class.
}
\section{Methods}{
-\subsection{Public methods}{
-\itemize{
-\item \href{#method-Bitset-new}{\code{Bitset$new()}}
-\item \href{#method-Bitset-insert}{\code{Bitset$insert()}}
-\item \href{#method-Bitset-remove}{\code{Bitset$remove()}}
-\item \href{#method-Bitset-clear}{\code{Bitset$clear()}}
-\item \href{#method-Bitset-size}{\code{Bitset$size()}}
-\item \href{#method-Bitset-or}{\code{Bitset$or()}}
-\item \href{#method-Bitset-and}{\code{Bitset$and()}}
-\item \href{#method-Bitset-not}{\code{Bitset$not()}}
-\item \href{#method-Bitset-xor}{\code{Bitset$xor()}}
-\item \href{#method-Bitset-set_difference}{\code{Bitset$set_difference()}}
-\item \href{#method-Bitset-sample}{\code{Bitset$sample()}}
-\item \href{#method-Bitset-choose}{\code{Bitset$choose()}}
-\item \href{#method-Bitset-copy}{\code{Bitset$copy()}}
-\item \href{#method-Bitset-to_vector}{\code{Bitset$to_vector()}}
-\item \href{#method-Bitset-clone}{\code{Bitset$clone()}}
-}
-}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-new}{}}}
\subsection{Method \code{new()}}{
create a bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$new(size, from = NULL)}\if{html}{\out{
}}
+\preformatted{Bitset$new(size, from)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{size}}{the size of the bitset.}
-
\item{\code{from}}{pointer to an existing IterableBitset to use; if \code{NULL}
make empty bitset, otherwise copy existing bitset.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-insert}{}}}
\subsection{Method \code{insert()}}{
insert into the bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$insert(v)}\if{html}{\out{
}}
+\preformatted{b$insert(v)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{v}}{an integer vector of elements to insert.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-remove}{}}}
\subsection{Method \code{remove()}}{
remove from the bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$remove(v)}\if{html}{\out{
}}
+\preformatted{b$remove(v)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{v}}{an integer vector of elements (not indices) to remove.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-clear}{}}}
\subsection{Method \code{clear()}}{
clear the bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$clear()}\if{html}{\out{
}}
+\preformatted{b$clear()}
}
-
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-size}{}}}
\subsection{Method \code{size()}}{
get the number of elements in the set.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$size()}\if{html}{\out{
}}
+\preformatted{b$size()}
}
-
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-or}{}}}
\subsection{Method \code{or()}}{
to "bitwise or" or union two bitsets.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$or(other)}\if{html}{\out{
}}
+\preformatted{b$or(other)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{other}}{the other bitset.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-and}{}}}
\subsection{Method \code{and()}}{
to "bitwise and" or intersect two bitsets.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$and(other)}\if{html}{\out{
}}
+\preformatted{b$and(other)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{other}}{the other bitset.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-not}{}}}
\subsection{Method \code{not()}}{
to "bitwise not" or complement a bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$not(inplace = TRUE)}\if{html}{\out{
}}
+\preformatted{b$not(inplace)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{inplace}}{whether to overwrite the current bitset, default = TRUE}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-xor}{}}}
\subsection{Method \code{xor()}}{
-to "bitwise xor" or get the symmetric difference of two bitset
+to "bitwise xor" get the symmetric difference of two bitset
(keep elements in either bitset but not in their intersection).
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$xor(other)}\if{html}{\out{
}}
+\preformatted{b$xor(other)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{other}}{the other bitset.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-set_difference}{}}}
\subsection{Method \code{set_difference()}}{
Take the set difference of this bitset with another
-(keep elements of this bitset which are not in \code{other}).
+(keep elements of this bitset which are not in \code{other})
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$set_difference(other)}\if{html}{\out{
}}
+\preformatted{b$set_difference(other)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{other}}{the other bitset.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-sample}{}}}
\subsection{Method \code{sample()}}{
sample a bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$sample(rate)}\if{html}{\out{
}}
+\preformatted{b$sample(rate)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{rate}}{the success probability for keeping each element, can be
a single value for all elements or a vector of unique
probabilities for keeping each element.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-choose}{}}}
\subsection{Method \code{choose()}}{
-choose k random items in the bitset
+choose k random items in the bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$choose(k)}\if{html}{\out{
}}
+\preformatted{b$choose(k)}
}
-
\subsection{Arguments}{
-\if{html}{\out{}}
\describe{
\item{\code{k}}{the number of items in the bitset to keep. The selection of
these k items from N total items in the bitset is random, and
k should be chosen such that \eqn{0 \le k \le N}.}
}
-\if{html}{\out{
}}
}
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-copy}{}}}
\subsection{Method \code{copy()}}{
-returns a copy the bitset.
+returns a copy of the bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$copy()}\if{html}{\out{
}}
+\preformatted{b$copy()}
}
-
}
\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-to_vector}{}}}
\subsection{Method \code{to_vector()}}{
-return an integer vector of the elements
-stored in this bitset.
+return an integer vector of the elements stored in this bitset.
\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$to_vector()}\if{html}{\out{
}}
+\preformatted{b$to_vector()}
}
-
}
-\if{html}{\out{
}}
-\if{html}{\out{}}
-\if{latex}{\out{\hypertarget{method-Bitset-clone}{}}}
-\subsection{Method \code{clone()}}{
-The objects of this class are cloneable with this method.
-\subsection{Usage}{
-\if{html}{\out{}}\preformatted{Bitset$clone(deep = FALSE)}\if{html}{\out{
}}
}
-\subsection{Arguments}{
-\if{html}{\out{}}
-\describe{
-\item{\code{deep}}{Whether to make a deep clone.}
-}
-\if{html}{\out{
}}
-}
-}
-}
diff --git a/tests/performance/bench-bitset.R b/tests/performance/bench-bitset.R
index 87899a1..1f2abff 100644
--- a/tests/performance/bench-bitset.R
+++ b/tests/performance/bench-bitset.R
@@ -88,6 +88,20 @@ ggplot(data = core_ops_bset) +
limit_args_grid <- data.frame(limit = 10^(3:8))
+create_bset <- bench::press(
+ {
+ bench::mark(
+ min_iterations = 100,
+ check = FALSE,
+ filter_gc = TRUE,
+ {Bitset$new(size = limit)}
+ )
+ },
+ .grid = limit_args_grid
+)
+
+create_bset <- simplify_bench_output(create_bset)
+
# clear
clear_bset <- bench::press(
{
@@ -119,6 +133,12 @@ not_bset <- bench::press(
not_bset <- simplify_bench_output(out = not_bset)
+ggplot(data = create_bset) +
+ geom_violin(aes(x = expression, y = time, color = expression, fill = expression)) +
+ facet_wrap(. ~ limit, scales = "free") +
+ coord_flip() +
+ ggtitle("Create benchmark")
+
ggplot(data = clear_bset) +
geom_violin(aes(x = expression, y = time, color = expression, fill = expression)) +
facet_wrap(. ~ limit, scales = "free") +
@@ -257,4 +277,4 @@ filter_bset <- simplify_bench_output(filter_bset)
ggplot(data = filter_bset) +
geom_violin(aes(x = as.factor(expression), y = time, color = expression, fill = expression)) +
facet_wrap(size ~ limit, scales = "free") +
- ggtitle("Sampling operations benchmark: filter")
\ No newline at end of file
+ ggtitle("Sampling operations benchmark: filter")