ids/0000755000176200001440000000000013113501667011034 5ustar liggesusersids/inst/0000755000176200001440000000000013113306437012007 5ustar liggesusersids/inst/doc/0000755000176200001440000000000013113306437012554 5ustar liggesusersids/inst/doc/ids.html0000644000176200001440000011117113113306437014223 0ustar liggesusers ids

The ids package provides randomly generated ids in a number of different forms with different readability and sizes.

Random bytes

The random_id function generates random identifiers by generating bytes random bytes and converting to hexadecimal (so each byte becomes a pair of characters). Rather than use R’s random number stream we use the openssl package here.

ids::random_id()
## [1] "9aa4d8e7fd4fd2d2007c907f2f43262f"

All ids functions take n as the first argument to be the number of identifiers generated:

ids::random_id(5)
## [1] "c9ef83cddb1c01acdd5c4ca1c6f80d65" "ce43343eebc5cd985b60feb2c7d7cb32"
## [3] "14d2e59fe7af4a6771de5d40c4859460" "03146fbcc3b7c5b1a3a5bf579187a313"
## [5] "0ef43dce811ecba10daeb85a1212b858"

The default here is 16 bytes, each of which has 256 values (so 256^16 = 2^128 = 3.4e38 combinations). You can make these larger or smaller with the bytes argument:

ids::random_id(5, 8)
## [1] "a2a3873e4d4eb769" "17ef56682957f105" "6ba5316397c06b45"
## [4] "702caf1aaf2a1b13" "e155a0e71e524e69"

If NULL is provided as n, then a generating function is returned (all ids functions do this):

f <- ids::random_id(NULL, 8)
f
## function (n = 1) 
## {
##     random_id(n, bytes, use_openssl)
## }
## <environment: 0x2c0def0>

This function sets all arguments except for n

f()
## [1] "a54ebe0cea6d08fd"
f(4)
## [1] "bc03e2e830276c4f" "a194b7d74d635a5f" "c5b40b60c0fbd303"
## [4] "887cd4842022e54a"

UUIDs

The above look a lot like UUIDs but they are not actually UUIDs. The uuid package provides real UUIDs generated with libuuid, and the ids::uuid function provides an interface to that:

ids::uuid()
## [1] "34820d25-2632-4200-b0e5-78cf930911fe"

As above, generate more than one UUID:

ids::uuid(4)
## [1] "dbf9c608-24e3-4dcd-b358-43f0a31ebea2"
## [2] "fe92be0e-add9-443b-a83e-160d2c6988ec"
## [3] "04235c12-0da1-4bb2-9158-b518aae17c88"
## [4] "ef58c280-6155-4e20-b7c9-e26d01c78a7b"

Generate time-based UUIDs:

ids::uuid(4, use_time = TRUE)
## [1] "24fc48f4-454b-11e7-8967-5065f32b99c0"
## [2] "24fc4962-454b-11e7-8967-5065f32b99c0"
## [3] "24fc49b2-454b-11e7-8967-5065f32b99c0"
## [4] "24fc49f8-454b-11e7-8967-5065f32b99c0"

and optionally drop the hyphens:

ids::uuid(5, drop_hyphens = TRUE)
## [1] "64f8abb9462949afb6feaf1c3598ca17" "4d2ae53b4c6a4b60b5e193355205a76f"
## [3] "2f78096c87df4b11ac3562733bd292c5" "cf0159ebaa66456aa3d9f3b4ee1254b1"
## [5] "34c2390f6abe4f2a8f2c1e08db458a1f"

Adjective animal

Generate (somewhat) human readable identifiers by combining one or more adjectives with an animal name.

ids::adjective_animal()
## [1] "displeasing_fritillarybutterfly"

The list of adjectives and animals comes from gfycat.com, via https://github.com/a-type/adjective-adjective-animal

Generate more than one identifier:

ids::adjective_animal(4)
## [1] "nitpicking_whiteeye"      "undivined_nene"          
## [3] "crazy_mockingbird"        "uncongested_amurstarfish"

Use more than one adjective for very long idenfiers

ids::adjective_animal(4, 3)
## [1] "crooked_serpentine_fungoid_nabarlek"                  
## [2] "conservable_kimberlite_sightly_axisdeer"              
## [3] "psychopharmacological_releasable_unquenchable_cricket"
## [4] "gastric_worldlywise_cumbersome_gar"

There are 1748 animal names and 8946 adjectives so each one you add increases the idenfier space by a factor of 8946. So for 1, 2, and 3 adjectives there are about 15.6 million, 140 billion and 1250 trillion possible combinations.

This is a much smaller space than the random identifiers above, but these are more readable and memorable.

Note that here, the random nunbers are coming from R’s random number stream so are affected by set.seed().

Because some of the animal and adjective names are very long (e.g. a quasiextraterritorial hexakosioihexekontahexaphobic queenalexandrasbirdwingbutterfly), in order to generate more readable/memorable identifiers it may be useful to restrict the length. Pass max_len in to do this.

ids::adjective_animal(4, max_len = 6)
## [1] "adored_kawala" "gassy_morpho"  "tame_kiwi"     "flinty_cony"

A vector of length 2 here can be used to apply to the adjectives and animal respectively:

ids::adjective_animal(20, max_len = c(5, Inf))
##  [1] "sour_anhinga"             "phony_armyant"           
##  [3] "slimy_dwarfrabbit"        "brisk_insect"            
##  [5] "right_neonbluehermitcrab" "heavy_godwit"            
##  [7] "sooty_urutu"              "other_doctorfish"        
##  [9] "silt_herculesbeetle"      "manic_emperorshrimp"     
## [11] "loved_mynah"              "skarn_cormorant"         
## [13] "alive_howlermonkey"       "kooky_raptors"           
## [15] "rocky_appaloosa"          "pudgy_tayra"             
## [17] "kooky_estuarinecrocodile" "swift_sphinx"            
## [19] "icky_fattaileddunnart"    "dry_englishpointer"

Note that this decreases the pool size and so increases the chance of collisions.

In addition to snake_case, the default, the punctuation between words can be changed to:

kebab-case:

ids::adjective_animal(1, 2, style = "kebab")
## [1] "intact-unauthorized-swallowtailbutterfly"

dot.case:

ids::adjective_animal(1, 2, style = "dot")
## [1] "germinable.sighted.wrasse"

camel-case:

ids::adjective_animal(1, 2, style = "camel")
## [1] "holographicRefillableGiantschnauzer"

PascalCase:

ids::adjective_animal(1, 2, style = "pascal")
## [1] "EvadibleSelfhonouredBufflehead"

CONSTANT_CASE (aka SHOUTY_CASE)

ids::adjective_animal(1, 2, style = "constant")
## [1] "RED_CENTAURIAN_COCKERSPANIEL"

or with spaces, lower case:

ids::adjective_animal(1, 2, style = "lower")
## [1] "civic depressed amoeba"

UPPER CASE

ids::adjective_animal(1, 2, style = "upper")
## [1] "PHANTASMAGORIAL TRICHOPHOBIA RINGTAILEDLEMUR"

Sentence case

ids::adjective_animal(1, 2, style = "sentence")
## [1] "Sorcerous imaginative goldfinch"

Title Case

ids::adjective_animal(1, 2, style = "title")
## [1] "Starchy Omnivorous Nandine"

Again, pass n = NULL here to create a generating function:

aa3 <- ids::adjective_animal(NULL, 3, style = "kebab", max_len = c(6, 8))

…which can be used to generate ids on demand.

aa3()
## [1] "former-dry-yester-noctule"
aa3(4)
## [1] "curt-murky-harsh-blobfish"  "catty-wintry-chuffy-isopod"
## [3] "jaded-rustic-adored-nymph"  "upper-freaky-rival-yucker"

Random sentences

The sentence function creates a sentence style identifier. This uses the approach described by Asana on their blog. This approach encodes 32 bits of information (so 2^32 ~= 4 billion possibilities) and in theory can be remapped to an integer if you really wanted to.

ids::sentence()
## [1] "17_flippant_chipmunks_striding_jubilantly"

As with adjective_animal, the case can be changed:

ids::sentence(2, "dot")
## [1] "18.colossal.moles.bursting.unabashedly"
## [2] "8.groovy.beavers.slurping.obnoxiously"

If you would rather past tense for the verbs, then pass past = TRUE:

ids::sentence(4, past = TRUE)
## [1] "19_jolly_porcupines_clamored_gently" 
## [2] "18_groovy_impalas_twisted_jubilantly"
## [3] "33_cool_chipmunks_skipped_loftily"   
## [4] "18_cuddly_pumas_sang_courageously"

proquints

“proquints” are an identifier that tries to be information dense but still human readable and (somewhat) pronounceable; “proquint” stands for PRO-nouncable QUINT-uplets. They are introduced in https://arxiv.org/html/0901.4016

ids can generate proquints:

ids::proquint(10)
##  [1] "mivij-dataj" "hisir-ginug" "hogom-vihoh" "lamus-sivot" "vajub-kolaf"
##  [6] "dadij-jilof" "rojak-jinot" "pajof-tisuf" "tosor-kibub" "kajom-vihur"

By default it generates two-word proquints but that can be changed:

ids::proquint(5, 1)
## [1] "pigud" "rukul" "noruj" "visub" "hosud"
ids::proquint(2, 4)
## [1] "gaguk-varov-magip-vafak" "gadov-samig-rojum-zakil"

Proquints are formed by alternating consonant/vowel/consonant/vowel/consonant using a subset of both (16 consonants and 4 vowels). This yields 2^16 (65,536) possibilities per word. Words are always lower case and always separated by a hyphen. So with 4 words there are 2^64 combinations in 23 characters.

Proquints are also useful in that they can be tranlated with integers. The proquint kapop has integer value 25258

ids::proquint_to_int("kapop")
## [1] 25258
ids::int_to_proquint(25258)
## [1] "kapop"

This makes proquints suitable for creating human-pronouncable identifers out of things like ip addresses, integer primary keys, etc.

The function ids::int_to_proquint_word will translate between proquint words and integers (and are vectorised)

w <- ids::int_to_proquint_word(sample(2^16, 10) - 1L)
w
##  [1] "mubuf" "rimuh" "kavav" "dolif" "vujiz" "hofab" "nisos" "dubuz"
##  [9] "tisoh" "jajog"

and ids::proquint_word_to_int does the reverse

ids::proquint_word_to_int(w)
##  [1] 35890 46644 25486  6610 60767 18560 38700  7231 55076 20835

whille ids::proquint_to_int and ids::int_to_proquint allows translation of multi-word proquints. Overflow is a real possibility; the maximum integer representable is only about r human_no(.Machine$integer.max) and the maximum floating point number of accuracy of 1 is about 9010 trillion – these are big numbers but fairly small proquints:

ids::int_to_proquint(.Machine$integer.max - 1)
## [1] "luzuz-zuzuv"
ids::int_to_proquint(2 / .Machine$double.eps)
## [1] "babob-babab-babab-babab"

But if you had a 6 word proquint this would not work!

p <- ids::proquint(1, 6)

Too big for an integer:

ids::proquint_to_int(p)
## Error in proquint_combine(idx, len, as): Numeric overflow: cannot represent proquint as numeric

And too big for an numeric number:

ids::proquint_to_int(p, as = "numeric")
## Error in proquint_combine(idx, len, as): Numeric overflow: cannot represent proquint as numeric

To allow this, we use openssl’s bignum support:

ids::proquint_to_int(p, as = "bignum")
## [[1]]
## [b] 62023053643412691785669488225

This returns a list with one bignum (this is required to allow vectorisation).

Roll your own identifiers

The ids functions can build identifiers in the style of adjective_animal or sentence. It takes as input a list of strings. This works particularly well with the rcorpora package which includes lists of strings.

Here is a list of Pokemon names:

pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name)
length(pokemon)
## [1] 663

…and here is a list of adjectives

adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs)
length(adjectives)
## [1] 962

So we have a total pool size of about 638 thousand, which is not huge, but it is at least topical.

To generate one identifier:

ids::ids(1, adjectives, pokemon)
## [1] "long-suffering_beldum"

All the style-changing code is available:

ids::ids(10, adjectives, pokemon, style = "dot")
##  [1] "canonical.deino"     "eaten.deino"         "remorseless.mantine"
##  [4] "veritable.togepi"    "blue-collar.weezing" "seated.heracross"   
##  [7] "useless.barboach"    "spineless.cubchoo"   "fading.barboach"    
## [10] "pitching.lanturn"

Better would be to wrap this so that the constants are not passed around the whole time:

adjective_pokemon <- function(n = 1, style = "snake") {
  pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name)
  adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs)
  ids::ids(n, adjectives, pokemon, style = style)
}

adjective_pokemon(10, "kebab")
##  [1] "ancestral-gorebyss"    "hopeless-shroomish"   
##  [3] "suspended-delcatty"    "fractional-pineco"    
##  [5] "cardinal-rayquaza"     "goalless-eelektross"  
##  [7] "peevish-onix"          "gypsy-taillow"        
##  [9] "progressive-kabuto"    "progressive-shroomish"

As a second example we can use the word lists in rcorpora to generate identifiers in the form <mood>_<scientist>, like “melancholic_darwin”. These are similar to the names of docker containers.

First the lists of names themselves:

moods <- tolower(rcorpora::corpora("humans/moods")$moods)
scientists <- tolower(rcorpora::corpora("humans/scientists")$scientists)

Moods include:

sample(moods, 10)
##  [1] "comfortable" "inquisitive" "vacant"      "satiric"     "unknown"    
##  [6] "insulted"    "noticed"     "involved"    "censored"    "distant"

The scientists names contain spaces which is not going to work for us because ids won’t correctly translate all internal spaces to the requested style.

sample(scientists, 10)
##  [1] "james dwight dana"                     
##  [2] "georges-louis leclerc, comte de buffon"
##  [3] "ukichiro nakaya"                       
##  [4] "jane marcet"                           
##  [5] "francis bacon"                         
##  [6] "eratosthenes"                          
##  [7] "pierre de fermat"                      
##  [8] "mary anning"                           
##  [9] "brahmagupta"                           
## [10] "alfred binet"

To hack around this we’ll just take the last name from the list and remove all hyphens:

scientists <- vapply(strsplit(sub("(-|jr\\.$)", "", scientists), " "),
                     tail, character(1), 1)

Which gives strings that are just letters (though there are a few non-ASCII characters here that may cause problems because string handling is just a big pile of awful)

sample(scientists, 10)
##  [1] "dirac"      "foucault"   "bosch"      "binet"      "archimedes"
##  [6] "cori"       "raman"      "mayr"       "lyell"      "hubble"

With the word lists, create an identifier:

ids::ids(1, moods, scientists)
## [1] "deceived_pavlov"

Or pass NULL for n and create a function:

sci_id <- ids::ids(NULL, moods, scientists, style = "kebab")

which takes just the number of identifiers to generate as an argument

