Skip to content

Instantly share code, notes, and snippets.

@hyginn
Last active January 15, 2019 16:56
Show Gist options
  • Save hyginn/af6e768e638355d50222f64e39816553 to your computer and use it in GitHub Desktop.
Save hyginn/af6e768e638355d50222f64e39816553 to your computer and use it in GitHub Desktop.
Full-range, quantum random integers in R from hexadecimals via the qrandom package
# x2int.R
#
# Author: Boris Steipe (ORCID: 0000-0002-1134-6758)
# License: (c) Author (2018) + MIT
# Date: 2019-01-14
#
# A utility to use qrandom::qrandom() to get integers from the full range
# of machine representable integers
# ==== intro ===================================================================
# I've been looking at the fabulous qrandom package (Köstlmeier 2019) that
# provides an interface to the equally fabulous quantum-randomness generator at
# ANU in Canberra. (https://qrng.anu.edu.au) (Haw at al. 2005)
if (!requireNamespace("qrandom")) {
install.packages(qrandom)
}
# ==== motivation ==============================================================
# I had planned to use qrandom() to get better seeds for R's RNG. R seeds the
# RNG once per session from Sys.time() and the process ID. That's a very small
# space of seeds! Moreover, random integers are produced from runif(), and
# multiplying an integer range with a floating point number. As Ottoboni and
# Stark recently showed (2018), that approach has sampling issues. So there is
# an incentive to do better and such improvement might go through working with
# true random numbers. Alas, set.seed() requires a signed 32-bit integer, and
# qrandom() doesn't supply those. Obviously we should be able to piece that
# together from hexadecimals. One would think.
# I was surprised to find that strtoi() does not work for negative numbers:
strtoi("0x7fffffff") # 2147483647 - same as .Machine$integer.max
# ... but the hex of -1 is "0xffffffff", and
strtoi("0xffffffff") # NA
# However, the inbuilt functions do work the other way around:
as.hexmode(-1) # [1] "ffffffff"
# ... which tells us that our representation was correct, but not how
# to obtain it.
# Unfortunately...
qrandom::qrandomunif(1, -.Machine$integer.max, .Machine$integer.max)
# gives an error, and the fabulous ANU quantum random server does not supply
# signed integers. (Looking under the hood of qrandomunif(), these numbers are
# indeed obtained by multiplying an R 32-bit floating point number with a
# disired output range.)
# ==== requirements and design =================================================
# Time to code our own:
# Strategy: break up the 8 digit hex block into 4 two-digit blocks,
# convert these to bits, assemble them in the correct order, and
# reinterpret as a signed 32 bit integer, thus avoiding intermediate conversion
# to integers outside the range at all times.
#
# This required to understand how integers are represented in the machine in
# the first place. Shout out to Chua Hock-Chuan (2014), at NTU Singapore
# for a very helpful tutorial.
# https://www3.ntu.edu.sg/home/ehchua/programming/java/datarepresentation.html
# ==== code ====================================================================
x2int <- function(x) {
# x: an 8 digit hex string
# return: its signed integer equivalent
stopifnot(all(nchar(x) == 8))
x[x == "80000000"] <- "00000000" # input would underflow: "-0"
x2bit <- function(x) {
# x: an 8 digit hex string
# return: a 32 integer vector of [0, 1] as its binary representation
x4 <- substring(x, c(1, 3, 5, 7), c(2, 4, 6, 8))
x4 <- rev(paste0("0x", x4))
m4 <- sapply(x4, FUN = function(x) { intToBits(strtoi(x))[1:8] })
return(as.integer(as.vector(m4)))
}
bit2int <- function(x) {
# x: a 32 integer vector of [0, 1]
# return: its signed integer equivalent
p2 <- 2^(0:30) # powers of two
s <- 1 # initialize sign
if (x[32] == 1) { # sign bit is set
x <- c(ifelse(x[1:31] == 1, 0, 1), x[32]) # flip bits
s <- -1 # sign is negative
}
return(as.integer((s * sum(x[1:31] * p2)) - x[32]))
}
int <- sapply(x, FUN = function(x) { bit2int(x2bit(x)) }, USE.NAMES = FALSE)
return(int)
}
# ==== examples ================================================================
# 10 random integers
(x <- qrandom::qrandom(n = 10, type = "hex16", blocksize = 4))
# [1] "3e1a0d73" "82116a50" "92e2b36c" "4ad988b0" "9e892b8f"
# [6] "5fcc564e" "59c29f0e" "dfbaa377" "b8348f8f" "ed8e7f53"
x2int(x)
# [1] 1041894771 -2112787888 -1830636692 1255770288 -1635177585
# [6] 1607226958 1505926926 -541416585 -1204514929 -309428397
# ==== tests ===================================================================
# Manual test - try anything interesting:
# x <- "d30124c6"
# x2int(x) # -754899770
# as.hexmode(-754899770) # "d30124c6"
#
library(testthat)
test_that("conversions are correct", {
expect_equal(0, x2int("00000000"))
expect_equal(1, x2int("00000001"))
expect_equal(-1, x2int("ffffffff"))
expect_equal(.Machine$integer.max, x2int("7fffffff"))
expect_equal(0, x2int("80000000"))
expect_equal(-.Machine$integer.max, x2int("80000001"))
expect_equal(strtoi("0x12345678"), x2int("12345678"))
expect_equal(12345678, x2int("00bc614e"))
expect_equal(-12345678, x2int("ff439eb2"))
expect_equal(c(1, -1), x2int(c("00000001", "ffffffff")))
expect_error(x2int(c("0001", "ffffffff")))
})
# ==== references ==============================================================
# Chua HC. (2014) A Tutorial on Data Representation.
# https://www3.ntu.edu.sg/home/ehchua/programming/java/datarepresentation.html
# J.Y. Haw, S.M. Assad, A.M. Lance, N.H.Y. Ng, V. Sharma, P.K. Lam, and T. Symul
# (2005) Maximization of Extractable Randomness in a Quantum Random-Number
# Generator. Phys. Rev. Applied 3, 054004
# Köstlmeier, S (2019) qrandom: True Random Numbers using the ANU Quantum Random
# Numbers Server https://cran.r-project.org/package=qrandom
# Ottoboni, K and Stark, PB. (2018) Random problems with R.
# https://www.stat.berkeley.edu/~stark/Preprints/r-random-issues.pdf
# [END]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment