commit 3e4e851bfa30869e48cee9e15edc6723bee684f5
parent d6987458f21cf1890045f2606d0f8ec4d2225b44
Author: Mattias Andrée <maandree@kth.se>
Date:   Thu,  3 Mar 2016 23:28:05 +0100
Add zrand
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
2 files changed, 97 insertions(+), 1 deletion(-)
diff --git a/src/internals.h b/src/internals.h
@@ -33,7 +33,8 @@
 
 #define LIST_CONSTS\
 	X(libzahl_const_1e19, zsetu, 10000000000000000000ULL) /* The largest power of 10 < 2⁶⁴. */\
-	X(libzahl_const_1e9,  zsetu, 1000000000ULL)           /* The largest power of 10 < 2³². */
+	X(libzahl_const_1e9,  zsetu, 1000000000ULL)           /* The largest power of 10 < 2³². */\
+	X(libzahl_const_1,    zsetu, 1)
 
 #define X(x)  extern z_t x;
 LIST_TEMPS
diff --git a/src/zrand.c b/src/zrand.c
@@ -0,0 +1,95 @@
+/* See LICENSE file for copyright and license details. */
+#include "internals"
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifndef FAST_RANDOM_PATHNAME
+# define FAST_RANDOM_PATHNAME  "/dev/urandom"
+#endif
+
+#ifndef SECURE_RANDOM_PATHNAME
+# define SECURE_RANDOM_PATHNAME  "/dev/random"
+#endif
+
+
+static void
+zrand_get_random_bits(z_t r, size_t bits, int fd)
+{
+	size_t read_total, n, chars = CEILING_BITS_TO_CHARS(bits);
+	ssize_t read_just;
+	uint32_t mask = 1;
+
+	if (r->alloced < chars)
+		zahl_realloc(r, chars);
+
+	for (n = chars << LB_BITS_PER_CHAR; n;) {
+		read_just = read(fd, (char *)(r->chars) + read_total, n);
+		if (read_just < 0)
+			FAILURE_JUMP();
+		read_total += read_just;
+		n -= read_just;
+	}
+
+	bit = BITS_IN_LAST_CHAR(bit)
+	mask <<= bit;
+	mask -= 1;
+
+	r->chars[chars - 1] &= mask;
+	for (n = chars; n--;) {
+		if (r->chars[n]) {
+			r->used = n + 1;
+			SET_SIGNUM(r, 1);
+			return;
+		}
+	}
+        SET_SIGNUM(r, 0);
+}
+
+void
+zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n)
+{
+	const char *pathname = 0;
+	size_t bits;
+	int fd;
+
+        switch (dev) {
+	case FAST_RANDOM:
+		pathname = FAST_RANDOM_PATHNAME;
+		break;
+	case SECURE_RANDOM:
+		pathname = SECURE_RANDOM_PATHNAME;
+		break;
+	default:
+		abort();
+	}
+
+	if (zzero(n)) {
+		SET_SIGNUM(r, 0);
+		return;
+	}
+
+	fd = open(pathname, O_RDONLY);
+
+	switch (dist) {
+	case QUASIUNIFORM:
+		bits = zbits(n);
+		zrand_get_random_bits(r, bits, fd);
+		zadd(r, r, libzahl_const_1);
+		zmul(r, r, n);
+		zrsh(r, r, bits);
+		break;
+
+	case UNIFORM:
+		bits = zbits(n);
+		do
+			zrand_get_random_bits(r, bits, fd);
+		while (zcmp(r, n) > 0);
+		break;
+
+	default:
+		abort();
+	}
+
+	close(fd);
+}