This document serves as an overview for measuring the performance of `RcppAlgos` against other tools for generating combinations, permutations, and partitions. This stackoverflow post: How to generate permutations or combinations of object in R? has some benchmarks. You will note that the examples in that post are relatively small. The benchmarks below will focus on larger examples where performance really matters and for this reason we only consider the packages arrangements, partitions, and RcppAlgos.

## Setup Information

For the benchmarks below, we used a `2025 Macbook Air Apple M4 24 GB` machine.

    library(RcppAlgos)
    library(partitions)
    library(arrangements)
    #>
    #> Attaching package: 'arrangements'
    #> The following object is masked from 'package:partitions':
    #>
    #>     compositions
    library(microbenchmark)

    options(digits = 4)
    options(width = 90)

    pertinent_output <- capture.output(sessionInfo())
    cat(paste(pertinent_output[1:3], collapse = "\n"))
    #> R version 4.5.2 (2025-10-31)
    #> Platform: aarch64-apple-darwin20
    #> Running under: macOS Sequoia 15.7.4

    pkgs <- c("RcppAlgos", "arrangements", "partitions", "microbenchmark")
    sapply(pkgs, packageVersion, simplify = FALSE)
    #> $RcppAlgos
    #> [1] '2.10.0'
    #>
    #> $arrangements
    #> [1] '1.1.9'
    #>
    #> $partitions
    #> [1] '1.10.9'
    #>
    #> $microbenchmark
    #> [1] '1.5.0'

    numThreads <- min(as.integer(RcppAlgos::stdThreadMax() / 2), 6)
    numThreads
    #> [1] 5

## Combinations

### Combinations - Distinct

    set.seed(13)
    v1 <- sort(sample(100, 30))
    m <- 21
    t1 <- comboGeneral(v1, m, Parallel = T)
    t2 <- combinations(v1, m)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 14307150       21
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = comboGeneral(v1, m, nThreads = numThreads),
                   cbRcppAlgosSer = comboGeneral(v1, m),
                   cbArrangements = combinations(v1, m),
                   times = 15, unit = "relative")
    #> Warning in microbenchmark(cbRcppAlgosPar = comboGeneral(v1, m, nThreads = numThreads), :
    #> less accurate nanosecond times to avoid potential integer overflows
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    15
    #>  cbRcppAlgosSer 3.360 3.111 3.024  3.055 3.027 2.695    15
    #>  cbArrangements 3.371 3.150 3.053  3.092 3.062 2.708    15

### Combinations - Repetition

    v2 <- v1[1:10]
    m <- 20
    t1 <- comboGeneral(v2, m, repetition = TRUE, nThreads = numThreads)
    t2 <- combinations(v2, m, replace = TRUE)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 10015005       20
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = comboGeneral(v2, m, TRUE, nThreads = numThreads),
                   cbRcppAlgosSer = comboGeneral(v2, m, TRUE),
                   cbArrangements = combinations(v2, m, replace = TRUE),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    15
    #>  cbRcppAlgosSer 3.119 2.993 2.956  2.986 2.940 2.689    15
    #>  cbArrangements 2.963 2.895 2.857  2.882 2.829 2.722    15

### Combinations - Multisets

    myFreqs <- c(2, 4, 4, 5, 3, 2, 2, 2, 3, 4, 1, 4, 2, 5)
    v3 <- as.integer(c(1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610))
    t1 <- comboGeneral(v3, 20, freqs = myFreqs, nThreads = numThreads)
    t2 <- combinations(freq = myFreqs, k = 20, x = v3)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 14594082       20
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = comboGeneral(v3, 20, freqs = myFreqs, nThreads = numThreads),
                   cbRcppAlgosSer = comboGeneral(v3, 20, freqs = myFreqs),
                   cbArrangements = combinations(freq = myFreqs, k = 20, x = v3),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    10
    #>  cbRcppAlgosSer 3.293 3.164 3.073  3.096 3.016 2.757    10
    #>  cbArrangements 6.804 6.579 6.332  6.421 6.241 5.418    10

## Permutations

