Skip to content

Instantly share code, notes, and snippets.

@Dracovian Dracovian/pcg.c
Created Jan 11, 2020

Embed
What would you like to do?
Taking another stab at PCG random.
#include <stdio.h>
#include <malloc.h>
#include <pthread.h>
#include <time.h>
#define MULTIPLIER 0x5851F42D4C957F2DULL
typedef struct {
pthread_mutex_t *mutex;
long long max, mod;
int rounds;
} args_t;
typedef unsigned long long qword;
typedef unsigned long dword;
typedef struct {
qword state,
increment;
} pcg_t;
void pcg_step(pcg_t *pcg) {
pcg->state *= MULTIPLIER;
pcg->state += pcg->increment;
}
qword pcg_rotate(qword value, qword rotation) {
return (value >> rotation) | (value << ((- rotation) & 63u));
}
qword pcg_output(qword state) {
return pcg_rotate((state >> 32u) ^ state, state >> 59u);
}
void pcg_srandom(pcg_t *pcg, qword init, qword seq) {
pcg->state = 0u;
pcg->increment = (seq << 1u) | 1u;
pcg_step(pcg);
pcg->state += init;
pcg_step(pcg);
}
qword pcg_random(pcg_t *pcg) {
pcg_step(pcg);
return pcg_output(pcg->state);
}
qword pcg_bounded(pcg_t *pcg, long long max, long long mod) {
long long threshold = -max % max;
while (1) {
long long randm = pcg_random(pcg);
if (randm >= threshold)
return (randm % max + 1) + mod;
}
}
void pcg_entropy(pthread_mutex_t *mutex, void *dest, dword size) {
pthread_mutex_lock(mutex);
qword seed = time(NULL);
pcg_t p;
dword temp;
pcg_srandom(&p, seed ^ (qword) &pcg_entropy, (qword) &temp);
char *pdest = (char *) dest;
for (dword i = 0u; i < size; ++i)
pdest[i] = (char) pcg_random(&p);
pthread_mutex_unlock(mutex);
}
void *action(void *args) {
args_t *targs = (args_t *) args;
qword seeds[2];
pcg_entropy(targs->mutex, (void *) seeds, sizeof seeds);
pcg_t pcg;
pcg_srandom(&pcg, seeds[0], seeds[1]);
qword rng = pcg_bounded(&pcg, targs->max, targs->mod);
fprintf(
stdout,
"[%dd%lld%c%lld]: %lld\n",
targs->rounds,
targs->max,
(targs->mod >= 0) ? '+' : '-',
(targs->mod >= 0) ? targs->mod : -targs->mod,
rng
);
return NULL;
}
void print_help(const char *filename) {
fprintf(stdout, "Usage: %s <amount>d<sides>[+-]<modifier>\n", filename);
}
int main(int argc, char **argv) {
if (argc != 2) {
print_help(argv[0]);
return 1;
}
args_t args;
sscanf(
argv[1],
"%dd%lld%lld",
&args.rounds,
&args.max,
&args.mod
);
pthread_mutex_t mutex;
if (pthread_mutex_init(&mutex, NULL) != 0) {
fprintf(stderr, "Mutex init failed!\n");
return 1;
}
pthread_t tid[args.rounds];
for (qword i = 0; i < args.rounds; i++) {
int e = pthread_create(&tid[i], NULL, &action, &args);
if (e != 0)
fprintf(stderr, "Cannot create thread!\n");
}
for (qword i = 0; i < args.rounds; i++)
pthread_join(tid[i], NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
@Dracovian

This comment has been minimized.

Copy link
Owner Author

Dracovian commented Jan 11, 2020

The major improvements are:

  1. The use of mutexes instead of spinlocks.
  2. The use of pthreads.
  3. We can now end up with negative values.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.