UCSF-CBI / rstudio-server-controller

RStudio Server Controller (RSC) - A Tool for Launching a Personal Instance of the RStudio Server
https://github.com/UCSF-CBI/rstudio-server-controller
ISC License
4 stars 2 forks source link

CLEANUP: Reimplement port RNG using linear congruential generator #66

Closed HenrikBengtsson closed 2 years ago

HenrikBengtsson commented 2 years ago

By implementing a good-enough RNG based on Linear Congruential Generator (LCG) we can:

With a generic freeport tool, we can re-use it here in the RSC, and later for a Jupyter Lab Controller, etc. The user (UID) will be able to produce reproducible pseudo-random port sequences, that can be seeded by both UID and the software label, e.g. ($UID, software_id) and ($UID, software_id), where we can generate a semi-unique software_id from the software label (e.g. rsc, jlc, ...) using some basic hash algorithm, e.g. the Java convention (https://github.com/HenrikBengtsson/R.oo/blob/d8cbc39888e996284d5f3d057d0ad96460fd3497/R/hashCode.R#L22-L27)

HenrikBengtsson commented 2 years ago

Gist in R:

#' Linear Congruential Generator
#'
#' @return
#' An integer in `{0, 1, ..., modulus-1}`.
#'
#' @reference
#' https://en.wikipedia.org/wiki/Linear_congruential_generator
lcg <- local({
  .seed <- NULL

  function(modulus = 2^31, a = 1103515245, c = 12345, seed = NULL) {
    stopifnot(
      length(modulus) == 1L, is.numeric(modulus), !is.na(modulus),
      length(a) == 1L, is.numeric(a), !is.na(a),
      length(c) == 1L, is.numeric(c), !is.na(c)
    )

    if (!is.null(seed)) {
      stopifnot(length(seed) == 1L, is.numeric(seed), !is.na(seed))
      .seed <<- seed
    }

    if (is.null(.seed)) {
      stop("Argument 'seed' must be specified at least once")
    }

    .seed <<- (a * .seed + c) %% modulus

    .seed
  }
})

lcg_integer <- function(min, max, seed = NULL) {
  stopifnot(
    length(min) == 1L, is.numeric(min), !is.na(min),
    length(max) == 1L, is.numeric(max), !is.na(max),
    min <= max
  )
  lcg(seed = seed) %% (max - min) + min
}

lcg_port <- function(min = 1024, max = 65535, seed = NULL) {
  random_integer(min = min, max = max, seed = seed)
}
## Initiate
> lcg(seed = 42)
[1] 1250496027

## Draw random ports from the LCG RNG stream
> lcg_port()
[1] 4760
> lcg_port()
[1] 7673
HenrikBengtsson commented 2 years ago

Moved to a standalone project: https://github.com/HenrikBengtsson/port4me. The idea is then to either depend on port4me as an external tool, or incorporate it as internal code (100% Bash).