sci_id(10)
##  [1] "self-assured-bohm"      "aloof-fossey"          
##  [3] "listless-skinner"       "thankful-hopkins"      
##  [5] "gullible-hopkins"       "ashamed-watson"        
##  [7] "complacent-curie"       "fine-leeuwenhoek"      
##  [9] "content-sommerfeld"     "comfortable-brongniart"
ids/inst/doc/ids.Rmd0000644000176200001440000002343713113306437014010 0ustar liggesusers--- title: "ids" author: "Rich FitzJohn" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{ids} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ``` {r echo = FALSE, results = "hide"} knitr::opts_chunk$set(error = FALSE) human_no <- function(x) { s <- log10(floor(x + 1)) p <- c(0, thousand = 3, million = 6, billion = 9, trillion = 12) i <- s > p j <- max(which(i)) str <- names(p)[j] if (nzchar(str)) { paste(signif(x / 10^p[[j]], 3), str) } else { as.character(x) } } set.seed(1) ``` The `ids` package provides randomly generated ids in a number of different forms with different readability and sizes. ## Random bytes The `random_id` function generates random identifiers by generating `bytes` random bytes and converting to hexadecimal (so each byte becomes a pair of characters). Rather than use R's random number stream we use the `openssl` package here. ``` {r } ids::random_id() ``` All `ids` functions take `n` as the first argument to be the number of identifiers generated: ``` {r } ids::random_id(5) ``` The default here is 16 bytes, each of which has 256 values (so 256^16 = 2^128 = 3.4e38 combinations). You can make these larger or smaller with the `bytes` argument: ``` {r } ids::random_id(5, 8) ``` If `NULL` is provided as `n`, then a generating function is returned (all ids functions do this): ``` {r } f <- ids::random_id(NULL, 8) f ``` This function sets all arguments except for `n` ``` {r } f() f(4) ``` ## UUIDs The above look a lot like UUIDs but they are not actually UUIDs. The `uuid` package provides real UUIDs generated with libuuid, and the `ids::uuid` function provides an interface to that: ``` {r } ids::uuid() ``` As above, generate more than one UUID: ``` {r } ids::uuid(4) ``` Generate time-based UUIDs: ``` {r } ids::uuid(4, use_time = TRUE) ``` and optionally drop the hyphens: ``` {r } ids::uuid(5, drop_hyphens = TRUE) ``` ## Adjective animal Generate (somewhat) human readable identifiers by combining one or more adjectives with an animal name. ``` {r } ids::adjective_animal() ``` The list of adjectives and animals comes from [gfycat.com](http://gfycat.com), via https://github.com/a-type/adjective-adjective-animal Generate more than one identifier: ``` {r } ids::adjective_animal(4) ``` Use more than one adjective for very long idenfiers ``` {r } ids::adjective_animal(4, 3) ``` ``` {r echo = FALSE, results = "hide"} n1 <- length(ids:::gfycat_animals) n2 <- length(ids:::gfycat_adjectives) ``` There are `r n1` animal names and `r n2` adjectives so each one you add increases the idenfier space by a factor of `r n2`. So for 1, 2, and 3 adjectives there are about `r human_no(n1 * n2)`, `r human_no(n1 * n2^2)` and `r human_no(n1 * n2^3)` possible combinations. This is a much smaller space than the random identifiers above, but these are more readable and memorable. Note that here, the random nunbers are coming from R's random number stream so are affected by `set.seed()`. Because some of the animal and adjective names are very long (e.g. a _quasiextraterritorial hexakosioihexekontahexaphobic queenalexandrasbirdwingbutterfly_), in order to generate more readable/memorable identifiers it may be useful to restrict the length. Pass `max_len` in to do this. ``` {r } ids::adjective_animal(4, max_len = 6) ``` A vector of length 2 here can be used to apply to the adjectives and animal respectively: ``` {r } ids::adjective_animal(20, max_len = c(5, Inf)) ``` Note that this decreases the pool size and so increases the chance of collisions. In addition to snake_case, the default, the punctuation between words can be changed to: kebab-case: ``` {r } ids::adjective_animal(1, 2, style = "kebab") ``` dot.case: ``` {r } ids::adjective_animal(1, 2, style = "dot") ``` camel-case: ``` {r } ids::adjective_animal(1, 2, style = "camel") ``` PascalCase: ``` {r } ids::adjective_animal(1, 2, style = "pascal") ``` CONSTANT_CASE (aka SHOUTY_CASE) ``` {r } ids::adjective_animal(1, 2, style = "constant") ``` or with spaces, lower case: ``` {r } ids::adjective_animal(1, 2, style = "lower") ``` UPPER CASE ``` {r } ids::adjective_animal(1, 2, style = "upper") ``` Sentence case ``` {r } ids::adjective_animal(1, 2, style = "sentence") ``` Title Case ``` {r } ids::adjective_animal(1, 2, style = "title") ``` Again, pass `n = NULL` here to create a generating function: ``` {r } aa3 <- ids::adjective_animal(NULL, 3, style = "kebab", max_len = c(6, 8)) ``` ...which can be used to generate ids on demand. ``` {r } aa3() aa3(4) ``` ## Random sentences The `sentence` function creates a sentence style identifier. This uses the approach described by Asana on [their blog](https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly). This approach encodes 32 bits of information (so 2^32 ~= 4 billion possibilities) and in theory can be remapped to an integer if you really wanted to. ``` {r } ids::sentence() ``` As with `adjective_animal`, the case can be changed: ``` {r } ids::sentence(2, "dot") ``` If you would rather past tense for the verbs, then pass `past = TRUE`: ``` {r } ids::sentence(4, past = TRUE) ``` ## proquints "proquints" are an identifier that tries to be information dense but still human readable and (somewhat) pronounceable; "proquint" stands for *PRO*-nouncable *QUINT*-uplets. They are introduced in https://arxiv.org/html/0901.4016 `ids` can generate proquints: ``` {r } ids::proquint(10) ``` By default it generates two-word proquints but that can be changed: ``` {r } ids::proquint(5, 1) ids::proquint(2, 4) ``` Proquints are formed by alternating consonant/vowel/consonant/vowel/consonant using a subset of both (16 consonants and 4 vowels). This yields 2^16 (65,536) possibilities per word. Words are always lower case and always separated by a hyphen. So with 4 words there are 2^64 combinations in 23 characters. Proquints are also useful in that they can be tranlated with integers. The proquint `kapop` has integer value 25258 ``` {r } ids::proquint_to_int("kapop") ids::int_to_proquint(25258) ``` This makes proquints suitable for creating human-pronouncable identifers out of things like ip addresses, integer primary keys, etc. The function `ids::int_to_proquint_word` will translate between proquint words and integers (and are vectorised) ``` {r } w <- ids::int_to_proquint_word(sample(2^16, 10) - 1L) w ``` and `ids::proquint_word_to_int` does the reverse ``` {r } ids::proquint_word_to_int(w) ``` whille `ids::proquint_to_int` and `ids::int_to_proquint` allows translation of multi-word proquints. Overflow is a real possibility; the maximum integer representable is only about `r human_no(.Machine$integer.max)` and the maximum floating point number of accuracy of 1 is about `r human_no(2 / .Machine$double.eps)` -- these are big numbers but fairly small proquints: ``` {r } ids::int_to_proquint(.Machine$integer.max - 1) ids::int_to_proquint(2 / .Machine$double.eps) ``` But if you had a 6 word proquint this would not work! ``` {r } p <- ids::proquint(1, 6) ``` Too big for an integer: ``` {r error = TRUE} ids::proquint_to_int(p) ``` And too big for an numeric number: ``` {r error = TRUE} ids::proquint_to_int(p, as = "numeric") ``` To allow this, we use `openssl`'s `bignum` support: ``` {r } ids::proquint_to_int(p, as = "bignum") ``` This returns a *list* with one bignum (this is required to allow vectorisation). ## Roll your own identifiers The `ids` functions can build identifiers in the style of `adjective_animal` or `sentence`. It takes as input a list of strings. This works particularly well with the `rcorpora` package which includes lists of strings. Here is a list of Pokemon names: ``` {r } pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) length(pokemon) ``` ...and here is a list of adjectives ``` {r } adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) length(adjectives) ``` So we have a total pool size of about `r human_no(length(adjectives) * length(pokemon))`, which is not huge, but it is at least topical. To generate one identifier: ``` {r } ids::ids(1, adjectives, pokemon) ``` All the style-changing code is available: ``` {r } ids::ids(10, adjectives, pokemon, style = "dot") ``` Better would be to wrap this so that the constants are not passed around the whole time: ``` {r } adjective_pokemon <- function(n = 1, style = "snake") { pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) ids::ids(n, adjectives, pokemon, style = style) } adjective_pokemon(10, "kebab") ``` As a second example we can use the word lists in rcorpora to generate identifiers in the form `_`, like "melancholic_darwin". These are similar to the names of docker containers. First the lists of names themselves: ``` {r } moods <- tolower(rcorpora::corpora("humans/moods")$moods) scientists <- tolower(rcorpora::corpora("humans/scientists")$scientists) ``` Moods include: ``` {r } sample(moods, 10) ``` The scientists names contain spaces which is not going to work for us because `ids` won't correctly translate all internal spaces to the requested style. ``` {r } sample(scientists, 10) ``` To hack around this we'll just take the last name from the list and remove all hyphens: ``` {r } scientists <- vapply(strsplit(sub("(-|jr\\.$)", "", scientists), " "), tail, character(1), 1) ``` Which gives strings that are just letters (though there are a few non-ASCII characters here that may cause problems because string handling is just a big pile of awful) ``` {r } sample(scientists, 10) ``` With the word lists, create an identifier: ``` {r } ids::ids(1, moods, scientists) ``` Or pass `NULL` for `n` and create a function: ``` {r } sci_id <- ids::ids(NULL, moods, scientists, style = "kebab") ``` which takes just the number of identifiers to generate as an argument ``` {r } sci_id(10) ``` ids/inst/doc/ids.R0000644000176200001440000001512713113306437013464 0ustar liggesusers## ----echo = FALSE, results = "hide"-------------------------------------- knitr::opts_chunk$set(error = FALSE) human_no <- function(x) { s <- log10(floor(x + 1)) p <- c(0, thousand = 3, million = 6, billion = 9, trillion = 12) i <- s > p j <- max(which(i)) str <- names(p)[j] if (nzchar(str)) { paste(signif(x / 10^p[[j]], 3), str) } else { as.character(x) } } set.seed(1) ## ------------------------------------------------------------------------ ids::random_id() ## ------------------------------------------------------------------------ ids::random_id(5) ## ------------------------------------------------------------------------ ids::random_id(5, 8) ## ------------------------------------------------------------------------ f <- ids::random_id(NULL, 8) f ## ------------------------------------------------------------------------ f() f(4) ## ------------------------------------------------------------------------ ids::uuid() ## ------------------------------------------------------------------------ ids::uuid(4) ## ------------------------------------------------------------------------ ids::uuid(4, use_time = TRUE) ## ------------------------------------------------------------------------ ids::uuid(5, drop_hyphens = TRUE) ## ------------------------------------------------------------------------ ids::adjective_animal() ## ------------------------------------------------------------------------ ids::adjective_animal(4) ## ------------------------------------------------------------------------ ids::adjective_animal(4, 3) ## ----echo = FALSE, results = "hide"-------------------------------------- n1 <- length(ids:::gfycat_animals) n2 <- length(ids:::gfycat_adjectives) ## ------------------------------------------------------------------------ ids::adjective_animal(4, max_len = 6) ## ------------------------------------------------------------------------ ids::adjective_animal(20, max_len = c(5, Inf)) ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "kebab") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "dot") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "camel") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "pascal") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "constant") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "lower") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "upper") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "sentence") ## ------------------------------------------------------------------------ ids::adjective_animal(1, 2, style = "title") ## ------------------------------------------------------------------------ aa3 <- ids::adjective_animal(NULL, 3, style = "kebab", max_len = c(6, 8)) ## ------------------------------------------------------------------------ aa3() aa3(4) ## ------------------------------------------------------------------------ ids::sentence() ## ------------------------------------------------------------------------ ids::sentence(2, "dot") ## ------------------------------------------------------------------------ ids::sentence(4, past = TRUE) ## ------------------------------------------------------------------------ ids::proquint(10) ## ------------------------------------------------------------------------ ids::proquint(5, 1) ids::proquint(2, 4) ## ------------------------------------------------------------------------ ids::proquint_to_int("kapop") ids::int_to_proquint(25258) ## ------------------------------------------------------------------------ w <- ids::int_to_proquint_word(sample(2^16, 10) - 1L) w ## ------------------------------------------------------------------------ ids::proquint_word_to_int(w) ## ------------------------------------------------------------------------ ids::int_to_proquint(.Machine$integer.max - 1) ids::int_to_proquint(2 / .Machine$double.eps) ## ------------------------------------------------------------------------ p <- ids::proquint(1, 6) ## ----error = TRUE-------------------------------------------------------- ids::proquint_to_int(p) ## ----error = TRUE-------------------------------------------------------- ids::proquint_to_int(p, as = "numeric") ## ------------------------------------------------------------------------ ids::proquint_to_int(p, as = "bignum") ## ------------------------------------------------------------------------ pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) length(pokemon) ## ------------------------------------------------------------------------ adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) length(adjectives) ## ------------------------------------------------------------------------ ids::ids(1, adjectives, pokemon) ## ------------------------------------------------------------------------ ids::ids(10, adjectives, pokemon, style = "dot") ## ------------------------------------------------------------------------ adjective_pokemon <- function(n = 1, style = "snake") { pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) ids::ids(n, adjectives, pokemon, style = style) } adjective_pokemon(10, "kebab") ## ------------------------------------------------------------------------ moods <- tolower(rcorpora::corpora("humans/moods")$moods) scientists <- tolower(rcorpora::corpora("humans/scientists")$scientists) ## ------------------------------------------------------------------------ sample(moods, 10) ## ------------------------------------------------------------------------ sample(scientists, 10) ## ------------------------------------------------------------------------ scientists <- vapply(strsplit(sub("(-|jr\\.$)", "", scientists), " "), tail, character(1), 1) ## ------------------------------------------------------------------------ sample(scientists, 10) ## ------------------------------------------------------------------------ ids::ids(1, moods, scientists) ## ------------------------------------------------------------------------ sci_id <- ids::ids(NULL, moods, scientists, style = "kebab") ## ------------------------------------------------------------------------ sci_id(10) ids/tests/0000755000176200001440000000000012612173545012201 5ustar liggesusersids/tests/testthat.R0000644000176200001440000000006212612173545014162 0ustar liggesuserslibrary(testthat) library(ids) test_check("ids") ids/tests/testthat/0000755000176200001440000000000013113501667014036 5ustar liggesusersids/tests/testthat/test-sentence.R0000644000176200001440000000162713007061140016735 0ustar liggesuserscontext("sentence") test_that("basic", { re <- "^[0-9]+(_[a-z]+){4}$" res <- sentence() expect_is(res, "character") expect_equal(length(res), 1) expect_match(res, re) }) test_that("tense", { verb <- vapply(strsplit(sentence(100), "_", fixed = TRUE), function(x) x[[4L]], character(1)) expect_true(all(verb %in% asana_verbs_present)) verb <- vapply(strsplit(sentence(100, past = TRUE), "_", fixed = TRUE), function(x) x[[4L]], character(1)) expect_true(all(verb %in% asana_verbs_past)) }) test_that("functional interface", { f <- sentence(NULL, style = "kebab", past = TRUE) expect_is(f, "function") re <- "^[0-9]+(-[a-z]+){4}$" expect_match(f(), re) res <- f(100) expect_true(all(grepl(re, res))) verb <- vapply(strsplit(res, "-", fixed = TRUE), function(x) x[[4L]], character(1)) expect_true(all(verb %in% asana_verbs_past)) }) ids/tests/testthat/test-adjective-animal.R0000644000176200001440000000437113107317605020340 0ustar liggesuserscontext("adjective animal") test_that("data", { expect_false(any(grepl("\\s", gfycat_animals))) expect_false(any(grepl("\\s", gfycat_adjectives))) }) test_that("basic", { res <- adjective_animal() expect_is(res, "character") expect_equal(length(res), 1) expect_match(res, "^[a-z]+_[a-z]+$") }) test_that("length", { expect_equal(length(adjective_animal(5)), 5) expect_equal(adjective_animal(0), character(0)) }) test_that("style", { expect_match(adjective_animal(style = "Pascal"), "^[A-Z][a-z]+[A-Z][a-z]+$") expect_match(adjective_animal(style = "camel"), "^[a-z]+[A-Z][a-z]+$") expect_match(adjective_animal(style = "snake"), "^[a-z]+_[a-z]+$") expect_match(adjective_animal(style = "kebab"), "^[a-z]+-[a-z]+$") expect_match(adjective_animal(style = "lower"), "^[a-z]+ [a-z]+$") expect_match(adjective_animal(style = "upper"), "^[A-Z]+ [A-Z]+$") expect_match(adjective_animal(style = "constant"), "^[A-Z]+_[A-Z]+$") expect_match(adjective_animal(style = "title"), "^[A-Z][a-z]+ [A-Z][a-z]+$") expect_match(adjective_animal(style = "sentence"), "^[A-Z][a-z]+ [a-z]+$") }) test_that("restrict length", { tmp <- strsplit(adjective_animal(200, max_len = 5), "_", fixed = TRUE) expect_lte(max(sapply(tmp, nchar)), 5) tmp <- strsplit(adjective_animal(200, max_len = c(5, 10)), "_", fixed = TRUE) tmp <- apply(sapply(tmp, nchar), 1, max) expect_lte(tmp[[1]], 5) expect_lte(tmp[[2]], 10) expect_gt(tmp[[2]], tmp[[1]]) }) test_that("restrict length error cases", { expect_error(adjective_animal(max_len = 2), "max_len must be at least 3") expect_error(adjective_animal(max_len = -2), "max_len must be at least 3") ## This is ignored -- perhaps it should not be? expect_silent(adjective_animal(max_len = numeric(0))) expect_error(adjective_animal(max_len = c(5, 6, 7)), "max_len must be length 1 or 2") expect_error(adjective_animal(n_adjectives = 2, max_len = c(5, 6, 7)), "max_len must be length 1 or 2") }) test_that("functional interface", { f <- adjective_animal(NULL, max_len = 10, style = "kebab", n_adjectives = 2) expect_is(f, "function") re <- "^[a-z]{2,10}-[a-z]{2,10}-[a-z]{2,10}$" expect_match(f(), re) x <- f(100) expect_true(all(grepl(re, x))) }) ids/tests/testthat/test-ids.R0000644000176200001440000000124113007061140015700 0ustar liggesuserscontext("ids") test_that("animals", { re_snake2 <- "^([a-z]+)_([a-z]+)$" re_camel2 <- "^([a-z]+)([A-Z][a-z]+)$" re_snake3 <- "^([a-z]+)_([a-z]+)_([a-z]+)$" res <- adjective_animal() expect_match(res, re_snake2) expect_match(adjective_animal(1, 2), re_snake3) expect_match(adjective_animal(style = "camel"), re_camel2) res2 <- adjective_animal(2, style = "camel") expect_match(res2, re_camel2) expect_true(all(sub(re_camel2, "\\1", res2) %in% gfycat_adjectives)) expect_true(all(tolower(sub(re_camel2, "\\2", res2)) %in% gfycat_animals)) ## Smoke test: for (s in names(cases())) { expect_is(adjective_animal(style = s), "character") } }) ids/tests/testthat/test-proquint.R0000644000176200001440000002614413113306233017016 0ustar liggesuserscontext("proquint") test_that("word conversions", { i <- 10^(0:4) ## From Python: cmp <- c("babad", "babap", "badoh", "bazom", "fisib") ## Ours w <- int_to_proquint_word(i) expect_identical(w, cmp) j <- proquint_word_to_int(w, TRUE) expect_equal(j, i) expect_is(j, "integer") k <- proquint_word_to_int(w, FALSE) expect_identical(j, k) expect_is(k, "integer") }) ## This is exhaustive, because it's not that slow to do everything: test_that("word conversions - exhaustive", { i <- seq_len(PROQUINT_WORD) - 1L w <- int_to_proquint_word(i) expect_equal(w, cache$proquint_words) expect_equal(int_to_proquint_word(i, FALSE), cache$proquint_words) ## word -> index expect_equal(proquint_word_to_int(w), i) expect_equal(proquint_word_to_int(w, FALSE), i) }) test_that("proquint conversions", { ## Then start on the multi-word cases known <- c("babad" = 1, "babap" = 10, "badoh" = 100, "bazom" = 1000, "fisib" = 10000, "babad-mipob" = 100000, "babaz-hanab" = 1000000, "bafim-nipab" = 10000000, "biluj-vahab" = 100000000, "govip-somab" = 1000000000) expect_equal(proquint_to_int(names(known), use_cache = TRUE), unname(known)) expect_equal(proquint_to_int(names(known), use_cache = FALSE), unname(known)) ## Then the reverse: expect_equal(int_to_proquint(unname(known), TRUE), names(known)) expect_equal(int_to_proquint(unname(known), FALSE), names(known)) }) ## Around the transition: test_that("1-2 word transition corner cases", { i <- (PROQUINT_WORD - 2):(PROQUINT_WORD + 2) expected <- c("zuzuv", "zuzuz", "babad-babab", "babad-babad", "babad-babaf") expect_equal(int_to_proquint(i), expected) expect_equal(int_to_proquint(i, FALSE), expected) expect_equal(proquint_to_int(expected, use_cache = TRUE), i) expect_equal(proquint_to_int(expected, use_cache = FALSE), i) }) test_that("sampled words do not depend on cache", { set.seed(1) w1 <- proquint_sample_words(10, TRUE) set.seed(1) w2 <- proquint_sample_words(10, FALSE) expect_identical(w1, w2) }) test_that("generate openssl random numbers", { i <- rand_i16(1, TRUE) expect_is(i, "integer") expect_gte(i, 0L) expect_lt(i, 2^16) j <- rand_i16(10, TRUE) expect_is(j, "integer") expect_equal(length(j), 10) expect_true(all(j >= 0L)) expect_true(all(j < 2^16)) set.seed(1) i <- rand_i16(100, TRUE) set.seed(1) j <- rand_i16(100, TRUE) expect_false(identical(i, j)) i <- rand_i16(2^16 * 16, TRUE) n <- tabulate(i + 1, 2^16) expect_true(all(i >= 0L)) expect_true(all(i < 2^16)) }) test_that("openssl random identifiers", { set.seed(1) w1 <- proquint(100, use_openssl = TRUE) set.seed(1) w2 <- proquint(100, use_openssl = TRUE) expect_false(identical(w1, w2)) }) test_that("bad type on conversion", { expect_error(int_to_proquint("one"), "Invalid type for 'x'") expect_error(int_to_proquint(TRUE), "Invalid type for 'x'") expect_error(int_to_proquint(1+4i), "Invalid type for 'x'") }) test_that("word -> int translation: missing values", { i <- 12345L w <- int_to_proquint_word(i) expect_identical(proquint_word_to_int(NA), NA_integer_) expect_identical(proquint_word_to_int(NA_character_), NA_integer_) expect_identical(proquint_word_to_int(c(NA_character_, w)), c(NA_integer_, i)) expect_identical(proquint_word_to_int(c(NA, w, w, NA)), c(NA_integer_, i, i, NA_integer_)) }) test_that("int -> word translation: missing values", { i <- 12345L w <- int_to_proquint_word(i) expect_identical(int_to_proquint_word(NA), NA_character_) expect_identical(int_to_proquint_word(NA_integer_), NA_character_) expect_identical(int_to_proquint_word(c(NA_integer_, i)), c(NA_character_, w)) expect_identical(int_to_proquint_word(c(NA, i, i, NA)), c(NA_character_, w, w, NA_character_)) }) test_that("identifier -> int translation: missing values", { i <- 12345L w <- int_to_proquint_word(i) ib <- openssl::bignum(i) expect_identical(proquint_to_int(NA, "integer"), NA_integer_) expect_identical(proquint_to_int(NA, "numeric"), NA_real_) expect_identical(proquint_to_int(NA, "bignum"), list(NULL)) expect_identical(proquint_to_int(NA_character_, "integer"), NA_integer_) expect_identical(proquint_to_int(NA_character_, "numeric"), NA_real_) expect_identical(proquint_to_int(NA_character_, "bignum"), list(NULL)) expect_identical(proquint_to_int(c(NA, w), "integer"), c(NA_integer_, i)) expect_identical(proquint_to_int(c(NA, w), "numeric"), c(NA_real_, i)) expect_identical(proquint_to_int(c(NA, w), "bignum"), list(NULL, ib)) expect_identical(proquint_to_int(c(NA, w, w, NA), "integer"), c(NA_integer_, i, i, NA_integer_)) expect_identical(proquint_to_int(c(NA, w, w, NA), "numeric"), c(NA_real_, i, i, NA_real_)) expect_identical(proquint_to_int(c(NA, w, w, NA), "bignum"), list(NULL, ib, ib, NULL)) }) test_that("int -> identifier translation: missing values", { i <- 12345L w <- int_to_proquint_word(i) ib <- openssl::bignum(i) expect_identical(int_to_proquint(NA), NA_character_) expect_identical(int_to_proquint(NA_integer_), NA_character_) expect_identical(int_to_proquint(NA_real_), NA_character_) expect_identical(int_to_proquint(list(NULL)), NA_character_) expect_identical(int_to_proquint(c(NA_integer_, i)), c(NA, w)) expect_identical(int_to_proquint(c(NA_real_, i)), c(NA, w)) expect_identical(int_to_proquint(list(NULL, ib)), c(NA, w)) expect_identical(int_to_proquint(c(NA_integer_, i, i, NA_integer_)), c(NA_character_, w, w, NA_character_)) expect_identical(int_to_proquint(c(NA_real_, i, i, NA_real_)), c(NA_character_, w, w, NA_character_)) expect_identical(int_to_proquint(list(NULL, ib, ib, NULL)), c(NA_character_, w, w, NA_character_)) }) test_that("identifier: 0", { expect_identical(proquint(0), character(0)) }) test_that("identifier: 1", { x <- proquint(1) expect_is(x, "character") expect_equal(length(x), 1) re <- sprintf("^%s-%s$", PROQUINT_RE_WORD, PROQUINT_RE_WORD) expect_match(x, re) }) test_that("identifier: n", { n <- 10 x <- proquint(n) expect_is(x, "character") expect_equal(length(x), n) re <- sprintf("^%s-%s$", PROQUINT_RE_WORD, PROQUINT_RE_WORD) expect_match(x, re, all = TRUE) }) test_that("vary word length", { re3 <- sprintf("^%s-%s-%s$", PROQUINT_RE_WORD, PROQUINT_RE_WORD, PROQUINT_RE_WORD) expect_match(proquint(1, 3), re3) expect_match(proquint(1, 1), PROQUINT_RE1) }) test_that("functional interface", { re3 <- sprintf("^%s-%s-%s$", PROQUINT_RE_WORD, PROQUINT_RE_WORD, PROQUINT_RE_WORD) f <- proquint(NULL, 3) expect_is(f, "function") expect_match(f(), re3) expect_equal(length(f()), 1) expect_equal(length(f(10)), 10) }) ## -- below here is all boring error handling stuff ## Validate words test_that("invalid word -> int", { expect_error(proquint_word_to_int("hello!"), "Invalid proquint word: 'hello!'") expect_error(proquint_word_to_int(c("hello!", "babad"),), "Invalid proquint word: 'hello!'") expect_error(proquint_word_to_int(c("hello!", "babad", "yo!"),), "Invalid proquint word: 'hello!', 'yo!'") expect_error(proquint_word_to_int(1), "Invalid proquint word: '1'") }) ## Validate word indexes test_that("invalid word -> int", { expect_error(int_to_proquint_word(-1), "Invalid proquint word index (out of range): -1", fixed = TRUE) expect_error(int_to_proquint_word(c(-1, 1)), "Invalid proquint word index (out of range): -1", fixed = TRUE) expect_error(int_to_proquint_word(c(-1, 1, 65536)), "Invalid proquint word index (out of range): -1, 65536", fixed = TRUE) expect_error(int_to_proquint_word("babad"), "Invalid proquint word index (not numeric)", fixed = TRUE) }) test_that("zero length input", { expect_equal(proquint_word_to_int(NULL), integer(0)) expect_equal(proquint_word_to_int(character(0)), integer(0)) ## This is not terrific but it is what it is: expect_equal(proquint_word_to_int(integer(0)), integer(0)) expect_equal(int_to_proquint_word(NULL), character(0)) expect_equal(int_to_proquint_word(integer(0)), character(0)) }) test_that("zero length input", { expect_identical(proquint_to_int(NULL, "integer"), integer(0)) expect_identical(proquint_to_int(NULL, "numeric"), numeric(0)) expect_identical(proquint_to_int(NULL, "bignum"), list()) expect_identical(proquint_to_int(character(0), "integer"), integer(0)) expect_identical(proquint_to_int(character(0), "numeric"), numeric(0)) expect_identical(proquint_to_int(character(0), "bignum"), list()) }) test_that("zero length input", { expect_identical(int_to_proquint(NULL), character(0)) expect_identical(int_to_proquint(logical(0)), character(0)) expect_identical(int_to_proquint(integer(0)), character(0)) expect_identical(int_to_proquint(numeric(0)), character(0)) expect_identical(int_to_proquint(list()), character(0)) }) test_that("invalid identifier", { expect_error(proquint_to_int(TRUE), "Expected a character vector for 'p'") expect_error(proquint_to_int("aaaaa"), "Invalid identifier: 'aaaaa'") }) test_that("proquint to int, varying formats", { i <- c(1, 10, 100, 1000, 10000) w <- int_to_proquint_word(i) expect_identical(proquint_to_int(w, "integer"), as.integer(i)) expect_identical(proquint_to_int(w, "numeric"), as.numeric(i)) expect_identical(proquint_to_int(w, "bignum"), lapply(i, openssl::bignum)) }) test_that("int to proquint, varying formats", { i <- c(1, 10, 100, 1000, 10000) w <- int_to_proquint_word(i) expect_identical(int_to_proquint(as.integer(i)), w) expect_identical(int_to_proquint(as.numeric(i)), w) expect_identical(int_to_proquint(lapply(i, openssl::bignum)), w) }) test_that("integer overflow", { big <- .Machine$integer.max * 2 pq <- int_to_proquint(big) expect_is(pq, "character") expect_error(proquint_to_int(pq, "integer"), "Integer overflow: cannot represent proquint as integer") expect_identical(proquint_to_int(pq, "numeric"), big) expect_identical(proquint_to_int(pq, "bignum"), list(openssl::bignum(big))) }) test_that("numeric overflow", { pow <- log2(2/.Machine$double.eps) + 1 big <- openssl::bignum(2)^pow + 1 pq <- int_to_proquint(big) expect_is(pq, "character") expect_error(proquint_to_int(pq, "integer"), "Integer overflow: cannot represent proquint as integer") expect_error(proquint_to_int(pq, "numeric"), "Numeric overflow: cannot represent proquint as numeric") expect_identical(proquint_to_int(pq, "bignum"), list(openssl::bignum(big))) }) ## This supports my temporary as_integer_bignum function: test_that("as_integer_bignum", { x <- c(0L, 255L, 256L, 1000L, 200000L, .Machine$integer.max) for (i in x) { expect_identical(as_integer_bignum(openssl::bignum(i)), i) } }) ids/tests/testthat/test-random.R0000644000176200001440000000171613007061140016410 0ustar liggesuserscontext("random") re_random <- function(len) { sprintf("^[[:xdigit:]]{%s}", len) } test_that("uuid", { expect_equal(random_id(0), character(0)) x1 <- random_id(1) expect_equal(length(x1), 1) expect_is(x1, "character") expect_match(x1, re_random(32)) x10 <- random_id(10) expect_equal(length(x10), 10) expect_is(x10, "character") expect_true(all(grepl(re_random(32), x10))) }) test_that("bind args", { f <- random_id(NULL, bytes = 5) expect_is(f, "function") expect_equal(as.list(formals(f)), list(n = 1)) expect_match(f(), re_random(10)) expect_equal(nchar(f()), 10) }) test_that("r byte stream", { set.seed(1) id1 <- random_id() set.seed(1) expect_false(id1 == random_id()) set.seed(1) expect_false(id1 == random_id(NULL)()) set.seed(1) id1 <- random_id(use_openssl = FALSE) set.seed(1) expect_true(id1 == random_id(use_openssl = FALSE)) set.seed(1) expect_true(id1 == random_id(NULL, use_openssl = FALSE)()) }) ids/tests/testthat/test-case.R0000644000176200001440000000253413007061140016042 0ustar liggesuserscontext("case") test_that("style match", { expect_equal(check_style("p")$name, "pascal") expect_equal(check_style("pascal")$name, "pascal") expect_equal(check_style("pascalcase")$name, "pascal") expect_equal(check_style("PascalCase")$name, "pascal") expect_equal(check_style("Pascal-Case")$name, "pascal") expect_equal(check_style("Pascal_Case")$name, "pascal") }) test_that("camel", { expect_equal(toupper_camel(character(0)), character(0)) expect_equal(toupper_camel("aaa"), "aaa") expect_equal(toupper_camel(c("aaa", "bbb")), c("aaa", "Bbb")) expect_equal(toupper_camel(rbind(c("aaa", "bbb"), c("ccc", "ddd"))), rbind(c("aaa", "Bbb"), c("ccc", "Ddd"))) }) test_that("sentence", { tr <- make_combine("sentence") expect_equal(tr(c("foo", "bar")), "Foo bar") expect_equal(tr(rbind(c("foo", "bar"), c("another", "sentence"))), c("Foo bar", "Another sentence")) }) test_that("error cases", { expect_error(check_style(NULL), "must be a character vector") expect_error(check_style(1), "must be a character vector") expect_error(check_style(character(0)), "must be a scalar") expect_error(check_style(c("camel", "upper")), "must be a scalar") expect_error(check_style("unknown"), "Invalid style 'unknown'") }) ids/tests/testthat/test-uuid.R0000644000176200001440000000132013007061140016065 0ustar liggesuserscontext("uuid") RE_UUID <- "^[[:xdigit:]]{8}-([[:xdigit:]]{4}-){3}[[:xdigit:]]{12}$" test_that("uuid", { expect_equal(uuid(0), character(0)) x1 <- uuid(1) expect_equal(length(x1), 1) expect_is(x1, "character") expect_match(x1, RE_UUID) x10 <- uuid(10) expect_equal(length(x10), 10) expect_is(x10, "character") expect_true(all(grepl(RE_UUID, x10))) expect_true(all(grepl("^[[:xdigit:]]{32}$", uuid(10, drop_hyphens = TRUE)))) expect_match(uuid(use_time = TRUE), RE_UUID) }) test_that("bind args", { f <- uuid(NULL) expect_is(f, "function") expect_equal(as.list(formals(f)), list(n = 1)) expect_match(f(), RE_UUID) g <- uuid(NULL, TRUE) expect_match(g(), "^[[:xdigit:]]{32}$") }) ids/NAMESPACE0000644000176200001440000000043413113306233012244 0ustar liggesusers# Generated by roxygen2: do not edit by hand export(adjective_animal) export(ids) export(int_to_proquint) export(int_to_proquint_word) export(proquint) export(proquint_to_int) export(proquint_word_to_int) export(random_id) export(sentence) export(uuid) importFrom(uuid,UUIDgenerate) ids/NEWS.md0000644000176200001440000000026013113306233012120 0ustar liggesusers# ids 1.1.0 (2017-05-22) * Fix occasionally failing text (removes one animal from the pool) * New identifier type "proquint" # ids 1.0.0 (2016-11-03) * Initial CRAN release ids/R/0000755000176200001440000000000013113306233011225 5ustar liggesusersids/R/case.R0000644000176200001440000000337213007061140012265 0ustar liggesusers# PascalCase # camelCase # snake_case # kebab-case # Title Case # lower case # upper case # CONSTANT_CASE # Sentence case check_style <- function(style) { if (!is.character(style)) { stop("style must be a character vector") } if (length(style) != 1L) { stop("style must be a scalar") } pos <- cases() i <- pmatch(sub("[_-]?case$", "", tolower(style)), names(pos)) if (is.na(i)) { stop(sprintf("Invalid style '%s'", style)) } ret <- pos[[i]] ret$name <- names(pos)[[i]] ret } make_combine <- function(style) { dat <- check_style(style) join <- dat$join tr <- dat$tr function(x) { if (is.matrix(x)) { apply(tr(x), 1, paste, collapse = join) } else { paste(tr(x), collapse = join) } } } toupper_initial <- function(x) { substr(x, 1, 1) <- toupper(substr(x, 1, 1)) x } toupper_camel <- function(x) { if (is.matrix(x)) { x[, -1] <- toupper_initial(x[, -1]) } else { x[-1] <- toupper_initial(x[-1]) } x } toupper_sentence <- function(x) { if (is.matrix(x)) { x[, 1] <- toupper_initial(x[, 1]) } else { x[1] <- toupper_initial(x[1]) } x } toupper_pascal <- function(x) { x[] <- toupper_initial(x) x } cases <- function() { list(snake = list(join = "_", tr = identity), kebab = list(join = "-", tr = identity), dot = list(join = ".", tr = identity), camel = list(join = "", tr = toupper_camel), pascal = list(join = "", tr = toupper_pascal), lower = list(join = " ", tr = identity), # lower case already upper = list(join = " ", tr = toupper), title = list(join = " ", tr = toupper_pascal), sentence = list(join = " ", tr = toupper_sentence), constant = list(join = "_", tr = toupper)) } ids/R/random.R0000644000176200001440000000421113007061140012623 0ustar liggesusers#' Random identifiers. By default this uses the \code{openssl} #' package to produce a random set of bytes, and expresses that as a #' hex character string. This does not affect R's random number #' stream. #' #' @title Cryptographically generated random identifiers #' @inheritParams ids #' #' @param bytes The number of bytes to include for each identifier. #' The length of the returned identifiers will be twice this long #' with each pair of characters representing a single byte. #' #' @param use_openssl A logical, indicating if we should use the #' openssl for generating the random identifiers. The openssl #' random bytestream is not affected by the state of the R random #' number generator (e.g., via \code{\link{set.seed}}) so may not be #' suitable for use where reproducibility is important. The speed #' should be very similar for both approaches. #' #' @export #' @author Rich FitzJohn #' @examples #' # Generate a random id: #' random_id() #' #' # Generate 10 of them! #' random_id(10) #' #' # Different length ids #' random_id(bytes = 8) #' # (note that the number of characters is twice the number of bytes) #' #' # The ids are not affected by R's RNG state: #' set.seed(1) #' (id1 <- random_id()) #' set.seed(1) #' (id2 <- random_id()) #' # The generated identifiers are different, despite the seed being the same: #' id1 == id2 #' #' # If you need these identifiers to be reproducible, pass use_openssl = FALSE #' set.seed(1) #' (id1 <- random_id(use_openssl = FALSE)) #' set.seed(1) #' (id2 <- random_id(use_openssl = FALSE)) #' # This time they are the same: #' id1 == id2 #' #' # Pass \code{n = NULL} to generate a function that binds your arguments: #' id8 <- random_id(NULL, bytes = 8) #' id8(10) random_id <- function(n = 1, bytes = 16, use_openssl = TRUE) { if (is.null(n)) { force(bytes) force(use_openssl) function(n = 1) { random_id(n, bytes, use_openssl) } } else { rand_bytes <- if (use_openssl) openssl::rand_bytes else r_rand_bytes apply(matrix(as.character(rand_bytes(n * bytes)), n), 1, paste, collapse = "") } } r_rand_bytes <- function(n) { as.raw(sample(256L, n, TRUE) - 1L) } ids/R/adjective_animal.R0000644000176200001440000000457713007061140014641 0ustar liggesusers#' Ids based on a number of adjectives and an animal #' #' The list of adjectives and animals comes from #' \url{https://github.com/a-type/adjective-adjective-animal}, and in #' turn from \url{gfycat.com} #' #' @title Ids based on a number of adjectives and an animal #' @param n_adjectives Number of adjectives to prefix the anmial with #' #' @param max_len The maximum length of a word part to include (this #' may be useful because some of the names are rather long. This #' stops you generating a #' \code{hexakosioihexekontahexaphobic_queenalexandrasbirdwingbutterfly}). #' A vector of length 2 can be passed in here in which case the #' first element will apply to the adjectives (all of them) and the #' second element will apply to the animals. #' #' @inheritParams ids #' @export #' @author Rich FitzJohn #' @examples #' # Generate a random identifier: #' adjective_animal() #' #' # Generate a bunch all at once: #' adjective_animal(5) #' #' # Control the style of punctuation with the style argument: #' adjective_animal(style = "lower") #' adjective_animal(style = "CONSTANT") #' adjective_animal(style = "camel") #' adjective_animal(style = "kebab") #' #' # Control the number of adjectives used #' adjective_animal(n_adjectives = 3) #' #' # This can get out of hand quickly though: #' adjective_animal(n_adjectives = 7) #' #' # Limit the length of adjectives and animals used: #' adjective_animal(10, max_len = 6) #' #' # The lengths can be controlled for adjectives and animals #' # separately, with Inf meaning no limit: #' adjective_animal(10, max_len = c(6, Inf), n_adjectives = 2) #' #' # Pass n = NULL to bind arguments to a function #' id <- adjective_animal(NULL, n_adjectives = 2, style = "dot", max_len = 6) #' id() #' id(10) adjective_animal <- function(n = 1, n_adjectives = 1, style = "snake", max_len = Inf) { if (any(is.finite(max_len))) { if (any(max_len < 3)) { stop("max_len must be at least 3") } if (length(max_len) == 1L) { max_len <- rep_len(max_len, 2) } else if (length(max_len) != 2L) { stop("max_len must be length 1 or 2") } gfycat_adjectives <- gfycat_adjectives[nchar(gfycat_adjectives) <= max_len[1]] gfycat_animals <- gfycat_animals[nchar(gfycat_animals) <= max_len[2]] } vals <- c(rep(list(gfycat_adjectives), n_adjectives), list(gfycat_animals)) ids(n, vals = vals, style = style) } ids/R/sysdata.rda0000644000176200001440000014516413107317434013410 0ustar liggesusers}kJ5|7#nfe}=-22K5 WkIDuJf#G 4=j[jkl??c6Y]TY X_,N1hL/CV6u>0}QY-u45c}6]}(r}cu]vy?#ʺ:;m|xV9wfZg?SVRmO<b}kg,6/cQT]VaȻs9";5cWď/9>;-x/f5gvcVge @|s0{Zv3~,uX=ӹk.y!\H;;kϬFZ7j޶*nSS׸w(3k_pdJ#36 K0^b'Vy,>?ܛc>A*βψОMWxhHs ZcYڢ=?'[O&иwD<ϪSkFSAs~x`cW`3;c;PԧB7Sorn2C5$ أuT1'_O+}zY_my+{7oEƦ]ŇU4hm'B!P!~{|.Y~:y}v?\Mӟ0 Jx_T+T-ax/K/Ey^E*!'uWS6C O ݷx'зKOUv.X`Y,oB7qoEH? fy.wTDd}.gYf36" M)pL*>7Xs S׌Z6iOM}.Dr"[n%=Q|Z hAF]&GnD߬moaIy&%8<oQINFι<ExvJ6c"WCdx{ANS:נ 0^2?qd\37v" Պ|[:vyѴ[ -M;yٙdy(TgRkM1ip|?ePM-ݚFnOUY&=Ud 8\o4-f7'F}%b,hY©ɼP(#W t^E; UVEQߊ/ʛBZY#:)km]zѷʼnaZ'7fWpfKCQaVp̩7J7-[+i? #49[Ut[d`gjڤ?I?W؊[:~2qZ/?O튣]9i/nl;7WSElMQۋ&q`;d $b~SM^58XˮUů1(DӢab5ɳÄJk5H&0d-Фz'ΧbqO6Gͅ7 gvZ7[Е,|4Vurzq]ObȉMd*=f߲KtK>f(:fE]*y;fQ~?fUdZ1n!MDt%|"8,>fm.U?P=?Z~NEi\tv} Ŀ6'Oe'#J;$ +(<֐Y \hSdZ JkM} ;^U*!@ ypq!B !,~3/v鵨|=7sB[Dؠ(AyVc;!4zh*ReɃ-i bu,dl/PH&K =:pM|rrֳc96'Ex ![m+4ydE@T'^e]%Z"diR9X[+VMgԴ7{n]N~&ys+H[oM?lINӮ./{,h։Tٸ\5 ? cF^n{1 .vSmBR6SY igzw;n+%Vɝ wmtq'^c` 1¤5zPӘk0 ⭔]1idz)4vrRZ[=_m\A֩ So=P/d5&+n U?r&M帔p/Q\/yIU-1OLS9wj2hZ%Mxn2(˛UCR?J6 >Q?Jux?jO66EM 3tɵEj]dE/M9itt~KE>õhZܦ꿟M6IVJ[?k7"4R$3NDRt|sVm:ߞ3/)g97s{I:2Vnu:u6gO2;=IZy],1 6?AB1^'2Z17fl5; K2|JPЉgoktY)?gCXdOyoc}v.x> 3p((i۹ɬyOڭx%ګ.Q6y{Ѥրe )XOaPeɔ]o|Y6ZRKi~x,< O#̂z:vyNcpeqʽ Zv#d 9+Hޮg-+8MF OLh`zLI 4° #yeUJM"S]Ů-nB蟹x6^|%׋9"/x]n':&dJ~,Zi|7/`[Xdф'F>B2?%ہO 2fyYЕC֤u6~Xs)T0ObRtI !K)k `KKmB%K4\qw*otø4GbWvOrG;5 M>CX쟨^+vV4F;!D.s<ݑtw $'? dċ&"2!׆ {r(m0k}8>i[9 'C}(.4TT/ܽFPa2)_- 0$qgoKGW w9)̟^hBXӹ6Hs$.ꬤXj3 )J*67زu@(ʽ]2m^Lm? aW70]3:`f KiCxd"d!chUS7 \:a? im{ FXfE@ cyP=t'*ce &rlt zb~9{RUx@l Yk,bg"+)?9Tc [Y\®60^=ě6;U^Rkdگ+\DBMߤdZm6CVėx+L \.65*uM:qm6@ԙ0]jop#Ii|]n;MؖIEBQ)hf߿(>z7J;V9r sO;rBy7Z%n"v1}Q BaB;}^S6!ks6RLyTS9>M~ؽ}Q /ym ^E"*p7n_ȴS'~ީb48qWd50+rgC&{bO67x54Qi38twnKrNڏiM_U;Nٹ/ IGO!q>.pv(xQhlx}Q )[7=Ee(OZZ֪Ղ&uӛp _8$:πفۀ[|R_"h~@pώ˞eH|*'*Sp  — "8_RTBH|.x%ث?C>d+.a\QpYJS2U.5䬣o})lJӟ"Z,:]zI꯲ ϶v y-Z}vD`# r37Y%~\WUt#`IϢT<"+̚cqV"" eO`WOxbݛn)p1#Z—0 kvu=W9Ģ0[}uWt%6{ͭҠ݅!uMNYelFV0 .ViIi(щ6ukgYG)^1}GlUWuH[oP0Q}|zwYiK`Q˜&%^ն8WIhh;&L +R<77 r3҇f}%*;fGd I 0P6\}1cE""U&S,Kxm7CgAC>򉠕MVP1Bҽ©r~䔙E´烲xrooT9!(Cn']q.xX(RRI_xʮD] O @%1 <"s A<رƝhY\]\E_+goRM5TxNf5h9("ZRg+V2%1DrOAnХ˓uM>KXݶE[Ͷm@pa@aX?Җ2 wV ?(ЃW$~I:Rg-90T詍ob; 6m+C@ot֚*TiUbxC|ݾ l-H ɸ,{)Y;Ss'LF*;!܇QVnE9hjː)q-mVl@њʩe؈wk3x3kǮ-zXd){;]x̸ :a!ł eB2E,/.4t^rxT06J7/E[o\M3i_ 5|Gc&aoo؍?矅ŽMz1ShwòGоԬr)rBg(+J;m_Jt}/mr=A,*:3rpI ]4T%?g^K5YYBb*jv)v0W!E4S0s˽OC.z-*qFz|T[tM8wYY/ATe7c/]s88nC.qn}0m0[p,j|Ctm*VGh)\+;`ԫECyeZR5P#I9>:j֝0A¿vN"Uq %.~ܫ$ wqf"HS߂Sr[,!\Ԃ1+i_G9ɢMEj~μH^ //tZH7/Mm)ۋd;!. , Nb9Zcg* &(tqKh7qޙ*,|dQH*Өm@"=C"Ep8gZ!"[}f$ʄ|[@RmoWiKo^xcHM|ٗdo4c}1̛P1L,S=/>lXd?-u@&ҪUr׺ uAgZN 6^A?,:D  ^]"]I}2{1g(bBZqǬvBzNyg9.:;ހ <b QYDU"סe~H~#[XAJANc't9 ,Ք@n]6,@%RKˬBod1ta錦3IC/^\(7[ZDdƃ1ɯM< h<׆ː8꘬3B6;iN͙׸ .h3Ͱ?Q(L|THEI̯m}% x}"?w1֮qd @ևxRBpƄ." ^Uݐ9#yddm`bQM-g7CS Q]Y.P! 0E&З\$ "͸Eu>س`9pwg.oE+>PtUE?_Kx1msސ?rOXadx`]&׍0ΦZ5ܴbZ8}$տFDu ^ym/P[_'xs0fQ6vܳE S3\G.Y,a~c '6wD;-q+ix6Wd,;mr[ܦ;㬕{Z]p)[ n1 r=+vNؙ.WN5ޤ-kQbL˕dPB%~9F7%_Yρ-"f ԻfPjGDvqe,V1oz)Rb!\2} Ȝ͐[.]!'z@ᅰS7߮IkA di $S@c&{@MK"S?R׀o]͇[l&MD7&Κ McBæ殛/\ted$>Դ+be'Q*vSb+l5"g $b5+G_J=ߢۑܞ|UԿ[- YL=2;\Q Gp1WSGǗ\f󜏯co#=ee)z;-Δ;1SkhBO3ޅ/`fe&s)dlj+_ [e*MydS$$B 1勉UoVA8&]"JCQig+?]GZ~o̔;;)OM@E]\ˋn\`-=_}ˑRZDçL{ϙwg:\g6Q̐#"ZEfbZnIVX%F#ME73$x)qBqwttp;H!~|V}G`S@^W|=%Ct~mQ&PW >^F&V nY;} 퓵oJδj-EchtٔiF )+xJ4;f''v%DPɨ1o7zu:K>SO OAUfeO ľTZ~8%[vxMGA @6-P j*OMmEkK-_ƒ{;~@@kkx`| rO!{3Hj!_ZkKe<ޙ&Kf>$CGpl7'? n# }<%Or累~7EN\;5 Y݆6ɐ^@(c@ jӂ=dr ,GH{*Bl 4 h'.+W"P?G3]!^L׽ tR ݚ ;8na V\s' fkyO>fj^dǻG5i㬓\j/:K|Ƌḿ^!;D*6Հ8x|~EDok* VeQ%.#u3 l2dxEaAܼMB'CH*υty;h2x'/61urZ;}ϧ"M@$풂'j،ik/N &-^XEYC <ԯZWͱ#OJ ^ pL@<'溩8#GɈ9?+lBć,Zxdz7]?:] uŪ x 4/sDv=wjX;*`Nڽs'zaOPҦ恋y ew'U/7ЀM,h1قb?Q q'A\T ڴoztpMh y4neD+\7J!&JHz{r3Cu˸@ePsڔYmܒTs( 83GcľZ; W*Hr˘rѽKmD09r&_'1C g_nwwuxc{V!+q70oci|-d=8,AɓME] B6X%T'SYSTJ>0 5_2Vlr>@~szepkcw0U&R 0+(( U)B1k&?L=UdFt08339YCv<"5unnMAaـ!9f +i[J{4HR?l:&pnf:ؒkL£f jXUÔ6W҃A`}[71pC3= 8܆RV $:4K* ׅtl@E} PHZC') p7aC !>X GL$o80U )puM257I74oʎ"yK}9]qd"Ñd8Et}:N?Kt09TG] S 5¦mqᮗ Ng>6pRqiH=gVWI=;< oe9q+leRpe,ؼ|K!po[%Gf@~NNBdQ1 8H4,ں) \1Tt̩e`ojJD/Xa\%”'ac"'tD3BW6 "puP^t;> x 6\ǖ[9?W=Sp?}/]2{CtkژpsFȥv 5!ƌt.Cu<ӛ'}|tCS0g0v0+5fo 2]6HCv.BpYc0Roבז$s\'~~fߧwv==#[uGSڊ S3A"\-ȎWْ6x df$})JkBiŽ :Q-gEG'pS 1޺lvF \3H PYTZYD+Էw;{{(@F@ KidUbc7?}n_kǴ-R9&#ݬJJsePYw ) \Sϝ.Թ4Bj6}aFIs&3k ]x)͛~semA>mFV{A^DS-!9[^-[н@ BZ{d ;wV 2A(p3 5hp 9%{Sъ7#\qTw} Rc^U^NT^Y 6DD4o,$B'D;I ɩf}j|g78nDĨw>"8:Ў<> jo:'ɄgMp"N ƃnЖ1l ʌCPEgEP *}5 i#  ?drE҅oFۙL>͒1Щܭ.idִ§/=G0q`L}̐aPEJD't:PwqI#3F̓]bO[3-0/(Pćs-_/0ҡF,H~˦IX4R73%6 U;}2 $883x596 y:v653i#'|NyUc4@N0יet?Q7xzB!J\~!{K?8aY.% gn+trE*鶐`w\8J5#BI.]X*)4[Y w]$@F E#尅 4u5/gFwCnu US19J90=[—x*JDN<}Rץ0d})A3z)#Wagܲ̌bۈzYw̍Y N `P~Px}$GQXXuU )P@(KCtX,iX [ !S2 V(@*BoBn*7g`[C+WZ NaL$o[3䇪k\`޷Ѩ4VyN==`s'{ Ѻ'.L祥);/dHy]uu%0d[/G?J?'/Dq@Gdw*)w'\k8jڼyɗ޻͎ӣ"&6d }^^AJI|$]r4ic5-]N pJ#OO~ȧ4]rеqdY @ f ="['kZ!?uT%B|Ҧ'=P f)&mRF^!;IF0{% >BZ )֛ϟd;]a!,1s",E6Ǯ|I(C?Fg91똩$rᵊ8'@/ t#ݕLɩsjIH)F??t}ݥ 4Brqw.*X*nycTB9 gMyA6o?8w~H9(`>-vT;gJ,.;~g/N_our?efq&=Vf3lcp[e$C(IUKr!鄄l oR} y1ir6,<҄YKxy GǤ99vY^w1CNiQEcS6:x=<6ߜ:nc do.$GZ[ E6 cTQUm^d3@^pϽi _PCd=)hpObHfК\#/(Yp Y?wN1b|c#IE ߣ͈IO bOO8  mxk63y mA}rEFVj1I5{p-jL 2C 7t9OW[yB}ٖ(̛3ko4>~ʟ9Ihw/l3nΥ_z2ѲkQQ,T3Nt/lhEF5LE*‘sؼY^4igO9sSO$o~_nU%mU;D~[Eeףj|8Rgψ;DgV5*f<)S:;~υo-;K%N8vtՒ&]I,vkgļ#RHY(sX9+P HK@'WUd@'Ā/@WDkMw/yӲhhy< >Be-)W ^ @S;';w ĝxuGcsq&!)w=B33)0?dNF@LN"H>ad-G?l}|\$1~ƜwhM Q㥗xy%}sj۱ `>pw"eU.eD)zܸг22ײʍK^p;ZFsaf՝ơkS[]Ӂ)X7F]<}z춣EЅ~a_dV{FɅhF;*C 8$ oR0!0'RUL 0ŮE&flZ(X_uшtƑF]=` NS ٱۙKAw@d8v1|I9n\)p!1^h}*<$*i.ыsc2Q4Yj7e%}4. 3~"qFtwT1R{@9"}KeL:9dQ{$5&$q=i`+@'_J1'z+؛-GH<<v_sf[c*Gf?R6 b: Oiȣ_7?:CDtAWv{xAjH;,@Ԟ#  %7O43bD7yKD%~B<Q#f+퐁%J@}^W㚃7ܩR7P`,(@KOHBTbi:iW>.N M6eGqZ| ]H]DU2&v͹lP:qpOq76 @4" mGXW  4z$ns"73lS`Ω:/n$RkFK9θRjMH=f̃RWt:~g|E vN>L2-n!k %kӾWdK՗$ICT'n:)z>Z4*LVe|,ƌHjY _i2Y>(O'jc  :wdz|HAjwϷwF=~CْqRqOҙߓ8s˙I ~w'`~(`L"ʸlk #m+x\+M ~7 RMfDc2kT?0_͗$*bEś ζW7qi1ʑOW$. 0m*|t3쉹(`c8pݠi(u?h[IEJ(]DzH]ՌNޙ tQrĤ$Y݄.iEZ;n?9MragO~g̱}mc3Ç-'mlX9v #$_2Pb Rɛ0^WE =`X0$w X%S\/>thiScJwM uGw -SeF7QlB~HUȿc3 4,Sf[h( v}5JJ4G7MD6`fXwonT$/83|eY}ef岊%g@%3_zU@H ݻnNL3BҤטlw%f$3tEN.@;_(FÜn6 ܒ=!{b8cgnx_i9ԯQHw JYyDoܾa&~% >>8P&䋭?T#Cߥ/0cS;\$p(zJm ķk/]V =04Kërݤ~*A(_pUEEL Mm@ IfiU^WI]#BK `8GӦř]o@W-G͔_il{Nou1/clqF'* VaB -7xDRRY,=g%L.t E&}؞Ǧ|fşTܬB#{tН5Ch h=ʈ}rmnx\2%:+sݹ8#kN*sXwlYD(dϙq1s[4yO/;Coפ> .}4=l.=ȉ ;o>0Y(Fܑs gXdMIp#<x3ypg3J~LۀM)Ws4P$ !'d}~nlͅg{E/r)뺦tNB.zJSPE&!m(fG| eI!yjx5ݡ eNY\V'sJ@.B >t gƛNcD`C]\543r](]2eѣA.S"nw4v#8^a<9#ȉ_Gg4n;n}Q>&`k@;fh5_/;5;mp$n`E3FH MNdWX±lcp&mYUi|vt~dKг7AO+q;=fz)mpʣXO,`ŻWweaӯĸtPV]23'ogrfJ8[f5OĈ>"1y<eS!<9-a$ ;YSUgkZ St-P",u/+Bzvqe7^xI_@vLY6 t8J8 9/ yvb ȓúю;l+);]@y l'EC"o$x'ȒK6@V+VNܣap /yi%Ը`ΛI UkE4@d>:%6w{Xh7-`ElV  ̯(MW)VyehJq@!J,wlsͅ./i I,'ʆng{R&0sVwJ:`1*୿ 2,# o+z@Sqn ecB9b^fvk#ڹ.1>a$R^Bį mͧ9B-b_,Wf2DK >1Nkӧd2xOis@q޾5{;S&g9cH^%J^ AS%YsЄҐ;TiCDxWf/J_a2;4,'*Q޳u@Bãmֹh,##rK[| ɾ|3t9!o;Bx@fZA LT=[iOy^oPJ1ALMD𥤇Ƴ߹*ޘ)dG8ґ|샕ݹso~AX>DIU0h>ƞlf..҈[>_=.鑚œ@ꬦ 8IZшLŕނ 'WcڋGAQd8e̜~]Ye̻eZ^]F"5{P.5VJ*e늌n (]!"^Fn,eg6PjLuH"/I4/Sۛ5;d{ELz͊N`r[pA+1%JTI}i BY,Ni ȄC94KѾ~"FC4>>#VSj;%:5bgn,]}࿙q IJ5? C܇Ѫ.сw"UD^RKͽ, A~3Ӗy@(4p^<{J =ԍ{@3bx>[^e8<\s *(֑0IksL jN.gPa.Xx-sSu@*ZSq_7.I~2QSbXRFj`򦙟o {׃,_sVje<~6del]AzOf4{ֵl|F-T6(H7Wf-X39ir95oWV#BC`R>e"%t[E ȸ;%Eѹs\PAi:_ ur'CC$׼/ػ O._uRC*SVURF ȭF&W~Cf@n&c \D֝uP2!;|m[\0j)'H)XNi/9dhR>x u~'rI‹LcvEvBu!3`G.SMBlgmx+>qh@Q 9+MW[DsѕgcġJmV7;_)*gEo]oAhQZ@;^͢cҶ+,\%(J|$eQْj%LZ? ,qgFܡݴH2LЌJ !E|/HiA>%6A8t'6@soX\k<ֳ.kb -W*&vj$a .2rǵ1b' >qrhw@0~|"]Ls?"#ži D P޸i\ }R'"O;Q'W14gozYCg@EX{Iu$MhcGzʎK ACONKrXLiub]T)'./҄T3@e ZP%1[ѤꒂK% ë_te?~(9R#qBc#hҾ$f(ܩ\kBx.'׆^CPCs$&2Ja3sר{q)Ȥl5 4V^o槶qsC?M& AX}ER5bIt&y܇/1QDg" }Œ?uNGG:alA%v^ƍWXkN%\#V<6*N_9;Clf\i$['q 7KͷH p6.4t g;},ӝ.^b<.X{!0|Jxꔟ9W'=*k%G@jUqmxҐ/3= Q\/+TOR1prH~(MPO@59"3Ls^LVxS9ꁑ2IToRq=s[T"U(Ќ`1V3^kfYs\dвl< 0dεjJ.*. D'RɦԞ]m~R$k@4PGSp+z~S8b}yD[6/8 Х^!ky+MwU]  wg]~,z.Iur3n,eD5P[3\m>%W?A7@wTS}x/RLӸRk씛?aU7Ih1? hpGț-ō _@Nf%|bmE V= NfXiH^љw?itq> !%E82?iwZ4!n .s䟀TMC6f'PS_\Ȧ@r Z9Zi,Ͻ{%O#;llb)V }DHK^]'<ܾ4`r _ @ʨLIwOɡ4OEutgV"dnE6Sn,NX!NfO>h&CS=[xO㩡G= s]1bU2+ޝ?34'5*ܢAv%)^9GR_ʼnmӔn MU[x-uj(5 !  4  Ѯ&+3C4uOCI+ι*,Tez@ _%'Gpm u ~9q |a-<^GWX:_l_v6ۚ_oC|vrN~CL?9C3#+d@4$Q@.ѵ;}<1+vE: )z\5T? Ҿb^ϘveGtt!G&[;mỂy>J4ٖFhzw3,yǃOQѣyR^+;]r6,:Ξ1cnos_ a? d;6`m?nqË#)Z#|֊ HQ'q_#kJs+ {AhX :,݅IG/_WP`wGDzvd?\Ppnl/_8Y+Ls|'|R.EU¢$@Wu4I {z77B~:;LiR?b:MKl  EK;Lg ׸^Vcd˳{>".vFX4uߨl54SV 64- NAڂ JN\ff ؗh 9!)íyR-f g~o: ˄߹sXD<q1RwQ?ww>6Y~j/ 68ߍam~7#xžEߎY.nw3q'4}<uw*>' Tt}=UBdL$ƈ-dW7I[YW>-5H\,}V*nYebuhSe8XY e&gX·GXϐM\F݊ؔ3܊7Y}Mx&Ldȿ_p](=:r y#2,EhF:䮫['RE7EȾ BZT!; _ ,A͜ @!9[Ә{yoGo5#4G`D7?ч9 hu9{l sěKAhƂ{|ЌJ8:=HQIDt4Ͽ>:7[JÙ^g, w$HS?gSk*2k{I!5^6!/}rG ![+foAyKåe6|Wq٫i9ɫ١xЩ*IB,KLسn~EɕoB|Se4I,Eh9Sg8bh*;cW:neߘ+د`dSB rvh*ق]rr2-o3Ąf2+8 =ѯ#exKXw!ҤV:#塖c 5I Vt 9F)X:ֹ.A `b 6WfiJ`S2Mˢvw3΀VpP,.hb-9IJtIEvJ&V rU# ظdi 1!P,^V7SxrBo!>-eKG(lF)tvnRrہVx۷K܆Ly mZpd Ikb.Q%]R,;ʸ!yXGT-E,[I搋tvdFkI3jQq?i!ycE\IE63XU&ڊ1UͱSSn. qxic[ vTY, @[!K%5&􍇧&Jt\PPɺ&NΖ7```{^]bҎi|vy+H;^.^iv@%*!<~Li\Sɻ&Esۅib'-SgCJ`=y)Otʑ{@8XpUH7j i;d,(tm8 C~/}%K>M(ɷeˬ>B$1F@Are\$nE ᅚtWr@4\ oU ^d:@KHkYܢ#Ơk'B'in;F{Bc|p c쭂 Z$5*ܗ7UW36+&CgR;Nݸ | %2 yf|*WW{ҏ J9@ЙAE_pq eiOc.`i: 3Z@qM_M gSCPEP($(ɼ O$SȟT v+u'#e!}Pd/(o[G'i!w ]NDc{/"U:7i6ZDYwc!eG"!FdW{e$wIZIi#ԩl3nj:K9XGz.RpVgKJnNey#̈́ =n&潢Lwt`sy.lQŎE̺JD{w @r:@^gl[1tLqXz[%8#=IC5B)+^ &XQZa*~фG 'pk\H|S1wn,dYhP]Cd#UcX&R{,?Ztsy}'Q7_KjJ:m `!蹙Hcӡ3eRh W<#ZX+t\),+ԩۮ;1{)mY~խ`y-+ -u;% ΐHQBF½Cg;1@S[H]YB{Mi Njs a=rdfI34h@p'ȦpSYE"~f'%HmN )眼dB%M^ ok)9%8V!-WɁ:ځpl}K=JFᄈp_;[4JR9+ƈ E4qF0  ňs%1,в$:95?#3澨+oW&AqCh"#mw *+z*z'KF#Љ%1Vp#Qz#mDLaP2 @!^D0R#gO@XYi#, |D|$Oj{Ê?]\[+Lp 'g_8=l? 썉O¸,:SzśMC p}`P ғGP/bMѳ/`ޕT_9e՗jV?ȴ26wLgWu`]8ؘOd$(REWF#vυSdВ/|m^<_)D.JOnބ1b gj[ub~H'. Xc3ĝ l<4B%>1c~ q' []: *`Ձ'm*[$ a;||-& L7Fud W@7;\wj .e3Fpѹp7n옹twVb=CNnUA,F m)aij_/֊ =rHdx_wR 8}nvl6 D Ӟq *ay"IOY2,7c." $'IR""ͨI,æ*dg 恻c t=Qӷh396ah[SL37wA,4J^6PP ;^@l ( .MF16PXNi0څOsm.؍ cOɍ,b%҇0Z+ICW uks8qjxY6|sK̭ kك9d_ bs*8h4[f~>MDKC;d+GC77+NEgOw a E20%/~}Otwy%(_̌!ސЊFۙ+3Zͷ{'Tȼzh2q ܚ)V B_!oV,PfmB- 5caQ_`7G~oȁ~p5i}8PxLYm3J>@w i>%Ɓ|nAk4[rtt|f*bD%9ANX`8gs3`ewXc>Wo%jK ';`cI @F,ZӶriߜzcFHEnޯ=@NۨzB ҽUϦm^QUp %J*wEqyC /YqS[>=pv7"{^ocl|M:&o4mD+Bv@f'g|iM}\I>;kf4بmv1V^ W;,۬)ne&S[4"icٿsbD5ǃz%5N5E>hMd t`R3zgƒ]@"7? KJ:%_I:*-vDQlxBKrs"w#~jb_fHn>fM֍ԹYV$Rb $^+%] } _o? /Unf<:ỲkSuyrOoϕMsǟq ѼC#,,1uqo d׽:zqH9%|7/Ysp1m37J#x>&|V`ڜ+-hmgiU3J֏^jꏙ 'h.ɑՒ޼qФVpZEJ2hI!}ZiۤN %`.,} P6߶n ŹᰨKx Ȧj_Aud_ Np]S. /t̝~FA~u^7jꐊ#Uc2D]㪡 BJxA."We{-u~`k|xU^Y~Sߊ?n.扚PË]IKQ14C|Pi~ۯg=~J=?N&Uk>n43gc^ "XXD鶐#w уd(xEk+G4ߴZ;R0;'H~tN.X_$wcz1 7 `J{M Vu}g =-,j7yM_+4nء |cn2/Z%hf6 ^[13:yU:/$L"T{C$sTId?jvjYHI܀>^781Q % ]uڸWqUbD w aN m3\YhS3|>C+4.bLIKImpav0`X]ܵn۱|'z[`n#];Yu`@^6 8*AgVE߃Tu([*H)^oKHɉkESM>@mÙwԌ9#r2Pw(Ig.Ȉ\]HD`8- r}ꎦO{'3' ϝac%K"IkC/wi1 t Jut㞘49KU~E1jAS:2O MβqvP{Ht1[H~NnZݫjO1aHW"ۆh=ŒkB]:_}Іa#e:PG ).-$J!7KϦ 8yUK71Ŝ2rQA0>%S)Jhz [@#EWh]K747]tESj֌S@|],H2;BtE^9@'_P 67:PUd hoY-I/zS/.>= >z^KqY_թEJjuP."q۷)pU&QG [M(;,29=B8؄I}*ٙeB7}E¤o'5Ziq9 t, "/,}ȫ;2TWm!_wiG)]y?QӁtL3f_^繢ЗŹqikHϹ?'W^/w:NYxQ  +Bigj@=7MXM]l:A`'p JX?Ma. yyzuצ:4N37256לdE/ųĥt.>䚉NȐM](W&3eD[ޞo]7~ e.*<I] [u} x&AvpicJX̿2 1mLdLsagux@zġ}rpnRn",`sOa=aZ|#fqRue^lRv`ɚF@.ZK#-k +M]9TxKbd\mUAkc}2Mxga7O-+>#4s$դCi% ''(Ul> EګV ! hbTɁh~kcWS!6ԓZ;GoL휋zr3SpY~ڧK텯H.3?$J _]rȀM6*y'Bqh"8&@TR8 m씐i۱bH!B [)3%/J%䫊/޻# ;74iZ8ou)Xe+<ȣYZ<8B¿r)A:@#8frO,jVLVUm/kP_*2ִ8y/ؾ 1Xխ7,0rfwqb)s@VilyEtoAz}jJ]eQڭu?aU>,<1vR|Fu(=>3֙}R#do޶kU*'koJR-ap+xbNW{#Ǽܰ 2H֡fb0W-,< C4sVJ:thF7u~U(q/uO;.AJ_._#;Y/M^®6dޝ9'wؿyڍW8KęEYMQ[UwCBn+f.7Pgc}(Z K'̸%xhM'Q_C;C 0ޑ5Z6{'p/Žu͡y |$hixs"4GU4wĸ:!ys[kI;ӡinIn6[6K%,/EMagt0q LjkxQu5dYlx-wp2PH M(HGifƃM9?Y _Ktۍ>F2| NAQxb)G;RHO7gXFSMr!{A$4# eA.TN!\z?9P:t g _ܥwQJr\Lֶ249gguP(ZȱÃ|d?`qOW4oHkoU N.K&)֗ȬWj٧jY65͕e32Y-jϭ}5B`H3#_l$iz${}FfcJ;:i0knV-䈼Χ# d4R)0#q+9 yZJً_t R'&ut٬*H~U#BipCO 0oX^D~]RूĞ@44l/t;cy\?iw;~0fRd@<#& 9f| )АzJ7v_*?fP~*bMH"k2'ƅ-F@'4T~Q˱8ivO.N,?[Exj 7LK{OskkE{guYagj c^`]>I4~0_Q3 /uLԖ(|lN;5MMc̠ `^bد?bo4:`FWz*~R[ݏgf9z{p5M+CȹU/>jC!Xm7ڜ3D/Vi7ߏzJ\J}X`JǝE6*ERf)y*M#NEsD_1-" U쉑 )Oqd?yD*EZMJ{^}@\WLT hӬL?8L PMQ_(PwVG8PU2&Hr 5Nւ۟@oҙ K2C mnms+7im`jj'MCT-4Ӄ (VH.R@ b@M7[NoRQYz5g;M5Qi{;B5qiP7BUQ_W0 `W\Ha™r4 &W;[T wܤaR_Iads:vK7Vɮ!+-9Gv##a<|䕤$[\ PEyNjDG@,I)馆"RF?yv:g@"vFip=H%\xdG/Hh: ra[I #e|=em,֢r'_cۑR$ BAȄ,s  (s22k!\(b2(a ˧PUN+G$ `-aq1V,kLe"xKw{r }<)53R؂Qr3 d#`dƱ:nwc*I.덕HtI遼P|F)d{^6[eG/ ]"NH1>t^?1b;r^6Fj'X)E'i*~"^8NhWɌ`2&PR{@nYK$52@;>U-(>NRG[k6--^$..B+SdR )  o0}O|y~3W뢃~*v?.SG+BkQDXCױ` ߝrL{CC6=ӘO5 /KMZ#hd!4 >kx:?X:4$fc.vR3f葳 .b(Hb=,@r uFӰNvOԟ13%oJ,NwQF\zυwܳP~Bq}m/魯N$ȣMOE^46IeC4`9; #95Uq/3Ėl7[I{|cHcY*e%6dUwz0T bQAEƛM-T - vr`p8@47qM 40A <3rlr+Y >@-"gҮ[A4nTs:ƺ%S[giWU_S mI`hHr!dEhP@ȏjȱ=sZ[Eiȿ"8 ^9$|r-4mwB"ة mՅO!FO!}Λsg{aj ȶٽI8 `=KSil\36+P}i3c9]ϴ{ELyj\Φ~3\IU潅)i #Pd${{jȋ) ~!6.o {1yIsl&z(K7pSݎ |ا|HO9V6R}!Co5VpE?,r@ ˜|F@df6݅6&ɲV08TI^N&-t:4_\a%01(\&S6v qM}[BŞ vX.^]y_׹ v(Ј<~q&3hC&w[hxY-;V3`F;,jN/ RK@? m) '[:;?Y3䥸04nɜ@Nؑ9.cU+3ypQ:V$Papʑ,I73TNM ߗ\xOnˬI(B3@" 65 V^a@׎E{f1CGf/ǫ3l^̐M3Rs01sa$ pI[Myn#@l9S[w@'Q_ȃ0ɭtQ=,HEM"J4 d rZ ϭh.0|,577Y@.S\^imٽSi37ڂNĞe=F%!7 B-L{9-6ϒe~}95^ctccj(N=AnFOf+> ajeiN;&Ik˘6. 3=߾'}Oay3o ft5cEFv_^L­>Q X2sRܖ\9P4P\E}Ӄ2i$ko1 gNJb# "l!C][ v\&|B 'UOl . ﲔ@3¤Ga9)&YX}%ucB4,rP_6F =+P9<ۋ1T'wʾ]VwӶm3Vzp!V1~ t+)m>?lZw(ET#f}γ@]b-RvPsI7\ivpu mv?Az'Uft"#2R)rM* n>* QQ% }ȹ+rZ+F>C3*3dz}5o^`vh4Pf"t?Ci3U[2eȗ`Q. (8-gQ2zHt{rj=C< ɪ;DQe;#$P!~Z%6 (ߜ\L '@.z\lo22/VΚy"c$@;}KVf-W(D-)l~jw`rqBSa("|Bxܩoo:8]?x@e{\xB}/ 8c2UCdM`,t#=ϐzwkE):S_mܧF!SJ&t3(3Y> ڿA{'{Ozg =3'ӌV$ M7ᳯ '&& 3OХ1il #-}Dސu A|# Iq-L-1>"xu% ߳qD2%βXKrK pg $9s֍u8t g;TTI3=KZ.ƍ h!uqw ֑\7' yxb`T2 [`O8}4,s,@K\>]"sP (y]^9{".Np_MJ[3@ t_sp54fi㉹cT=:?17(Y!gnօ|iSrOaW%Ex2~)Ik. ]y++jqj:u s 4/sUGhhK_(('J:"5wo; t7qoh+cY$pE=_DI]qmilv㭨A%7h{hd=NVhΤ((`a.TN#^1g?|мU³@ٝpDTd@ .+RƆYg0S!@,=C^zICC'8d^6 Ruo%bgNlGpꅛqLOdO_Мq3#'uGoW'iF3–Dq,4k>n-PZ*'K/@Oݸ 8l\l04ɉBzZ<ͫu/ ]S{gƆhTȒ~ыn WÜ.Ys`Ą 5˧Y9e - "8.[1Q}͙4૖iWT2!7%Qp ^2^ Su ^uǢ-yl+^Tub2jLBZ1w!L!C ?x>u[X;vjvDSq=BWMN^5[Wu/{}24ϝi7%R[큥I,\,^R@y" B/Brj4?.槪`Mד>ڎCZ,eoSHv"Qy~p9o(}x Ⱥ7 %gہ&0IQ/C>;K )XT▯X7D;}Uק8m#@f7?z}* o^RJQ͹?Nc8ʏ `oPG'm#< ]2)6| 3iIrY%Q:}"NH#5 'Li)V$=jɦuǹsK̝ۙZ:^?XrǷ|M zab;8[0n 'O/xE驐3ԑ3:PIqrQsF}7/vj+$x5xC߀L&c  g%Hs[ j!'K MY5\d:[AxSegEtG6xĄ.`e?zj{$8=r#/d/eX$cqMlE^&y-ܴbѨn3 ILmK 8n BCdl{|"Y ̗-@c^e\my;#72 _rV򹌐~&>8e6g3V&ܭPGg }eDv7>vgw (VlŅY"S jef3Hӎݕ3>.[.3bnh:f䨣dlqUn>3b X,Qguf$"2^5J5~Rm/ 䞛?+9JQXLV-9+aC7wu9/!vP1NFgΧe ӡ壺hؼ,XAʹ{Ps*d+B>"[~* JGns/Z'ܘ"r~8JY* e"řwg t>_9G6P1voy!>@Dm٣wvc\q9dt5H+d7%O¦95c=綦SaNvfKYA)ΪP~^ Ғ}߹LcpXGveۂ^>2΁B-wo.#+9-v&){Lb;:׋<%+)w<9~-Cвkı I Sy+RFY[T)yQXRZx{X5fW| x+нTv/Q-N)%U ½*mT8WwewAr yMN"v^ԮS,J6"VuCG-Ttʤ|Ĭ7l49| Q a.ZÏo7} L%Z#۸ZX$$}(iAEKža Iʢ5>E(lJRRbg= %>X(9QBzQSorxOtn&YUَ MB' QW9>Rlm:H/P3U(@3']˜Q@vImX=/#Yz)=4c 1L)) { scal <- PROQUINT_WORD^(rsequence(n_words) - 1L) idx <- rep(x, n_words) %/% scal %% PROQUINT_WORD } else { idx <- x } } else if (is.logical(x) && all(is.na(x))) { ## Eww nasty corner case here that turns up because a naked 'NA' ## in R is a logical vector. return(rep(NA_character_, length(x))) } else { if (inherits(x, "bignum")) { x <- list(x) } else if (!is_bignum_list(x)) { stop("Invalid type for 'x'") } is_na <- lengths(x) == 0 if (any(is_na)) { return(na_recall(x, NA_character_, int_to_proquint, use_cache, missing = is_na)) } base <- openssl::bignum(PROQUINT_WORD) f <- function(el) { n_words <- big_log_ceil(el, PROQUINT_WORD) if (n_words == 1L) { as_integer_bignum(el) } else { pow <- rsequence(n_words) - 1L vapply(n_words - seq_len(n_words), function(pow) as_integer_bignum(el %/% (base^pow) %% base), integer(1)) } } tmp <- lapply(x, f) idx <- unlist(tmp) n_words <- lengths(tmp) } words <- int_to_proquint_word(idx, use_cache, FALSE) if (any(n_words > 1L)) { ## It might be worth a special case here where all things are the ## same length because we can use matrix/apply/paste? i <- rep(seq_along(x), n_words) res <- as.character(tapply(words, i, paste, collapse = "-")) } else { res <- words } res } ##' @param as The target data type for conversion from proquint. The ##' options are \code{integer}, \code{numeric} and \code{bignum}. ##' The first two will overflow given sufficiently large input - ##' this will throw an error (overflow is at ##' \code{.Machine$integer.max} and \code{2 / .Machine$double.eps - 1} ##' for \code{integer} and \code{numeric} respectively). For ##' \code{bignum} this will return a \emph{list} of \code{bignum} ##' elements \emph{even if \code{p} is of length 1}. ##' @rdname proquint_conversion ##' @export proquint_to_int <- function(p, as = "numeric", use_cache = TRUE) { as <- match.arg(as, c("integer", "numeric", "bignum")) if (length(p) == 0L) { return(switch(as, integer = integer(0), numeric = numeric(0), bignum = list())) } if (anyNA(p)) { na <- switch(as, integer = NA_integer_, numeric = NA_real_, bignum = list(NULL)) return(na_recall(p, na, proquint_to_int, as, use_cache)) } if (!is.character(p)) { stop("Expected a character vector for 'p'") } err <- !grepl(PROQUINT_RE, p) if (any(err)) { stop("Invalid identifier: ", paste(sprintf("'%s'", p[err]), collapse = ", ")) } words <- strsplit(p, "-") len <- lengths(words) idx <- proquint_word_to_int(unlist(words), use_cache, FALSE) proquint_combine(idx, len, as) } ##' @export ##' @rdname proquint_conversion ##' @param validate Validate the range of inputs? Because these ##' functions are used internally, they can skip input validation. ##' You can too if you promise to pass sanitised input in. If ##' out-of-range values are passed in and validation is disabled the ##' behaviour is undefined and subject to change. proquint_word_to_int <- function(w, use_cache = TRUE, validate = TRUE) { if (anyNA(w)) { return(na_recall(w, NA_integer_, proquint_word_to_int, use_cache, validate)) } if (validate) { err <- !grepl(PROQUINT_RE1, w) if (any(err)) { stop(sprintf("Invalid proquint word: %s", paste(sprintf("'%s'", w[err]), collapse = ", "))) } } if (use_cache) { idx <- match(w, proquint_word_cache()) - 1L } else { sx <- matrix(unlist(strsplit(unlist(w, use.names = FALSE), NULL)), ncol = 5L, byrow = TRUE) i <- array(0L, dim(sx)) i[, PROQUINT_IDX_C] <- match(sx[, PROQUINT_IDX_C], PROQUINT_CONSONANT) - 1L i[, PROQUINT_IDX_V] <- match(sx[, PROQUINT_IDX_V], PROQUINT_VOWEL) - 1L idx <- as.integer(drop(i %*% PROQUINT_MULT)) } idx } ##' @export ##' @rdname proquint_conversion int_to_proquint_word <- function(i, use_cache = TRUE, validate = TRUE) { if (length(i) == 0) { # avoid some corner cases here return(character(0)) } if (anyNA(i)) { return(na_recall(i, NA_character_, int_to_proquint_word, use_cache, validate)) } if (validate) { if (!is.numeric(i)) { stop("Invalid proquint word index (not numeric)") } err <- i < 0 | i >= PROQUINT_WORD if (any(err)) { stop(sprintf("Invalid proquint word index (out of range): %s", paste(i[err], collapse = ", "))) } } if (use_cache) { word <- proquint_word_cache()[i + 1L] } else { j <- t(outer(i, PROQUINT_MOD, `%%`)) %/% PROQUINT_MULT j[PROQUINT_IDX_V, ] <- j[PROQUINT_IDX_V, ] + 16L word <- apply(matrix(PROQUINT_POOL[c(j) + 1L], 5L, length(i)), 2, paste0, collapse = "") } word } ## Internal support functions: proquint_sample_words <- function(n, use_cache = TRUE, use_openssl = FALSE) { int_to_proquint_word(rand_i16(n, use_openssl), use_cache) } cache <- new.env(parent = emptyenv()) proquint_word_cache <- function() { if (is.null(cache$proquint_words)) { ## This takes ~0.25s, but would be paid across all words anyway idx <- as.matrix(expand.grid(1:16, 17:20, 1:16, 17:20, 1:16))[, 5:1] let <- array(PROQUINT_POOL[c(idx)], dim(idx)) cache$proquint_words <- apply(let, 1, paste, collapse = "") } cache$proquint_words } rsequence <- function(nvec) { unlist(lapply(nvec, function(n) rev(seq_len(n)))) } big_log_ceil <- function(x, base) { ret <- 1L while (x > base) { ret <- ret + 1L x <- x %/% base } ret } is_bignum_list <- function(x) { is.list(x) && all(vapply(x, function(el) inherits(el, "bignum") || is.null(el), logical(1))) } ## This is a hack until openssl is updated; the development version ## has a new as.integer method that should works nicely. ## ## TODO: before CRAN release, check that this does actually work! See ## the relevant test in test-proquint.R as_integer_bignum <- function(x) { if (openssl_supports_as_integer()) { as.integer(x) # nocov } else { x <- as.raw(x) i <- length(x) - seq_along(x) as.integer(sum(256^i * as.integer(x))) } } openssl_supports_as_integer <- function() { if (is.null(cache$openssl_supports_as_integer)) { cache$openssl_supports_as_integer <- utils::packageVersion("openssl") > "0.9.6" } cache$openssl_supports_as_integer } na_recall <- function(x, na, fun, ..., missing = is.na(x)) { ret <- rep(na, length(x)) i <- !missing ret[i] <- fun(x[i], ...) ret } ## This tries to deal with the nastiness of overflowing but keeps most ## of the sausage factory out of the main functions. proquint_combine <- function(idx, len, as) { grp <- rep(seq_along(len), len) if (as == "bignum") { big_combine1 <- function(x) { n <- length(x) base <- openssl::bignum(PROQUINT_WORD) res <- openssl::bignum(0) for (i in seq_along(x)) { res <- res + x[[i]] * base^(n - i) } res } res <- tapply(idx, grp, big_combine1) attributes(res) <- NULL } else { scal <- PROQUINT_WORD^(rsequence(len) - 1L) res <- tapply(scal * idx, rep(seq_along(len), len), sum) if (as == "integer") { i <- res > .Machine$integer.max if (any(i)) { stop("Integer overflow: cannot represent proquint as integer") } res <- as.integer(res) } else { i <- res >= 2 / .Machine$double.eps if (any(i)) { stop("Numeric overflow: cannot represent proquint as numeric") } res <- as.numeric(res) } } res } rand_i16 <- function(n, use_openssl = FALSE) { if (use_openssl) { r <- matrix(as.integer(openssl::rand_bytes(2 * n)), 2) r[1L, ] <- r[1L, ] * 256L as.integer(colSums(matrix(r, 2))) } else { sample(PROQUINT_WORD, n, replace = TRUE) - 1L } } ids/R/sentence.R0000644000176200001440000001664013007061140013160 0ustar liggesusers#' Create a sentence style identifier. This uses the approach #' described by Asana on their blog #' \url{https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly/}. #' This approach encodes 32 bits of information (so 2^32 ~= 4 billion #' possibilities) and in theory can be remapped to an integer if you #' really wanted to. #' #' @title Sentence style identifiers #' #' @inheritParams ids #' #' @param past Use the past tense for verbs (e.g., slurped or jogged #' rather than slurping or jogging) #' #' @export #' @author Rich FitzJohn #' @examples #' # Generate an identifier #' sentence() #' #' # Generate a bunch #' sentence(10) #' #' # As with adjective_animal, use "style" to control punctuation #' sentence(style = "Camel") #' sentence(style = "dot") #' sentence(style = "Title") #' #' # Change the tense of the verb: #' set.seed(1) #' sentence() #' set.seed(1) #' sentence(past = TRUE) #' #' # Pass n = NULL to bind arguments to a function #' id <- sentence(NULL, past = TRUE, style = "dot") #' id() #' id(10) sentence <- function(n = 1, style = "snake", past = FALSE) { verbs <- if (past) asana_verbs_past else asana_verbs_present ids(n, asana_ids, asana_adjectives, asana_nouns, verbs, asana_adverbs, style = style) } asana_ids <- as.character(2:33) asana_adjectives <- c( "adorable", "adventurous", "alluring", "amazing", "ambitious", "amusing", "astonishing", "attractive", "awesome", "bashful", "bawdy", "beautiful", "bewildered", "bizarre", "bouncy", "brainy", "brave", "brawny", "burly", "capricious", "careful", "caring", "cautious", "charming", "cheerful", "chivalrous", "classy", "clever", "clumsy", "colossal", "cool", "coordinated", "courageous", "cuddly", "curious", "cute", "daffy", "dapper", "dashing", "dazzling", "delicate", "delightful", "determined", "eager", "embarrassed", "enchanted", "energetic", "enormous", "entertaining", "enthralling", "enthusiastic", "evanescent", "excited", "exotic", "exuberant", "exultant", "fabulous", "fancy", "festive", "finicky", "flashy", "flippant", "fluffy", "fluttering", "funny", "furry", "fuzzy", "gaudy", "gentle", "giddy", "glamorous", "gleaming", "goofy", "gorgeous", "graceful", "grandiose", "groovy", "handsome", "happy", "hilarious", "honorable", "hulking", "humorous", "industrious", "incredible", "intelligent", "jazzy", "jolly", "joyous", "kind", "macho", "magnificent", "majestic", "marvelous", "mighty", "mysterious", "naughty", "nimble", "nutty", "oafish", "obnoxious", "outrageous", "pretty", "psychedelic", "psychotic", "puzzled", "quirky", "quizzical", "rambunctious", "remarkable", "sassy", "shaggy", "smelly", "sneaky", "spiffy", "swanky", "sweet", "swift", "talented", "thundering", "unkempt", "upbeat", "uppity", "wacky", "waggish", "whimsical", "wiggly", "zany") asana_nouns <- c( "aardvarks", "alligators", "alpacas", "anteaters", "antelopes", "armadillos", "baboons", "badgers", "bears", "beavers", "boars", "buffalos", "bulls", "bunnies", "camels", "cats", "chameleons", "cheetahs", "centaurs", "chickens", "chimpanzees", "chinchillas", "chipmunks", "cougars", "cows", "coyotes", "cranes", "crickets", "crocodiles", "deers", "dinasaurs", "dingos", "dogs", "donkeys", "dragons", "elephants", "elves", "ferrets", "flamingos", "foxes", "frogs", "gazelles", "giraffes", "gnomes", "gnus", "goats", "gophers", "gorillas", "hamsters", "hedgehogs", "hippopotamus", "hobbits", "hogs", "horses", "hyenas", "ibexes", "iguanas", "impalas", "jackals", "jackalopes", "jaguars", "kangaroos", "kittens", "koalas", "lambs", "lemmings", "leopards", "lions", "ligers", "lizards", "llamas", "lynxes", "meerkat", "moles", "mongooses", "monkeys", "moose", "mules", "newts", "okapis", "orangutans", "ostriches", "otters", "oxes", "pandas", "panthers", "peacocks", "pegasuses", "phoenixes", "pigeons", "pigs", "platypuses", "ponies", "porcupines", "porpoises", "pumas", "pythons", "rabbits", "raccoons", "rams", "reindeers", "rhinoceroses", "salamanders", "seals", "sheep", "skunks", "sloths", "slugs", "snails", "snakes", "sphinxes", "sprites", "squirrels", "takins", "tigers", "toads", "trolls", "turtles", "unicorns", "walruses", "warthogs", "weasels", "wolves", "wolverines", "wombats", "woodchucks", "yaks", "zebras") asana_verbs_past <- c( "ambled", "assembled", "burst", "babbled", "charged", "chewed", "clamored", "coasted", "crawled", "crept", "danced", "dashed", "drove", "flopped", "galloped", "gathered", "glided", "hobbled", "hopped", "hurried", "hustled", "jogged", "juggled", "jumped", "laughed", "marched", "meandered", "munched", "passed", "plodded", "pranced", "ran", "raced", "rushed", "sailed", "sang", "sauntered", "scampered", "scurried", "skipped", "slogged", "slurped", "spied", "sprinted", "spurted", "squiggled", "squirmed", "stretched", "strode", "strut", "swam", "swung", "traveled", "trudged", "tumbled", "twisted", "wade", "wandered", "whistled", "wiggled", "wobbled", "yawned", "zipped", "zoomed") asana_verbs_present <- c( "ambling", "assembling", "bursting", "babbling", "charging", "chewing", "clamoring", "coasting", "crawling", "creeping", "dancing", "dashing", "driving", "flopping", "galloping", "gathering", "gliding", "hobbling", "hopping", "hurrying", "hustling", "jogging", "juggling", "jumping", "laughing", "marching", "meandering", "munching", "passing", "plodding", "prancing", "running", "racing", "rushing", "sailing", "singing", "sauntering", "scampering", "scurriing", "skipping", "slogging", "slurping", "spying", "sprinting", "spurting", "squiggling", "squirming", "stretching", "striding", "strutting", "swimming", "swinging", "traveling", "trudging", "tumbling", "twisting", "wading", "wandering", "whistling", "wiggling", "wobbling", "yawning", "zipping", "zooming") asana_adverbs <- c( "absentmindedly", "adventurously", "angrily", "anxiously", "awkwardly", "bashfully", "beautifully", "bleakly", "blissfully", "boastfully", "boldly", "bravely", "briskly", "calmly", "carefully", "cautiously", "cheerfully", "cleverly", "cluelessly", "clumsily", "coaxingly", "colorfully", "coolly", "courageously", "curiously", "daintily", "defiantly", "deliberately", "delightfully", "diligently", "dreamily", "drudgingly", "eagerly", "effortlessly", "elegantly", "energetically", "enthusiastically", "excitedly", "fervently", "foolishly", "furiously", "gallantly", "gently", "gladly", "gleefully", "gracefully", "gratefully", "happily", "hastily", "haphazardly", "hungrily", "innocently", "inquisitively", "intensely", "jokingly", "joshingly", "joyously", "jovially", "jubilantly", "kiddingly", "knavishly", "knottily", "kookily", "lazily", "loftily", "longingly", "lovingly", "loudly", "loyally", "madly", "majestically", "merrily", "mockingly", "mysteriously", "nervously", "noisily", "obnoxiously", "oddly", "optimistically", "overconfidently", "outside", "owlishly", "patiently", "playfully", "politely", "powerfully", "purposefully", "quaintly", "quarrelsomely", "queasily", "quickly", "quietly", "quirkily", "quizzically", "rapidly", "reassuringly", "recklessly", "reluctantly", "reproachfully", "sadly", "scarily", "seriously", "shakily", "sheepishly", "shyly", "silently", "sillily", "sleepily", "slowly", "speedily", "stealthily", "sternly", "suspiciously", "sweetly", "tenderly", "tensely", "thoughtfully", "triumphantly", "unabashedly", "unaccountably", "urgently", "vainly", "valiantly", "victoriously", "warmly", "wearily", "youthfully", "zestfully") ids/R/uuid.R0000644000176200001440000000170013007061140012311 0ustar liggesusers#' Generate UUIDs using the uuid package. This is simply a thin #' wrapper around \code{uuid::UUIDgenerate} that matches the interface #' in the rest of the ids package. #' #' @title Generate UUIDs #' #' @inheritParams ids #' #' @param drop_hyphens Drop the hyphens from the UUID? #' #' @param use_time Passed through to \code{UUIDgenerate} as \code{use.time}. #' #' @export #' #' @importFrom uuid UUIDgenerate #' @author Rich FitzJohn #' @examples #' # Generate one id #' uuid() #' #' # Or a bunch #' uuid(10) #' #' # More in the style of random_id() #' uuid(drop_hyphens = TRUE) uuid <- function(n = 1, drop_hyphens = FALSE, use_time = NA) { if (is.null(n)) { force(drop_hyphens) force(use_time) function(n = 1) { uuid(n, drop_hyphens, use_time) } } else { res <- vapply(seq_len(n), function(i) uuid::UUIDgenerate(use_time), character(1)) if (drop_hyphens) gsub("-", "", res, fixed = TRUE) else res } } ids/R/ids.R0000644000176200001440000000205613007061140012127 0ustar liggesusers## A future version of this will keep a pool so that duplicates are ## excluded. For now we'll just rely on the reasonably large pool. #' Generic id generating function #' @title Generic id generating function #' #' @param n number of ids to return. If \code{NULL}, it instead #' returns the generating function #' #' @param ... A number of character vectors #' #' @param vals A list of character vectors, \emph{instead} of \code{...} #' #' @param style Style to join words with. Can be one of "Pascal", #' "camel", "snake", "kebab", "dot", "title", "sentence", "lower", #' "upper", and "constant". #' #' @return Either a character vector of length \code{n}, or a #' function of one argument if \code{n} is \code{NULL} #' #' @export #' @author Rich FitzJohn #' @examples #' # For an example, please see the vignette ids <- function(n, ..., vals = list(...), style = "snake") { combine <- make_combine(style) force(vals) gen <- function(n = 1) { combine(vapply(vals, sample, character(n), n, replace = TRUE)) } if (is.null(n)) gen else gen(n) } ids/vignettes/0000755000176200001440000000000013113501076013036 5ustar liggesusersids/vignettes/src/0000755000176200001440000000000013113306233013623 5ustar liggesusersids/vignettes/src/ids.R0000644000176200001440000002306113113306233014527 0ustar liggesusers## --- ## title: "ids" ## author: "Rich FitzJohn" ## date: "`r Sys.Date()`" ## output: rmarkdown::html_vignette ## vignette: > ## %\VignetteIndexEntry{ids} ## %\VignetteEngine{knitr::rmarkdown} ## %\VignetteEncoding{UTF-8} ## --- ##+ echo = FALSE, results = "hide" knitr::opts_chunk$set(error = FALSE) human_no <- function(x) { s <- log10(floor(x + 1)) p <- c(0, thousand = 3, million = 6, billion = 9, trillion = 12) i <- s > p j <- max(which(i)) str <- names(p)[j] if (nzchar(str)) { paste(signif(x / 10^p[[j]], 3), str) } else { as.character(x) } } set.seed(1) ## The `ids` package provides randomly generated ids in a number of ## different forms with different readability and sizes. ## ## Random bytes ## The `random_id` function generates random identifiers by generating ## `bytes` random bytes and converting to hexadecimal (so each byte ## becomes a pair of characters). Rather than use R's random number ## stream we use the `openssl` package here. ids::random_id() ## All `ids` functions take `n` as the first argument to be the number ## of identifiers generated: ids::random_id(5) ## The default here is 16 bytes, each of which has 256 values (so ## 256^16 = 2^128 = 3.4e38 combinations). You can make these larger or ## smaller with the `bytes` argument: ids::random_id(5, 8) ## If `NULL` is provided as `n`, then a generating function is ## returned (all ids functions do this): f <- ids::random_id(NULL, 8) f ## This function sets all arguments except for `n` f() f(4) ## ## UUIDs ## The above look a lot like UUIDs but they are not actually UUIDs. ## The `uuid` package provides real UUIDs generated with libuuid, and ## the `ids::uuid` function provides an interface to that: ids::uuid() ## As above, generate more than one UUID: ids::uuid(4) ## Generate time-based UUIDs: ids::uuid(4, use_time = TRUE) ## and optionally drop the hyphens: ids::uuid(5, drop_hyphens = TRUE) ## ## Adjective animal ## Generate (somewhat) human readable identifiers by combining one or ## more adjectives with an animal name. ids::adjective_animal() ## The list of adjectives and animals comes from ## [gfycat.com](http://gfycat.com), via ## https://github.com/a-type/adjective-adjective-animal ## Generate more than one identifier: ids::adjective_animal(4) ## Use more than one adjective for very long idenfiers ids::adjective_animal(4, 3) ##+ echo = FALSE, results = "hide" n1 <- length(ids:::gfycat_animals) n2 <- length(ids:::gfycat_adjectives) ## There are `r n1` animal names and `r n2` adjectives so each one you ## add increases the idenfier space by a factor of `r n2`. So for 1, ## 2, and 3 adjectives there are about `r human_no(n1 * n2)`, ## `r human_no(n1 * n2^2)` and `r human_no(n1 * n2^3)` possible combinations. ## This is a much smaller space than the random identifiers above, but ## these are more readable and memorable. ## Note that here, the random nunbers are coming from R's random ## number stream so are affected by `set.seed()`. ## Because some of the animal and adjective names are very long ## (e.g. a _quasiextraterritorial hexakosioihexekontahexaphobic ## queenalexandrasbirdwingbutterfly_), in order to generate more ## readable/memorable identifiers it may be useful to restrict the ## length. Pass `max_len` in to do this. ids::adjective_animal(4, max_len = 6) ## A vector of length 2 here can be used to apply to the adjectives ## and animal respectively: ids::adjective_animal(20, max_len = c(5, Inf)) ## Note that this decreases the pool size and so increases the chance ## of collisions. ## In addition to snake_case, the default, the punctuation between ## words can be changed to: ## ## kebab-case: ids::adjective_animal(1, 2, style = "kebab") ## dot.case: ids::adjective_animal(1, 2, style = "dot") ## camel-case: ids::adjective_animal(1, 2, style = "camel") ## PascalCase: ids::adjective_animal(1, 2, style = "pascal") ## CONSTANT_CASE (aka SHOUTY_CASE) ids::adjective_animal(1, 2, style = "constant") ## or with spaces, lower case: ids::adjective_animal(1, 2, style = "lower") ## UPPER CASE ids::adjective_animal(1, 2, style = "upper") ## Sentence case ids::adjective_animal(1, 2, style = "sentence") ## Title Case ids::adjective_animal(1, 2, style = "title") ## Again, pass `n = NULL` here to create a generating function: aa3 <- ids::adjective_animal(NULL, 3, style = "kebab", max_len = c(6, 8)) ## ...which can be used to generate ids on demand. aa3() aa3(4) ## ## Random sentences ## The `sentence` function creates a sentence style identifier. This ## uses the approach described by Asana on [their ## blog](https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly). ## This approach encodes 32 bits of information (so 2^32 ~= 4 billion ## possibilities) and in theory can be remapped to an integer if you ## really wanted to. ids::sentence() ## As with `adjective_animal`, the case can be changed: ids::sentence(2, "dot") ## If you would rather past tense for the verbs, then pass `past = TRUE`: ids::sentence(4, past = TRUE) ## ## proquints ## "proquints" are an identifier that tries to be information dense ## but still human readable and (somewhat) pronounceable; "proquint" ## stands for *PRO*-nouncable *QUINT*-uplets. They are introduced in ## https://arxiv.org/html/0901.4016 ## `ids` can generate proquints: ids::proquint(10) ## By default it generates two-word proquints but that can be changed: ids::proquint(5, 1) ids::proquint(2, 4) ## Proquints are formed by alternating ## consonant/vowel/consonant/vowel/consonant using a subset of both ## (16 consonants and 4 vowels). This yields 2^16 (65,536) ## possibilities per word. Words are always lower case and always ## separated by a hyphen. So with 4 words there are 2^64 combinations ## in 23 characters. ## Proquints are also useful in that they can be tranlated with ## integers. The proquint `kapop` has integer value 25258 ids::proquint_to_int("kapop") ids::int_to_proquint(25258) ## This makes proquints suitable for creating human-pronouncable ## identifers out of things like ip addresses, integer primary keys, ## etc. ## The function `ids::int_to_proquint_word` will translate between ## proquint words and integers (and are vectorised) w <- ids::int_to_proquint_word(sample(2^16, 10) - 1L) w ## and `ids::proquint_word_to_int` does the reverse ids::proquint_word_to_int(w) ## whille `ids::proquint_to_int` and `ids::int_to_proquint` allows ## translation of multi-word proquints. Overflow is a real ## possibility; the maximum integer representable is only about `r ## human_no(.Machine$integer.max)` and the maximum floating point ## number of accuracy of 1 is about `r human_no(2 / ## .Machine$double.eps)` -- these are big numbers but fairly small ## proquints: ids::int_to_proquint(.Machine$integer.max - 1) ids::int_to_proquint(2 / .Machine$double.eps) ## But if you had a 6 word proquint this would not work! p <- ids::proquint(1, 6) ## Too big for an integer: ##+ error = TRUE ids::proquint_to_int(p) ## And too big for an numeric number: ##+ error = TRUE ids::proquint_to_int(p, as = "numeric") ## To allow this, we use `openssl`'s `bignum` support: ids::proquint_to_int(p, as = "bignum") ## This returns a *list* with one bignum (this is required to allow ## vectorisation). ## ## Roll your own identifiers ## The `ids` functions can build identifiers in the style of ## `adjective_animal` or `sentence`. It takes as input a list of ## strings. This works particularly well with the `rcorpora` package ## which includes lists of strings. ## Here is a list of Pokemon names: pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) length(pokemon) ## ...and here is a list of adjectives adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) length(adjectives) ## So we have a total pool size of about `r human_no(length(adjectives) * ## length(pokemon))`, which is not huge, but it is at least topical. ## To generate one identifier: ids::ids(1, adjectives, pokemon) ## All the style-changing code is available: ids::ids(10, adjectives, pokemon, style = "dot") ## Better would be to wrap this so that the constants are not passed ## around the whole time: adjective_pokemon <- function(n = 1, style = "snake") { pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) ids::ids(n, adjectives, pokemon, style = style) } adjective_pokemon(10, "kebab") ## As a second example we can use the word lists in rcorpora to ## generate identifiers in the form `_`, like ## "melancholic_darwin". These are similar to the names of ## docker containers. ## First the lists of names themselves: moods <- tolower(rcorpora::corpora("humans/moods")$moods) scientists <- tolower(rcorpora::corpora("humans/scientists")$scientists) ## Moods include: sample(moods, 10) ## The scientists names contain spaces which is not going to work for ## us because `ids` won't correctly translate all internal spaces to ## the requested style. sample(scientists, 10) ## To hack around this we'll just take the last name from the list and ## remove all hyphens: scientists <- vapply(strsplit(sub("(-|jr\\.$)", "", scientists), " "), tail, character(1), 1) ## Which gives strings that are just letters (though there are a few ## non-ASCII characters here that may cause problems because string ## handling is just a big pile of awful) sample(scientists, 10) ## With the word lists, create an identifier: ids::ids(1, moods, scientists) ## Or pass `NULL` for `n` and create a function: sci_id <- ids::ids(NULL, moods, scientists, style = "kebab") ## which takes just the number of identifiers to generate as an argument sci_id(10) ids/vignettes/ids.Rmd0000644000176200001440000002343713113306233014270 0ustar liggesusers--- title: "ids" author: "Rich FitzJohn" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{ids} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ``` {r echo = FALSE, results = "hide"} knitr::opts_chunk$set(error = FALSE) human_no <- function(x) { s <- log10(floor(x + 1)) p <- c(0, thousand = 3, million = 6, billion = 9, trillion = 12) i <- s > p j <- max(which(i)) str <- names(p)[j] if (nzchar(str)) { paste(signif(x / 10^p[[j]], 3), str) } else { as.character(x) } } set.seed(1) ``` The `ids` package provides randomly generated ids in a number of different forms with different readability and sizes. ## Random bytes The `random_id` function generates random identifiers by generating `bytes` random bytes and converting to hexadecimal (so each byte becomes a pair of characters). Rather than use R's random number stream we use the `openssl` package here. ``` {r } ids::random_id() ``` All `ids` functions take `n` as the first argument to be the number of identifiers generated: ``` {r } ids::random_id(5) ``` The default here is 16 bytes, each of which has 256 values (so 256^16 = 2^128 = 3.4e38 combinations). You can make these larger or smaller with the `bytes` argument: ``` {r } ids::random_id(5, 8) ``` If `NULL` is provided as `n`, then a generating function is returned (all ids functions do this): ``` {r } f <- ids::random_id(NULL, 8) f ``` This function sets all arguments except for `n` ``` {r } f() f(4) ``` ## UUIDs The above look a lot like UUIDs but they are not actually UUIDs. The `uuid` package provides real UUIDs generated with libuuid, and the `ids::uuid` function provides an interface to that: ``` {r } ids::uuid() ``` As above, generate more than one UUID: ``` {r } ids::uuid(4) ``` Generate time-based UUIDs: ``` {r } ids::uuid(4, use_time = TRUE) ``` and optionally drop the hyphens: ``` {r } ids::uuid(5, drop_hyphens = TRUE) ``` ## Adjective animal Generate (somewhat) human readable identifiers by combining one or more adjectives with an animal name. ``` {r } ids::adjective_animal() ``` The list of adjectives and animals comes from [gfycat.com](http://gfycat.com), via https://github.com/a-type/adjective-adjective-animal Generate more than one identifier: ``` {r } ids::adjective_animal(4) ``` Use more than one adjective for very long idenfiers ``` {r } ids::adjective_animal(4, 3) ``` ``` {r echo = FALSE, results = "hide"} n1 <- length(ids:::gfycat_animals) n2 <- length(ids:::gfycat_adjectives) ``` There are `r n1` animal names and `r n2` adjectives so each one you add increases the idenfier space by a factor of `r n2`. So for 1, 2, and 3 adjectives there are about `r human_no(n1 * n2)`, `r human_no(n1 * n2^2)` and `r human_no(n1 * n2^3)` possible combinations. This is a much smaller space than the random identifiers above, but these are more readable and memorable. Note that here, the random nunbers are coming from R's random number stream so are affected by `set.seed()`. Because some of the animal and adjective names are very long (e.g. a _quasiextraterritorial hexakosioihexekontahexaphobic queenalexandrasbirdwingbutterfly_), in order to generate more readable/memorable identifiers it may be useful to restrict the length. Pass `max_len` in to do this. ``` {r } ids::adjective_animal(4, max_len = 6) ``` A vector of length 2 here can be used to apply to the adjectives and animal respectively: ``` {r } ids::adjective_animal(20, max_len = c(5, Inf)) ``` Note that this decreases the pool size and so increases the chance of collisions. In addition to snake_case, the default, the punctuation between words can be changed to: kebab-case: ``` {r } ids::adjective_animal(1, 2, style = "kebab") ``` dot.case: ``` {r } ids::adjective_animal(1, 2, style = "dot") ``` camel-case: ``` {r } ids::adjective_animal(1, 2, style = "camel") ``` PascalCase: ``` {r } ids::adjective_animal(1, 2, style = "pascal") ``` CONSTANT_CASE (aka SHOUTY_CASE) ``` {r } ids::adjective_animal(1, 2, style = "constant") ``` or with spaces, lower case: ``` {r } ids::adjective_animal(1, 2, style = "lower") ``` UPPER CASE ``` {r } ids::adjective_animal(1, 2, style = "upper") ``` Sentence case ``` {r } ids::adjective_animal(1, 2, style = "sentence") ``` Title Case ``` {r } ids::adjective_animal(1, 2, style = "title") ``` Again, pass `n = NULL` here to create a generating function: ``` {r } aa3 <- ids::adjective_animal(NULL, 3, style = "kebab", max_len = c(6, 8)) ``` ...which can be used to generate ids on demand. ``` {r } aa3() aa3(4) ``` ## Random sentences The `sentence` function creates a sentence style identifier. This uses the approach described by Asana on [their blog](https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly). This approach encodes 32 bits of information (so 2^32 ~= 4 billion possibilities) and in theory can be remapped to an integer if you really wanted to. ``` {r } ids::sentence() ``` As with `adjective_animal`, the case can be changed: ``` {r } ids::sentence(2, "dot") ``` If you would rather past tense for the verbs, then pass `past = TRUE`: ``` {r } ids::sentence(4, past = TRUE) ``` ## proquints "proquints" are an identifier that tries to be information dense but still human readable and (somewhat) pronounceable; "proquint" stands for *PRO*-nouncable *QUINT*-uplets. They are introduced in https://arxiv.org/html/0901.4016 `ids` can generate proquints: ``` {r } ids::proquint(10) ``` By default it generates two-word proquints but that can be changed: ``` {r } ids::proquint(5, 1) ids::proquint(2, 4) ``` Proquints are formed by alternating consonant/vowel/consonant/vowel/consonant using a subset of both (16 consonants and 4 vowels). This yields 2^16 (65,536) possibilities per word. Words are always lower case and always separated by a hyphen. So with 4 words there are 2^64 combinations in 23 characters. Proquints are also useful in that they can be tranlated with integers. The proquint `kapop` has integer value 25258 ``` {r } ids::proquint_to_int("kapop") ids::int_to_proquint(25258) ``` This makes proquints suitable for creating human-pronouncable identifers out of things like ip addresses, integer primary keys, etc. The function `ids::int_to_proquint_word` will translate between proquint words and integers (and are vectorised) ``` {r } w <- ids::int_to_proquint_word(sample(2^16, 10) - 1L) w ``` and `ids::proquint_word_to_int` does the reverse ``` {r } ids::proquint_word_to_int(w) ``` whille `ids::proquint_to_int` and `ids::int_to_proquint` allows translation of multi-word proquints. Overflow is a real possibility; the maximum integer representable is only about `r human_no(.Machine$integer.max)` and the maximum floating point number of accuracy of 1 is about `r human_no(2 / .Machine$double.eps)` -- these are big numbers but fairly small proquints: ``` {r } ids::int_to_proquint(.Machine$integer.max - 1) ids::int_to_proquint(2 / .Machine$double.eps) ``` But if you had a 6 word proquint this would not work! ``` {r } p <- ids::proquint(1, 6) ``` Too big for an integer: ``` {r error = TRUE} ids::proquint_to_int(p) ``` And too big for an numeric number: ``` {r error = TRUE} ids::proquint_to_int(p, as = "numeric") ``` To allow this, we use `openssl`'s `bignum` support: ``` {r } ids::proquint_to_int(p, as = "bignum") ``` This returns a *list* with one bignum (this is required to allow vectorisation). ## Roll your own identifiers The `ids` functions can build identifiers in the style of `adjective_animal` or `sentence`. It takes as input a list of strings. This works particularly well with the `rcorpora` package which includes lists of strings. Here is a list of Pokemon names: ``` {r } pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) length(pokemon) ``` ...and here is a list of adjectives ``` {r } adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) length(adjectives) ``` So we have a total pool size of about `r human_no(length(adjectives) * length(pokemon))`, which is not huge, but it is at least topical. To generate one identifier: ``` {r } ids::ids(1, adjectives, pokemon) ``` All the style-changing code is available: ``` {r } ids::ids(10, adjectives, pokemon, style = "dot") ``` Better would be to wrap this so that the constants are not passed around the whole time: ``` {r } adjective_pokemon <- function(n = 1, style = "snake") { pokemon <- tolower(rcorpora::corpora("games/pokemon")$pokemon$name) adjectives <- tolower(rcorpora::corpora("words/adjs")$adjs) ids::ids(n, adjectives, pokemon, style = style) } adjective_pokemon(10, "kebab") ``` As a second example we can use the word lists in rcorpora to generate identifiers in the form `_`, like "melancholic_darwin". These are similar to the names of docker containers. First the lists of names themselves: ``` {r } moods <- tolower(rcorpora::corpora("humans/moods")$moods) scientists <- tolower(rcorpora::corpora("humans/scientists")$scientists) ``` Moods include: ``` {r } sample(moods, 10) ``` The scientists names contain spaces which is not going to work for us because `ids` won't correctly translate all internal spaces to the requested style. ``` {r } sample(scientists, 10) ``` To hack around this we'll just take the last name from the list and remove all hyphens: ``` {r } scientists <- vapply(strsplit(sub("(-|jr\\.$)", "", scientists), " "), tail, character(1), 1) ``` Which gives strings that are just letters (though there are a few non-ASCII characters here that may cause problems because string handling is just a big pile of awful) ``` {r } sample(scientists, 10) ``` With the word lists, create an identifier: ``` {r } ids::ids(1, moods, scientists) ``` Or pass `NULL` for `n` and create a function: ``` {r } sci_id <- ids::ids(NULL, moods, scientists, style = "kebab") ``` which takes just the number of identifiers to generate as an argument ``` {r } sci_id(10) ``` ids/README.md0000644000176200001440000000311313113477621012313 0ustar liggesusers# ids [![Project Status: Active - The project has reached a stable, usable state and is being actively developed.](http://www.repostatus.org/badges/latest/active.svg)](http://www.repostatus.org/#active) [![Linux Build Status](https://travis-ci.org/richfitz/ids.svg?branch=master)](https://travis-ci.org/richfitz/ids) [![codecov.io](https://codecov.io/github/richfitz/ids/coverage.svg?branch=master)](https://codecov.io/github/richfitz/ids?branch=master) [![](https://www.r-pkg.org/badges/version/ids)](https://cran.r-project.org/package=ids) Generate random identifiers in a number of styles: * random ids of any number of bytes, such as `31f6d556fe2b303c` * UUIDs using the `uuid` package, such as `4f0efabf-0375-4a08-89ea-b8f162f07c44` * human readable identifiers in the style `_` (following [gfycat.com](http://gfycat.com)), such as `misanthropic_lungfish` * human readable identifiers in the style of a sentence (following [Asana](https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly), such as `33_enormous_chinchillas_tumbling_elegantly` * [proquint](https://arxiv.org/html/0901.4016) ("PRO-nouncable QUINT-uplet") idenfiers such as `dizuz-soboz` (which can be convereted to an integer such as 40,2638,895) These can all be tweaked with options for length, words that are used, and the case that joins words. There is a function `ids` for creating your own human readable identifiers. ## Installation Install this package from CRAN ```r install.packages("ids") ``` or install the development version with ```r remotes::install_github("richfitz/ids", upgrade = FALSE) ``` ids/MD50000644000176200001440000000333713113501667011352 0ustar liggesusers4091af5f9e2d664bd854e5bac679c25c *DESCRIPTION 375df64adc5fae5096deaab266562515 *LICENSE f767ee5f90930d88dc275ab885442b5c *NAMESPACE 9ba8cec5e1f72548e71fca7b02787252 *NEWS.md 8351b5b164ab62da02ff2dd2f70a5c61 *R/adjective_animal.R b63f08afe8b470a4493d8f145e8e009e *R/case.R 33ed144845912352750b3efd3ef3f3af *R/ids.R d880787be3a7b35acf7598ba2b0c7807 *R/proquint.R 0a03a0a8a851b8eea57ffda9638c61e8 *R/random.R 865026807fc261f909ae6e2306a49b0b *R/sentence.R 872b6e010301a8c2d43e0aecc4489ebc *R/sysdata.rda 924dd61dd367f17ccce8a7e82e6e0d0c *R/uuid.R 30dedc2c41327fdfa49d6395a275412b *README.md f4c799ad4e88e3f0bf27336f3a8029cf *build/vignette.rds e9638c4ec56ef22f9cdf4be179b4c1c3 *inst/doc/ids.R f93337840401937e7235d01e891f731a *inst/doc/ids.Rmd 115d5273e21c7ffd08786848efa172a9 *inst/doc/ids.html 40ab4f2885ec96bba658fd6b74cac30c *man/adjective_animal.Rd 6aae4a2c6db5bb90b45173deb3916235 *man/ids.Rd ca299dbc3302730b5c4d18225a8cca86 *man/proquint.Rd 8c267e7f0426a22bd5011fd54593904f *man/proquint_conversion.Rd ff61f9e3e95c8f5f9ac2b022f26c170d *man/random_id.Rd 881f8a8f0dcc635871614d508d18f7ec *man/sentence.Rd a73a3fe3b420013170685710ff3ec565 *man/uuid.Rd 5ee3b2de5f54fce7f29c2c9a99a6abbb *tests/testthat.R c8852387bcf279c08d6d0df22b108e41 *tests/testthat/test-adjective-animal.R 3224f3bf773eb2058c51054b9dda4454 *tests/testthat/test-case.R 023ca74acf32e93a9beee81e189a2f32 *tests/testthat/test-ids.R 7e855199dc4365f17a2f2f35f019daa6 *tests/testthat/test-proquint.R 930e58d5158d01a7b4b0fa301e9a33ee *tests/testthat/test-random.R 0a1743f4e4ecb086d9bea8ae7664a74f *tests/testthat/test-sentence.R 72372ee0fb68492e8772004fa93a98e5 *tests/testthat/test-uuid.R f93337840401937e7235d01e891f731a *vignettes/ids.Rmd 8232159c7f29f3d78df225ea4e41329b *vignettes/src/ids.R ids/build/0000755000176200001440000000000013113501076012125 5ustar liggesusersids/build/vignette.rds0000644000176200001440000000027413113501076014467 0ustar liggesusersb```b`fff`b2 1# 'fL) MAf qTf栉M$ H Tt]R Rî?#S+`zP԰Aհe ,s\ܠL t7`~΢r=xA$Gs=ʕXVr7}ids/DESCRIPTION0000644000176200001440000000124313113501667012542 0ustar liggesusersPackage: ids Title: Generate Random Identifiers Version: 1.0.1 Authors@R: person("Rich", "FitzJohn", role = c("aut", "cre"), email = "rich.fitzjohn@gmail.com") Description: Generate random or human readable and pronounceable identifiers. License: MIT + file LICENSE URL: https://github.com/richfitz/ids BugReports: https://github.com/richfitz/ids/issues Imports: openssl, uuid Suggests: knitr, rcorpora, rmarkdown, testthat RoxygenNote: 6.0.1 VignetteBuilder: knitr NeedsCompilation: no Packaged: 2017-05-31 08:43:42 UTC; rich Author: Rich FitzJohn [aut, cre] Maintainer: Rich FitzJohn Repository: CRAN Date/Publication: 2017-05-31 08:49:59 UTC ids/man/0000755000176200001440000000000013113306233011577 5ustar liggesusersids/man/sentence.Rd0000644000176200001440000000251013113306233013670 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/sentence.R \name{sentence} \alias{sentence} \title{Sentence style identifiers} \usage{ sentence(n = 1, style = "snake", past = FALSE) } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{style}{Style to join words with. Can be one of "Pascal", "camel", "snake", "kebab", "dot", "title", "sentence", "lower", "upper", and "constant".} \item{past}{Use the past tense for verbs (e.g., slurped or jogged rather than slurping or jogging)} } \description{ Create a sentence style identifier. This uses the approach described by Asana on their blog \url{https://blog.asana.com/2011/09/6-sad-squid-snuggle-softly/}. This approach encodes 32 bits of information (so 2^32 ~= 4 billion possibilities) and in theory can be remapped to an integer if you really wanted to. } \examples{ # Generate an identifier sentence() # Generate a bunch sentence(10) # As with adjective_animal, use "style" to control punctuation sentence(style = "Camel") sentence(style = "dot") sentence(style = "Title") # Change the tense of the verb: set.seed(1) sentence() set.seed(1) sentence(past = TRUE) # Pass n = NULL to bind arguments to a function id <- sentence(NULL, past = TRUE, style = "dot") id() id(10) } \author{ Rich FitzJohn } ids/man/random_id.Rd0000644000176200001440000000351713113306233014030 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/random.R \name{random_id} \alias{random_id} \title{Cryptographically generated random identifiers} \usage{ random_id(n = 1, bytes = 16, use_openssl = TRUE) } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{bytes}{The number of bytes to include for each identifier. The length of the returned identifiers will be twice this long with each pair of characters representing a single byte.} \item{use_openssl}{A logical, indicating if we should use the openssl for generating the random identifiers. The openssl random bytestream is not affected by the state of the R random number generator (e.g., via \code{\link{set.seed}}) so may not be suitable for use where reproducibility is important. The speed should be very similar for both approaches.} } \description{ Random identifiers. By default this uses the \code{openssl} package to produce a random set of bytes, and expresses that as a hex character string. This does not affect R's random number stream. } \examples{ # Generate a random id: random_id() # Generate 10 of them! random_id(10) # Different length ids random_id(bytes = 8) # (note that the number of characters is twice the number of bytes) # The ids are not affected by R's RNG state: set.seed(1) (id1 <- random_id()) set.seed(1) (id2 <- random_id()) # The generated identifiers are different, despite the seed being the same: id1 == id2 # If you need these identifiers to be reproducible, pass use_openssl = FALSE set.seed(1) (id1 <- random_id(use_openssl = FALSE)) set.seed(1) (id2 <- random_id(use_openssl = FALSE)) # This time they are the same: id1 == id2 # Pass \\code{n = NULL} to generate a function that binds your arguments: id8 <- random_id(NULL, bytes = 8) id8(10) } \author{ Rich FitzJohn } ids/man/ids.Rd0000644000176200001440000000147013113306233012647 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ids.R \name{ids} \alias{ids} \title{Generic id generating function} \usage{ ids(n, ..., vals = list(...), style = "snake") } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{...}{A number of character vectors} \item{vals}{A list of character vectors, \emph{instead} of \code{...}} \item{style}{Style to join words with. Can be one of "Pascal", "camel", "snake", "kebab", "dot", "title", "sentence", "lower", "upper", and "constant".} } \value{ Either a character vector of length \code{n}, or a function of one argument if \code{n} is \code{NULL} } \description{ Generic id generating function } \examples{ # For an example, please see the vignette } \author{ Rich FitzJohn } ids/man/uuid.Rd0000644000176200001440000000136513113306233013041 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/uuid.R \name{uuid} \alias{uuid} \title{Generate UUIDs} \usage{ uuid(n = 1, drop_hyphens = FALSE, use_time = NA) } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{drop_hyphens}{Drop the hyphens from the UUID?} \item{use_time}{Passed through to \code{UUIDgenerate} as \code{use.time}.} } \description{ Generate UUIDs using the uuid package. This is simply a thin wrapper around \code{uuid::UUIDgenerate} that matches the interface in the rest of the ids package. } \examples{ # Generate one id uuid() # Or a bunch uuid(10) # More in the style of random_id() uuid(drop_hyphens = TRUE) } \author{ Rich FitzJohn } ids/man/proquint.Rd0000644000176200001440000000451713113306233013756 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/proquint.R \name{proquint} \alias{proquint} \title{Generate random proquint identifiers} \usage{ proquint(n = 1, n_words = 2L, use_cache = TRUE, use_openssl = FALSE) } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{n_words}{The number of words for each identifier; each word has \code{2^16} (65536) possible combinations, a two-word proquint has \code{2^32} possible combinations and an \code{k}-word proquint has \code{2^(k * 16)} possible combinations.} \item{use_cache}{Because there are relatively few combinations per word, and because constructing short strings is relatively expensive in R, it may be useful to cache all 65536 possible words. If \code{TRUE} then the first time that this function is used all words will be cached and the results used - the first time may take up to ~1/4 of a second and subsequent calls will be much faster. The identifiers selected will not change with this option (i.e., given a particular random seed, changing this option will not affect the identifiers randomly selected).} \item{use_openssl}{Use openssl for random number generation, with the primary effect that the identifiers will not be affected by R's random seed (at a small speed cost).} } \description{ Generate random "proquint" identifiers. "proquint" stands for PRO-nouncable QUINT-uplets and were described by Daniel Wilkerson in \url{https://arxiv.org/html/0901.4016}. Each "word" takes one of \eqn{2^16} possibilities. A four word proquint has a keyspace of \eqn{10^19} possibilities but takes only 23 characters. Proquint identifiers can be interchanged with integers (though this is totally optional); see \code{\link{proquint_to_int}} and the other functions documented on that page. } \details{ In the abstract of their paper, Wilkerson introduces proquints: "Identifiers (IDs) are pervasive throughout our modern life. We suggest that these IDs would be easier to manage and remember if they were easily readable, spellable, and pronounceable. As a solution to this problem we propose using PRO-nouncable QUINT-uplets of alternating unambiguous consonants and vowels: proquints." } \examples{ # A single, two word, proquint proquint() # Longer identifier: proquint(n_words = 5) # More identifiers proquint(10) } ids/man/adjective_animal.Rd0000644000176200001440000000410313113306233015343 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/adjective_animal.R \name{adjective_animal} \alias{adjective_animal} \title{Ids based on a number of adjectives and an animal} \usage{ adjective_animal(n = 1, n_adjectives = 1, style = "snake", max_len = Inf) } \arguments{ \item{n}{number of ids to return. If \code{NULL}, it instead returns the generating function} \item{n_adjectives}{Number of adjectives to prefix the anmial with} \item{style}{Style to join words with. Can be one of "Pascal", "camel", "snake", "kebab", "dot", "title", "sentence", "lower", "upper", and "constant".} \item{max_len}{The maximum length of a word part to include (this may be useful because some of the names are rather long. This stops you generating a \code{hexakosioihexekontahexaphobic_queenalexandrasbirdwingbutterfly}). A vector of length 2 can be passed in here in which case the first element will apply to the adjectives (all of them) and the second element will apply to the animals.} } \description{ Ids based on a number of adjectives and an animal } \details{ The list of adjectives and animals comes from \url{https://github.com/a-type/adjective-adjective-animal}, and in turn from \url{gfycat.com} } \examples{ # Generate a random identifier: adjective_animal() # Generate a bunch all at once: adjective_animal(5) # Control the style of punctuation with the style argument: adjective_animal(style = "lower") adjective_animal(style = "CONSTANT") adjective_animal(style = "camel") adjective_animal(style = "kebab") # Control the number of adjectives used adjective_animal(n_adjectives = 3) # This can get out of hand quickly though: adjective_animal(n_adjectives = 7) # Limit the length of adjectives and animals used: adjective_animal(10, max_len = 6) # The lengths can be controlled for adjectives and animals # separately, with Inf meaning no limit: adjective_animal(10, max_len = c(6, Inf), n_adjectives = 2) # Pass n = NULL to bind arguments to a function id <- adjective_animal(NULL, n_adjectives = 2, style = "dot", max_len = 6) id() id(10) } \author{ Rich FitzJohn } ids/man/proquint_conversion.Rd0000644000176200001440000000606013113306233016216 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/proquint.R \name{int_to_proquint} \alias{int_to_proquint} \alias{proquint_to_int} \alias{proquint_word_to_int} \alias{int_to_proquint_word} \title{Convert to and from proquints} \usage{ int_to_proquint(x, use_cache = TRUE) proquint_to_int(p, as = "numeric", use_cache = TRUE) proquint_word_to_int(w, use_cache = TRUE, validate = TRUE) int_to_proquint_word(i, use_cache = TRUE, validate = TRUE) } \arguments{ \item{x}{An integer (or integer-like) value to convert to a proquint} \item{use_cache}{Because there are relatively few combinations per word, and because constructing short strings is relatively expensive in R, it may be useful to cache all 65536 possible words. If \code{TRUE} then the first time that this function is used all words will be cached and the results used - the first time may take up to ~1/4 of a second and subsequent calls will be much faster. The identifiers selected will not change with this option (i.e., given a particular random seed, changing this option will not affect the identifiers randomly selected).} \item{p}{A character vector representing a proquint} \item{as}{The target data type for conversion from proquint. The options are \code{integer}, \code{numeric} and \code{bignum}. The first two will overflow given sufficiently large input - this will throw an error (overflow is at \code{.Machine$integer.max} and \code{2 / .Machine$double.eps - 1} for \code{integer} and \code{numeric} respectively). For \code{bignum} this will return a \emph{list} of \code{bignum} elements \emph{even if \code{p} is of length 1}.} \item{w}{A proquint \emph{word} (five letter string)} \item{validate}{Validate the range of inputs? Because these functions are used internally, they can skip input validation. You can too if you promise to pass sanitised input in. If out-of-range values are passed in and validation is disabled the behaviour is undefined and subject to change.} \item{i}{An integer representing a single proquint word (in the range 0:65535)} } \description{ Convert to and from proquints. } \details{ These functions try to be type safe and predictable about what they will and will not return. For \code{proquint_to_int}, because numeric overflow is a possibility, it is important to consider whether a proquint can be meaninfully translated into an integer or a numeric and the functions will throw an error rather than failing in a more insidious way (promoting the type or returning NA). \code{proquint_word_to_int} always retuns an integer vector of the same length as the input. Missing values are allowed; a missing integer representation of a proquint will translate as \code{NA_character_} and a missing proquint will translate as \code{NA_integer_} (if \code{as = "integer"}), \code{NA_real_}, if \code{as = "numeric"} or as \code{NULL} (if \code{as = "bignum"}). Names are always discarded. Future versions may gain an argument \code{named} with a default of \code{FALSE}, but that setting to \code{TRUE} would preseve names. Let me know if this would be useful. } ids/LICENSE0000644000176200001440000000005313113306233012027 0ustar liggesusersYEAR: 2017 COPYRIGHT HOLDER: Rich FitzJohn