summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstderr64 <stderr64@null.net>2026-09-24 20:49:27 +0300
committerstderr64 <stderr64@null.net>2026-09-24 20:49:27 +0300
commitf54963e1364568d8f4f6e1a5be1646d4c5e7a94e (patch)
tree447191762cf030afce72724b8374b06348feab2d
downloadlibxorshift-f54963e1364568d8f4f6e1a5be1646d4c5e7a94e.tar.gz
libxorshift-f54963e1364568d8f4f6e1a5be1646d4c5e7a94e.tar.zst
First commit
-rw-r--r--Makefile13
-rw-r--r--include/xorshift.h4
-rw-r--r--structs.h6
-rwxr-xr-xtests/test_statistical_properties.py73
-rw-r--r--xorshift.c64
5 files changed, 160 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4ffc5a4
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+OUTPUT_MULTIPLIER=0x6ab64cc1
+
+rel:
+ mkdir -p ./build
+ $(CC) -Wall -Werror -pedantic -O2 -march=x86-64-v3 -mavx2 -std=gnu23 -DOUTPUT_MULTIPLIER=${OUTPUT_MULTIPLIER} -shared -fPIC ./xorshift.c -o ./build/libxorshift.so
+
+debug:
+ mkdir -p ./build
+ $(CC) -Wall -Werror -pedantic -O2 -march=x86-64-v3 -mavx2 -std=gnu23 -ggdb -DOUTPUT_MULTIPLIER=${OUTPUT_MULTIPLIER} -shared -fPIC ./xorshift.c -o ./build/libxorshift.so
+
+clean:
+ rm -rf ./build
diff --git a/include/xorshift.h b/include/xorshift.h
new file mode 100644
index 0000000..0e58967
--- /dev/null
+++ b/include/xorshift.h
@@ -0,0 +1,4 @@
+bool initialize_states();
+uint32_t get_random_output();
+uint32_t get_uniform_uint( uint32_t min, uint32_t max );
+void clear_rng_state();
diff --git a/structs.h b/structs.h
new file mode 100644
index 0000000..34c923b
--- /dev/null
+++ b/structs.h
@@ -0,0 +1,6 @@
+struct rng_state{
+ size_t state_idx;
+ uint32_t states[16];
+ uint32_t gen_period_ctr;
+ uint8_t period_end_ctr;
+};
diff --git a/tests/test_statistical_properties.py b/tests/test_statistical_properties.py
new file mode 100755
index 0000000..1346293
--- /dev/null
+++ b/tests/test_statistical_properties.py
@@ -0,0 +1,73 @@
+#!/usr/bin/python3 -u
+import sys;
+import argparse;
+from ctypes import *;
+import numpy as np;
+
+aparser = argparse.ArgumentParser();
+aparser.add_argument( "--count", type=int, required=True, help="Amount of number combinations to generate" );
+aparser.add_argument( "--num-count", type=int, required=True, help="Amount of numbers in each combination generated" );
+aparser.add_argument( "--min", type=int, required=True, help="Minimum number" );
+aparser.add_argument( "--max", type=int, required=True, help="Maximum number" );
+args = aparser.parse_args();
+del aparser;
+
+if ( args.max <= args.min ):
+ del args;
+ print( "Error: maximum number must be more than minimum" );
+ sys.exit( -1 );
+
+libxorshift = CDLL( "../build/libxorshift.so" );
+libxorshift.initialize_states.restype = c_bool;
+libxorshift.get_uniform_uint.restype = c_uint;
+libxorshift.get_uniform_uint.argtypes = [c_uint, c_uint];
+
+if ( libxorshift.initialize_states() is c_bool(False) ):
+ print( "Error: failed to initialize RNG states" );
+ del libxorshift;
+ sys.exit( -1 );
+
+generated_combinations = [];
+generated_sum = 0;
+
+current_combination = [];
+gen_num = 0;
+
+for c_count in range( 0, args.count ):
+ for bcount in range( 0, args.num_count ):
+ gen_num = int( libxorshift.get_uniform_uint(c_uint(args.min), c_uint(args.max)) );
+ generated_sum += gen_num;
+ current_combination.append( gen_num );
+ generated_combinations.append( current_combination );
+ current_combination = [];
+
+del current_combination, gen_num;
+
+print( "Statistical properties results" );
+print( "-----" );
+
+print( "Each combination's mean value" );
+print( "-----" );
+
+combination_str = None;
+
+for gen_combination in generated_combinations:
+ combination_str = ", ".join( list(map(str, gen_combination)) );
+ print( f"Combination {combination_str} has mean {np.mean(gen_combination)}" );
+
+del combination_str;
+
+print( "-----" );
+print( "Overall statistical properties" );
+print( "-----" );
+
+print( f"Expected mean overall: {(args.max / 2) * args.num_count}" );
+print( f"Actual overall mean: {generated_sum / args.count}" );
+
+print( "-----" );
+
+del generated_sum, generated_combinations;
+
+del libxorshift;
+del args;
+sys.exit( 0 );
diff --git a/xorshift.c b/xorshift.c
new file mode 100644
index 0000000..6fdd5fb
--- /dev/null
+++ b/xorshift.c
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <limits.h>
+#include <sys/random.h>
+#include <string.h>
+#include "structs.h"
+
+struct rng_state global_rng_state;
+
+__attribute__((visibility("default"))) bool initialize_states(){
+ assert( (sizeof(uint32_t) * CHAR_BIT) == 32 );
+ memset( (void*)&global_rng_state, 0, sizeof(struct rng_state) );
+ ssize_t state_bytes_read = 0;
+ if ( (state_bytes_read = getrandom((void*)&global_rng_state.states, 16 * sizeof(uint32_t), GRND_NONBLOCK)) == -1 )
+ return false;
+ if ( (size_t)state_bytes_read < (16 * sizeof(uint32_t)) ){
+ errno = EIO;
+ return false;
+ }
+ global_rng_state.state_idx = global_rng_state.states[7] & 0x0f;
+ errno = 0;
+ return true;
+}
+
+__attribute__((visibility("default"))) uint32_t get_random_output(){
+ size_t use_state = global_rng_state.state_idx;
+ global_rng_state.state_idx = global_rng_state.states[use_state] & 0x0f;
+ if ( global_rng_state.gen_period_ctr >= 0xffffffff ){
+ global_rng_state.period_end_ctr++;
+ if ( global_rng_state.period_end_ctr >= 16 ){
+ assert( initialize_states() == true );
+ global_rng_state.period_end_ctr = 0;
+ }
+ global_rng_state.gen_period_ctr = 0;
+ }
+ global_rng_state.gen_period_ctr++;
+ global_rng_state.states[use_state] ^= global_rng_state.states[use_state] >> 11;
+ global_rng_state.states[use_state] ^= global_rng_state.states[use_state] << 7;
+ global_rng_state.states[use_state] ^= global_rng_state.states[use_state] >> 16;
+ return global_rng_state.states[use_state] * OUTPUT_MULTIPLIER;
+}
+
+__attribute__((visibility("default"))) uint32_t get_uniform_uint( uint32_t min, uint32_t max ){
+ if ( max <= min )
+ return min;
+ uint32_t ncount = (max - min) + 1;
+ uint32_t upper_bound = 0xffffffff - (0xffffffff % ncount);
+ uint32_t gen_output = get_random_output();
+ if ( gen_output > upper_bound ){
+ while ( gen_output > upper_bound )
+ gen_output = get_random_output();
+ }
+ return (gen_output % max) + min;
+}
+
+__attribute__((visibility("default"))) void clear_rng_state(){
+ memset( (void*)&global_rng_state, 0, sizeof(struct rng_state) );
+ return;
+}