Blob Blame History Raw
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>

static uint64_t seed = -1;
static uint32_t rand32(void)
{
	seed = 6364136223846793005ull*seed + 1;
	return seed >> 32;
}
static uint64_t rand64(void)
{
	uint64_t u = rand32();
	return u<<32 | rand32();
}
static double frand(){return rand64() * 0x1p-64;}
static float frandf(){return rand32() * 0x1p-32f;}
static long double frandl(){return rand64() * 0x1p-64L;}

/* uniform random in [0,n), n > 0 must hold */
uint64_t randn(uint64_t n)
{
	uint64_t r, m;

	/* m is the largest multiple of n */
	m = -1;
	m -= m%n;
	while ((r = rand64()) >= m);
	return r%n;
}

/* uniform on [a,b] */
uint64_t randint(uint64_t a, uint64_t b)
{
	if (b < a) {
		uint64_t t = b;
		b = a;
		a = t;
	}
	return a + randn(b - a + 1);
}

int insert(uint64_t *tab, size_t len, uint64_t v)
{
	size_t i = v & (len-1);
	size_t j = 1;

	/* 0 means empty, v > 0 must hold */
	while (tab[i]) {
		if (tab[i] == v)
			return -1;
		i += j++;
		i &= len-1;
	}
	tab[i] = v;
	return 0;
}

static void shuffle2(uint64_t *p, uint64_t *q, size_t np, size_t nq)
{
	size_t i,r,t;

	i = np+nq;
	while (i > np) {
		r = randn(i);
		i--;
		t = q[i-np];
		if (r < np) {
			q[i-np] = p[r];
			p[r] = t;
		} else {
			q[i-np] = q[r-np];
			q[r-np] = t;
		}
	}
}

/* choose k unique numbers from [0,n), k <= n */
int choose(uint64_t n, size_t k, uint64_t *p)
{
	uint64_t *tab;
	size_t i, j, len;

	if (n < k)
		return -1;

	if (n < 16) {
		/* no alloc */
		while (k)
			if (randn(n--) < k)
				p[--k] = n;
		return 0;
	}

	if (k < 8) {
		/* no alloc, n > 15 > 2*k */
		for (i = 0; i < k;) {
			p[i] = randn(n);
			for (j = 0; p[j] != p[i]; j++);
			if (j == i)
				i++;
		}
		return 0;
	}

	if (n < 5*k && (n-k)*sizeof *tab < (size_t)-1) {
		/* allocation is < 4*k */
		tab = malloc((n-k) * sizeof *tab);
		if (!tab)
			return -1;
		for (i = 0; i < k; i++)
			p[i] = i;
		for (; i < n; i++)
			tab[i-k] = i;
		if (n-k < k)
			shuffle2(p, tab, k, n-k);
		else
			shuffle2(tab, p, n-k, k);
		free(tab);
		return 0;
	}

	/* allocation is < 4*k */
	for (len = 16; len <= 2*k; len *= 2);
	tab = calloc(len, sizeof *tab);
	if (!tab)
		return -1;
	for (i = 0; i < k; i++)
		while (insert(tab, len, randn(n)+1));
	for (i = 0; i < len; i++)
		if (tab[i])
			*p++ = tab[i]-1;
	free(tab);
	return 0;
}

static int cmp64(const void *a, const void *b)
{
	const uint64_t *ua = a, *ub = b;
	return *ua < *ub ? -1 : (*ua > *ub ? 1 : 0);
}

// todo: in place flip problem

/* choose k unique uint64_t numbers */
int choose64(size_t k, uint64_t *p)
{
	size_t i, c;

	/* no alloc, collisions should be very rare */
	for (i = 0; i < k; i++)
		p[i] = rand64();
	do {
		c = 0;
		qsort(p, k, sizeof *p, cmp64);
		for (i = 1; i < k; i++)
			if (p[i] == p[i-1]) {
				p[i-1] = rand64();
				c = 1;
			}
	} while (c);
	return 0;
}

/* equidistant sampling with some randomness */
int sample(uint64_t n, size_t k, uint64_t *p)
{
	uint64_t a = 0;
	uint64_t d = n/k;
	size_t m = n%k;
	size_t i, j;
	uint64_t *q;

	if (!d)
		return -1;
	q = malloc((m+1) * sizeof *q);
	if (!q)
		return -1;
	if (choose(k, m, q))
		return -1;
	qsort(q, m, sizeof *q, cmp64);
	q[m] = k;
	for (i = j = 0; i < k; i++) {
		uint64_t t;

		while (q[j] < i)
			j++;
		if (q[j] == i)
			t = d+1;
		else
			t = d;
		p[i] = a + randn(t);
		a += t;
	}
	free(q);
	return 0;
}

/* [-inf,inf] uniform on representation */
int genall(size_t k, uint64_t *p)
{
	size_t i;
	uint64_t n, d;
	d = 1;
	d <<= 52;
	if (sample(-2*d, k, p))
		return -1;
	n = 0x7ff;
	n <<= 52;
	for (i = 0; i < k; i++)
		if (p[i] > n)
			p[i] += d-1;
	return 0;
}

/* [a,b) uniform on representation, 0 <= a <= b */
int genab(size_t k, uint64_t a, uint64_t b, uint64_t *p)
{
	size_t i;

	if (sample(b-a, k, p))
		return -1;
	for (i = 0; i < k; i++)
		p[i] += a;
	return 0;
}

#define asfloat(x) ((union{uint64_t af_i; double af_f;}){.af_i=x}.af_f)
#define asint(x)   ((union{uint64_t af_i; double af_f;}){.af_f=x}.af_i)

int main(int argc, char *argv[])
{
	uint64_t k, i;
	uint64_t *p;
	double a,b,m;
	char *e;
	int opt;

	k = 1000;
	a = 0;
	b = 1;
	m = 1;
	while ((opt = getopt(argc, argv, "n:a:b:m:s:")) != -1) {
		switch(opt) {
		case 'n':
			k = strtoull(optarg,&e,0);
			break;
		case 'a':
			a = strtod(optarg,&e);
			if (a < 0)
				goto usage;
			break;
		case 'b':
			b = strtod(optarg,&e);
			if (b < 0)
				goto usage;
			break;
		case 'm':
			m = strtod(optarg,&e);
			break;
		case 's':
			seed = strtoull(optarg,&e,0);
			break;
		default:
usage:
			fprintf(stderr, "usage: %s -n num -a absmin -b absmax -m mult -s seed\n", argv[0]);
			return -1;
		}
		if (*e || errno)
			goto usage;
	}
	if (!(a <= b))
		goto usage;
	p = malloc(k * sizeof *p);
	if (!p)
		return -1;
	if (genab(k, asint(a), asint(b), p))
//	if (genall(k,p))
		return -1;
	for (i = 0; i < k; i++)
//		printf("0x%016llx\n", p[i]);
		printf("%a\n", m*asfloat(p[i]));
	return 0;
}