summaryrefslogtreecommitdiff
path: root/libmisc/rand.c
blob: d1643eeee7f8602ab3b654e765d16f1c29839e72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* libmisc/rand.c - Non-crytpographic random-number utilities
 *
 * Copyright (C) 2024-2025  Luke T. Shumaker <lukeshu@lukeshu.com>
 * SPDX-License-Identifier: AGPL-3.0-or-later
 */

#include <stdlib.h> /* for random() */

#include <libmisc/assert.h>

#include <libmisc/rand.h>

uint64_t rand_uint63n(uint64_t cnt) {
	assert(cnt != 0 && ((cnt-1) & 0x8000000000000000) == 0);
	if (cnt <= UINT64_C(1)<<31) {
		uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt;
		uint32_t rnd;
		do {
			rnd = random();
		} while (rnd >= fair_cnt);
		return rnd % cnt;
	} else if (cnt <= UINT64_C(1)<<62) {
		uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt;
		uint64_t rnd;
		do {
			rnd = (((uint64_t)random()) << 31) | random();
		} while (rnd >= fair_cnt);
		return rnd % cnt;
	} else if (cnt <= UINT64_C(1)<<63) {
		uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt;
		uint64_t rnd;
		do {
			rnd = (((uint64_t)random()) << 62) | (((uint64_t)random()) << 31) | random();
		} while (rnd >= fair_cnt);
		return rnd % cnt;
	}
	assert_notreached("cnt is out of bounds");
}