### Permutations - Distinct

    v4 <- as.integer(c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59))
    t1 <- permuteGeneral(v4, 6, nThreads = numThreads)
    t2 <- permutations(v4, 6)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 8910720       6
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = permuteGeneral(v4, 6, nThreads = numThreads),
                   cbRcppAlgosSer = permuteGeneral(v4, 6),
                   cbArrangements = permutations(v4, 6),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    15
    #>  cbRcppAlgosSer 1.494 1.446 1.352  1.421 1.364 1.283    15
    #>  cbArrangements 2.177 2.110 2.032  2.081 2.135 1.793    15

    ## Indexing permutation example with the partitions package
    t1 <- permuteGeneral(11, nThreads = 4)
    t2 <- permutations(11)
    t3 <- perms(11)

    dim(t1)
    #> [1] 39916800       11

    stopifnot(identical(t1, t2), identical(t1, t(as.matrix(t3))))
    rm(t1, t2, t3)
    invisible(gc())

    microbenchmark(cbRcppAlgosPar = permuteGeneral(11, nThreads = 4),
                   cbRcppAlgosSer = permuteGeneral(11),
                   cbArrangements = permutations(11),
                   cbPartitions   = perms(11),
                   times = 5, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000     5
    #>  cbRcppAlgosSer 2.223 2.538 2.263  2.528 1.879 2.312     5
    #>  cbArrangements 3.537 3.568 3.267  3.432 2.610 3.430     5
    #>    cbPartitions 9.250 9.446 7.978  9.493 6.656 6.588     5

### Permutations - Repetition

    v5 <- v3[1:5]
    t1 <- permuteGeneral(v5, 10, repetition = TRUE, nThreads = numThreads)
    t2 <- permutations(v5, 10, replace = TRUE)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 9765625      10
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = permuteGeneral(v5, 10, TRUE, nThreads = numThreads),
                   cbRcppAlgosSer = permuteGeneral(v5, 10, TRUE),
                   cbArrangements = permutations(x = v5, k = 10, replace = TRUE),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq    max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.0000    10
    #>  cbRcppAlgosSer 2.423 2.355 1.997  2.327 2.159 0.9621    10
    #>  cbArrangements 2.822 2.768 2.494  2.712 2.521 1.8244    10

### Permutations - Multisets

    v6 <- sort(runif(12))
    t1 <- permuteGeneral(v6, 7, freqs = rep(1:3, 4), nThreads = numThreads)
    t2 <- permutations(freq = rep(1:3, 4), k = 7, x = v6)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 19520760        7
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = permuteGeneral(v6, 7, freqs = rep(1:3, 4), nThreads = numThreads),
                   cbRcppAlgosSer = permuteGeneral(v6, 7, freqs = rep(1:3, 4)),
                   cbArrangements = permutations(freq = rep(1:3, 4), k = 7, x = v6),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    10
    #>  cbRcppAlgosSer 3.473 3.511 3.153  3.367 2.552 2.731    10
    #>  cbArrangements 3.937 3.985 3.658  3.874 3.138 3.117    10

## Partitions

### Partitions - Distinct

#### All Distinct Partitions

    t1 <- comboGeneral(0:140, freqs=c(140, rep(1, 140)),
                       constraintFun = "sum", comparisonFun = "==",
                       limitConstraints = 140)
    t2 <- partitions(140, distinct = TRUE)
    t3 <- diffparts(140)

    # Each package has different output formats... we only examine dimensions
    # and that each result is a partition of 140
    stopifnot(identical(dim(t1), dim(t2)), identical(dim(t1), dim(t(t3))),
                        all(rowSums(t1) == 140), all(rowSums(t2) == 140),
                        all(colSums(t3) == 140))
    dim(t1)
    #> [1] 9617150      16
    rm(t1, t2, t3)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = partitionsGeneral(0:140, freqs=c(140, rep(1, 140)), nThreads = numThreads),
                   cbRcppAlgosSer = partitionsGeneral(0:140, freqs=c(140, rep(1, 140))),
                   cbArrangements = partitions(140, distinct = TRUE),
                   cbPartitions   = diffparts(140),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr    min     lq   mean median     uq    max neval
    #>  cbRcppAlgosPar  1.000  1.000  1.000  1.000  1.000  1.000    10
    #>  cbRcppAlgosSer  3.252  3.133  2.911  2.982  2.739  2.709    10
    #>  cbArrangements  2.492  2.431  2.256  2.292  2.168  2.146    10
    #>    cbPartitions 17.359 17.199 15.909 17.115 14.684 13.572    10

#### Restricted Distinct Partitions

    t1 <- comboGeneral(160, 10,
                       constraintFun = "sum", comparisonFun = "==",
                       limitConstraints = 160)
    t2 <- partitions(160, 10, distinct = TRUE)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 8942920      10
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = partitionsGeneral(160, 10, nThreads = numThreads),
                   cbRcppAlgosSer = partitionsGeneral(160, 10),
                   cbArrangements = partitions(160, 10, distinct = TRUE),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    10
    #>  cbRcppAlgosSer 3.153 3.082 2.713  3.017 2.973 1.902    10
    #>  cbArrangements 4.490 4.400 3.700  4.303 4.192 2.230    10

### Partitions - Repetition

#### All Partitions

    t1 <- comboGeneral(0:65, repetition = TRUE, constraintFun = "sum",
                       comparisonFun = "==", limitConstraints = 65)
    t2 <- partitions(65)
    t3 <- parts(65)

    # Each package has different output formats... we only examine dimensions
    # and that each result is a partition of 65
    stopifnot(identical(dim(t1), dim(t2)), identical(dim(t1), dim(t(t3))),
              all(rowSums(t1) == 65), all(rowSums(t2) == 65),
              all(colSums(t3) == 65))
    dim(t1)
    #> [1] 2012558      65
    rm(t1, t2, t3)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = partitionsGeneral(0:65, repetition = TRUE,
                                                      nThreads = numThreads),
                   cbRcppAlgosSer = partitionsGeneral(0:65, repetition = TRUE),
                   cbArrangements = partitions(65),
                   cbPartitions   = parts(65),
                   times = 20, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    20
    #>  cbRcppAlgosSer 2.793 2.749 2.276  2.415 2.182 1.776    20
    #>  cbArrangements 2.011 1.961 1.628  1.746 1.644 1.076    20
    #>    cbPartitions 9.885 9.768 7.711  8.600 7.274 4.866    20

#### Restricted Partitions

    t1 <- comboGeneral(100, 15, TRUE, constraintFun = "sum",
                       comparisonFun = "==", limitConstraints = 100)
    t2 <- partitions(100, 15)
    stopifnot(identical(t1, t2))
    dim(t1)
    #> [1] 9921212      15
    rm(t1, t2)

    # This takes a really long time... not because of restrictedparts,
    # but because apply is not that fast. This transformation is
    # needed for proper comparisons. As a result, we will compare
    # a smaller example
    # t3 <- t(apply(as.matrix(restrictedparts(100, 15, include.zero = F)), 2, sort))
    t3 <- t(apply(as.matrix(restrictedparts(50, 15, include.zero = F)), 2, sort))
    stopifnot(identical(partitions(50, 15), t3))
    rm(t3)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = partitionsGeneral(100, 15, TRUE,
                                                      nThreads = numThreads),
                   cbRcppAlgosSer = partitionsGeneral(100, 15, TRUE),
                   cbArrangements = partitions(100, 15),
                   cbPartitions   = restrictedparts(100, 15,
                                                    include.zero = FALSE),
                   times = 10, unit = "relative")
    #> Unit: relative
    #>            expr    min    lq   mean median     uq    max neval
    #>  cbRcppAlgosPar  1.000  1.00  1.000  1.000  1.000  1.000    10
    #>  cbRcppAlgosSer  3.378  3.31  2.974  2.952  2.735  2.638    10
    #>  cbArrangements  4.484  4.44  4.079  4.091  3.653  4.024    10
    #>    cbPartitions 15.181 15.09 13.452 13.576 12.017 11.979    10

