From f54963e1364568d8f4f6e1a5be1646d4c5e7a94e Mon Sep 17 00:00:00 2001 From: stderr64 Date: Thu, 24 Sep 2026 20:49:27 +0300 Subject: First commit --- Makefile | 13 +++++++ include/xorshift.h | 4 ++ structs.h | 6 +++ tests/test_statistical_properties.py | 73 ++++++++++++++++++++++++++++++++++++ xorshift.c | 64 +++++++++++++++++++++++++++++++ 5 files changed, 160 insertions(+) create mode 100644 Makefile create mode 100644 include/xorshift.h create mode 100644 structs.h create mode 100755 tests/test_statistical_properties.py create mode 100644 xorshift.c 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} -- cgit v1.3.1-10-gc9f91