### Partitions - Multisets

Currently, `RcppAlgos` is the only package capable of efficiently generating partitions of multisets. Therefore, we will only time `RcppAlgos` and use this as a reference for future improvements.

    t1 <- comboGeneral(120, 10, freqs = rep(1:8, 15),
                       constraintFun = "sum", comparisonFun = "==",
                       limitConstraints = 120)
    dim(t1)
    #> [1] 7340225      10
    stopifnot(all(rowSums(t1) == 120))
    microbenchmark(cbRcppAlgos = partitionsGeneral(120, 10, freqs = rep(1:8, 15)),
                   times = 10)
    #> Unit: milliseconds
    #>         expr min    lq  mean median    uq   max neval
    #>  cbRcppAlgos 178 180.3 182.4  181.4 182.9 189.6    10

## Compositions

### Compositions - Repetition

#### All Compositions (Small case)

    t1 <- compositionsGeneral(0:15, repetition = TRUE)
    t2 <- arrangements::compositions(15)
    t3 <- partitions::compositions(15)

    # Each package has different output formats... we only examine dimensions
    # and that each result is a partition of 15
    stopifnot(identical(dim(t1), dim(t2)), identical(dim(t1), dim(t(t3))),
              all(rowSums(t1) == 15), all(rowSums(t2) == 15),
              all(colSums(t3) == 15))
    dim(t1)
    #> [1] 16384    15
    rm(t1, t2, t3)
    invisible(gc())
    microbenchmark(cbRcppAlgosSer = compositionsGeneral(0:15, repetition = TRUE),
                   cbArrangements = arrangements::compositions(15),
                   cbPartitions   = partitions::compositions(15),
                   times = 20, unit = "relative")
    #> Unit: relative
    #>            expr     min      lq    mean  median      uq     max neval
    #>  cbRcppAlgosSer   1.107   1.101   1.098   1.096   1.094   1.053    20
    #>  cbArrangements   1.000   1.000   1.000   1.000   1.000   1.000    20
    #>    cbPartitions 110.419 117.834 145.577 145.641 165.113 191.249    20

For the next two examples, we will exclude the `partitions` package for efficiency reasons.

#### All Compositions (Larger case)

    t1 <- compositionsGeneral(0:23, repetition = TRUE)
    t2 <- arrangements::compositions(23)

    # Each package has different output formats... we only examine dimensions
    # and that each result is a partition of 23
    stopifnot(identical(dim(t1), dim(t2)), all(rowSums(t1) == 23),
              all(rowSums(t2) == 23))
    dim(t1)
    #> [1] 4194304      23
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = compositionsGeneral(0:23, repetition = TRUE,
                                                        nThreads = numThreads),
                   cbRcppAlgosSer = compositionsGeneral(0:23, repetition = TRUE),
                   cbArrangements = arrangements::compositions(23),
                   times = 20, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    20
    #>  cbRcppAlgosSer 3.833 3.639 3.562  3.580 3.526 3.051    20
    #>  cbArrangements 4.138 3.921 3.831  3.853 3.783 3.250    20

#### Compositions of Specific Length

    t1 <- compositionsGeneral(30, 10, repetition = TRUE)
    t2 <- arrangements::compositions(30, 10)

    stopifnot(identical(t1, t2), all(rowSums(t1) == 30))
    dim(t1)
    #> [1] 10015005       10
    rm(t1, t2)
    invisible(gc())
    microbenchmark(cbRcppAlgosPar = compositionsGeneral(30, 10, repetition = TRUE,
                                                        nThreads = numThreads),
                   cbRcppAlgosSer = compositionsGeneral(30, 10, repetition = TRUE),
                   cbArrangements = arrangements::compositions(30, 10),
                   times = 20, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosPar 1.000 1.000 1.000  1.000 1.000 1.000    20
    #>  cbRcppAlgosSer 3.075 2.986 2.908  2.874 2.845 2.780    20
    #>  cbArrangements 3.133 2.992 2.945  2.891 2.861 3.108    20

### Specialized Composition Benchmarks

Similar to partitions of multisets, several composition variants supported in `RcppAlgos` (e.g., distinct parts, capped cases, etc.) are not currently available in other R packages or combinatorics libraries. In such cases, benchmarks will only report timings for `RcppAlgos`, which can serve as a reference point for future implementations and improvements.

#### Compositions with Specific `target`

    t1 <- compositionsGeneral(10, 10, repetition = TRUE, target = 30)
    dim(t1)
    #> [1] 9091270      10

    stopifnot(all(rowSums(t1) == 30))
    microbenchmark(
        cbRcppAlgosSer = compositionsGeneral(10, 10, repetition = TRUE, target = 30),
        cbRcppAlgosPar = compositionsGeneral(10, 10, repetition = TRUE,
                                             target = 30, nThreads = numThreads),
        times = 10,
        check = "identical"
    )
    #> Unit: milliseconds
    #>            expr   min    lq  mean median    uq   max neval
    #>  cbRcppAlgosSer 57.44 57.49 59.77  59.17 61.68 63.69    10
    #>  cbRcppAlgosPar 17.49 17.90 23.16  21.83 25.34 39.69    10

#### Compositions with Distinct Parts

    t1 <- compositionsGeneral(50, 8)
    dim(t1)
    #> [1] 4677120       8

    stopifnot(all(rowSums(t1) == 50))
    microbenchmark(
        cbRcppAlgosSer = compositionsGeneral(50, 8),
        cbRcppAlgosPar = compositionsGeneral(50, 8, nThreads = numThreads),
        times = 10,
        check = "identical"
    )
    #> Unit: milliseconds
    #>            expr    min     lq   mean median     uq    max neval
    #>  cbRcppAlgosSer 214.49 215.25 216.22 215.53 215.99 221.19    10
    #>  cbRcppAlgosPar  52.13  53.51  55.81  55.12  56.63  64.84    10

#### Compositions with Distinct Parts & Specific `target`

    t1 <- compositionsGeneral(30, 7, target = 60)
    dim(t1)
    #> [1] 11773440        7

    stopifnot(all(rowSums(t1) == 60))
    microbenchmark(
        cbRcppAlgosSer = compositionsGeneral(30, 7, target = 60),
        cbRcppAlgosPar = compositionsGeneral(30, 7, target = 60, nThreads = numThreads),
        times = 10,
        check = "identical"
    )
    #> Unit: milliseconds
    #>            expr   min     lq   mean median     uq   max neval
    #>  cbRcppAlgosSer 359.1 359.62 363.92 361.99 364.36 380.2    10
    #>  cbRcppAlgosPar  92.9  94.45  96.98  96.17  97.18 105.7    10

## Iterators

We will show one example from each category to demonstrate the efficiency of the iterators in `RcppAlgos`. The results are similar for the rest of the cases not shown.

### Combinations

    pkg_arrangements <- function(n, total) {
        a <- icombinations(n, as.integer(n / 2))
        for (i in 1:total) a$getnext()
    }

    pkg_RcppAlgos <- function(n, total) {
        a <- comboIter(n, as.integer(n / 2))
        for (i in 1:total) a@nextIter()
    }

    total <- comboCount(18, 9)
    total
    #> [1] 48620

    microbenchmark(cbRcppAlgos    = pkg_RcppAlgos(18, total),
                   cbArrangements = pkg_arrangements(18, total),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min   lq  mean median    uq   max neval
    #>     cbRcppAlgos  1.00  1.0  1.00   1.00  1.00  1.00    15
    #>  cbArrangements 19.78 19.5 19.13  19.27 18.76 18.57    15

### Permutations

    pkg_arrangements <- function(n, total) {
        a <- ipermutations(n)
        for (i in 1:total) a$getnext()
    }

    pkg_RcppAlgos <- function(n, total) {
        a <- permuteIter(n)
        for (i in 1:total) a@nextIter()
    }

    total <- permuteCount(8)
    total
    #> [1] 40320

    microbenchmark(cbRcppAlgos    = pkg_RcppAlgos(8, total),
                   cbArrangements = pkg_arrangements(8, total),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>     cbRcppAlgos  1.00  1.00  1.00   1.00  1.00  1.00    15
    #>  cbArrangements 19.42 19.19 18.87  18.76 18.38 19.43    15

### Partitions

    pkg_partitions <- function(n, total) {
        a <- firstpart(n)
        for (i in 1:(total - 1)) a <- nextpart(a)
    }

    pkg_arrangements <- function(n, total) {
        a <- ipartitions(n)
        for (i in 1:total) a$getnext()
    }

    pkg_RcppAlgos <- function(n, total) {
        a <- partitionsIter(0:n, repetition = TRUE)
        for (i in 1:total) a@nextIter()
    }

    total <- partitionsCount(0:40, repetition = TRUE)
    total
    #> [1] 37338

    microbenchmark(cbRcppAlgos    = pkg_RcppAlgos(40, total),
                   cbArrangements = pkg_arrangements(40, total),
                   cbPartitions   = pkg_partitions(40, total),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq  mean median    uq   max neval
    #>     cbRcppAlgos  1.00  1.00  1.00   1.00  1.00  1.00    15
    #>  cbArrangements 15.38 15.19 14.39  15.04 13.39 13.75    15
    #>    cbPartitions 24.48 24.25 22.94  23.91 21.30 21.55    15

### Compositions

    pkg_partitions <- function(n, total) {
        a <- firstcomposition(n)
        for (i in 1:(total - 1)) a <- nextcomposition(a, FALSE)
    }

    pkg_arrangements <- function(n, total) {
        a <- icompositions(n)
        for (i in 1:total) a$getnext()
    }

    pkg_RcppAlgos <- function(n, total) {
        a <- compositionsIter(0:n, repetition = TRUE)
        for (i in 1:total) a@nextIter()
    }

    total <- compositionsCount(0:15, repetition = TRUE)
    total
    #> [1] 16384

    microbenchmark(cbRcppAlgos    = pkg_RcppAlgos(15, total),
                   cbArrangements = pkg_arrangements(15, total),
                   cbPartitions   = pkg_partitions(15, total),
                   times = 15, unit = "relative")
    #> Unit: relative
    #>            expr   min    lq mean median    uq   max neval
    #>     cbRcppAlgos  1.00  1.00  1.0   1.00  1.00  1.00    15
    #>  cbArrangements 14.35 14.16 13.8  14.01 13.81 12.46    15
    #>    cbPartitions 44.92 44.54 43.8  44.08 43.62 41.54    15