diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/Makefile | 44 | ||||
| -rw-r--r-- | source/README.md | 146 | ||||
| -rw-r--r-- | source/pcg_random.c | 94 | ||||
| -rw-r--r-- | source/pcg_random.h | 52 | ||||
| -rw-r--r-- | source/splitmix64.c | 89 | ||||
| -rw-r--r-- | source/splitmix64.h | 44 | ||||
| -rw-r--r-- | source/wy_random.c | 105 | ||||
| -rw-r--r-- | source/wy_random.h | 44 | ||||
| -rw-r--r-- | source/xoroshiro_random.c | 108 | ||||
| -rw-r--r-- | source/xoroshiro_random.h | 48 | ||||
| -rw-r--r-- | source/xorshift_random.c | 75 | ||||
| -rw-r--r-- | source/xorshift_random.h | 44 |
12 files changed, 838 insertions, 55 deletions
diff --git a/source/Makefile b/source/Makefile index 5888d63..cefad43 100644 --- a/source/Makefile +++ b/source/Makefile @@ -1,22 +1,46 @@ -# Makefile for PhotonSpinRandom C implementation +# Makefile for Random Number Generator C implementations CC = cc CFLAGS = -O2 -Wall -Wextra -std=c99 LDFLAGS = -lm -TARGET = photon_spin_random -SRC = photon_spin_random.c -HEADER = photon_spin_random.h +TARGETS = photon_spin_random xorshift_random splitmix64 wy_random pcg_random xoroshiro_random .PHONY: all clean test -all: $(TARGET) +all: $(TARGETS) -$(TARGET): $(SRC) $(HEADER) - $(CC) $(CFLAGS) -DPHOTON_SPIN_TEST_MAIN -o $(TARGET) $(SRC) $(LDFLAGS) +photon_spin_random: photon_spin_random.c photon_spin_random.h + $(CC) $(CFLAGS) -DPHOTON_SPIN_TEST_MAIN -o $@ photon_spin_random.c $(LDFLAGS) -test: $(TARGET) - ./$(TARGET) +xorshift_random: xorshift_random.c xorshift_random.h + $(CC) $(CFLAGS) -DXORSHIFT_TEST_MAIN -o $@ xorshift_random.c $(LDFLAGS) + +splitmix64: splitmix64.c splitmix64.h + $(CC) $(CFLAGS) -DSPLITMIX64_TEST_MAIN -o $@ splitmix64.c $(LDFLAGS) + +wy_random: wy_random.c wy_random.h + $(CC) $(CFLAGS) -DWY_TEST_MAIN -o $@ wy_random.c $(LDFLAGS) + +pcg_random: pcg_random.c pcg_random.h + $(CC) $(CFLAGS) -DPCG_TEST_MAIN -o $@ pcg_random.c $(LDFLAGS) + +xoroshiro_random: xoroshiro_random.c xoroshiro_random.h + $(CC) $(CFLAGS) -DXOROSHIRO_TEST_MAIN -o $@ xoroshiro_random.c $(LDFLAGS) + +test: $(TARGETS) + @echo "=== Testing PhotonSpinRandom ===" + ./photon_spin_random + @echo "\n=== Testing XorShiftRandom ===" + ./xorshift_random + @echo "\n=== Testing SplitMix64 ===" + ./splitmix64 + @echo "\n=== Testing WyRandom ===" + ./wy_random + @echo "\n=== Testing PCG Random ===" + ./pcg_random + @echo "\n=== Testing XoroShiro Random ===" + ./xoroshiro_random clean: - rm -f $(TARGET) + rm -f $(TARGETS) diff --git a/source/README.md b/source/README.md index c1f8d7f..af72351 100644 --- a/source/README.md +++ b/source/README.md @@ -1,95 +1,151 @@ -# PhotonSpinRandom C Implementation +# Random Number Generator C Implementations -This directory contains a C implementation of the PhotonSpinRandom generator, originally written in C# by Will Stafford Parsons and distributed in the [unity-helpers](https://github.com/wallstop/unity-helpers) repository. +This directory contains C implementations of various random number generators, originally written in C# by Will Stafford Parsons and distributed in the [unity-helpers](https://github.com/wallstop/unity-helpers) repository. ## Files -- **photon_spin_random.h** - Header file with type definitions and function declarations -- **photon_spin_random.c** - Implementation of the PhotonSpin32 random number generator -- **Makefile** - Build configuration for easy compilation -- **donut.c** - ASCII art donut (not a source file, QA artifact) +### PhotonSpinRandom +- **photon_spin_random.h** / **photon_spin_random.c** - 20-word ring-buffer RNG inspired by SHISHUA +- Large state, excellent distribution, huge period (~2^512) +- Best for: Heavy simulation workloads requiring large streams -## About PhotonSpinRandom +### XorShift +- **xorshift_random.h** / **xorshift_random.c** - Classic, extremely fast PRNG +- Tiny state (32-bit), very fast, modest quality +- Best for: Effects, particles, jitter, lightweight randomness -PhotonSpin32 is a 20-word ring-buffer random number generator inspired by SHISHUA, designed for high throughput and large period (~2^512). It excels at: +### SplitMix64 +- **splitmix64.h** / **splitmix64.c** - Fast 64-bit seeding/mixing generator +- Very fast, great for seed generation and hashing +- Best for: Seeding other PRNGs, quick mixing, gameplay randomness -- Generating large streams of high-quality random numbers -- Heavy simulation workloads requiring excellent distribution -- Deterministic state capture and restoration +### WyRandom +- **wy_random.h** / **wy_random.c** - Wyhash-inspired multiply-mix PRNG +- Fast, good distribution, multiply-based mixing +- Best for: General gameplay RNG, weight selection, shuffles -**Not suitable for**: Cryptographic or security-sensitive applications. +### PCG (Permuted Congruential Generator) +- **pcg_random.h** / **pcg_random.c** - High-quality, small-state PRNG +- Excellent statistical quality, passes TestU01 BigCrush and PractRand +- Best for: General gameplay, procedural content, Monte Carlo sampling + +### XoroShiro +- **xoroshiro_random.h** / **xoroshiro_random.c** - Fast 128-bit state Xoroshiro PRNG +- Very fast, good quality, long period (~2^128−1) +- Best for: General-purpose randomness, procedural generation + +### Build System +- **Makefile** - Build configuration for all generators +- **donut.c** - ASCII art donut (QA artifact, not a source file) ## Building ### Using Make ```bash -make # Build the test program -make test # Build and run tests +make # Build all test programs +make test # Build and run all tests make clean # Clean build artifacts ``` ### Using @BAKE -The source file includes a `@BAKE` statement for use with the [bake](https://github.com/8e8m/bake) build tool: +Each source file includes a `@BAKE` statement for use with the [bake](https://github.com/8e8m/bake) build tool. Example from photon_spin_random.c: ```bash -# The @BAKE command is embedded in photon_spin_random.c: # @BAKE cc -O2 -Wall -Wextra -std=c99 -o photon_spin_random photon_spin_random.c -lm @STOP ``` -To use it with bake, simply run bake in this directory. - ### Manual Compilation ```bash # For testing with built-in test harness: cc -O2 -Wall -Wextra -std=c99 -DPHOTON_SPIN_TEST_MAIN -o photon_spin_random photon_spin_random.c -lm +cc -O2 -Wall -Wextra -std=c99 -DXORSHIFT_TEST_MAIN -o xorshift_random xorshift_random.c -lm +cc -O2 -Wall -Wextra -std=c99 -DSPLITMIX64_TEST_MAIN -o splitmix64 splitmix64.c -lm +cc -O2 -Wall -Wextra -std=c99 -DWY_TEST_MAIN -o wy_random wy_random.c -lm +cc -O2 -Wall -Wextra -std=c99 -DPCG_TEST_MAIN -o pcg_random pcg_random.c -lm +cc -O2 -Wall -Wextra -std=c99 -DXOROSHIRO_TEST_MAIN -o xoroshiro_random xoroshiro_random.c -lm -# As a library (without test main): +# As libraries (without test main): cc -O2 -Wall -Wextra -std=c99 -c photon_spin_random.c +# ... etc for other generators ``` -## Usage Example +## Usage Examples +### PhotonSpinRandom ```c #include "photon_spin_random.h" -int main(void) { - photon_spin_random_t rng; - - // Initialize with a seed - photon_spin_init(&rng, 42); - - // Generate random numbers - uint32_t rand_uint = photon_spin_next_uint(&rng); - float rand_float = photon_spin_next_float(&rng); // [0, 1) - double rand_double = photon_spin_next_double(&rng); // [0, 1) - int32_t rand_range = photon_spin_next_int_range(&rng, 0, 100); // [0, 100) - - return 0; -} +photon_spin_random_t rng; +photon_spin_init(&rng, 42); +uint32_t rand_uint = photon_spin_next_uint(&rng); +float rand_float = photon_spin_next_float(&rng); // [0, 1) ``` -## API Reference +### XorShift +```c +#include "xorshift_random.h" + +xorshift_random_t rng; +xorshift_init(&rng, 42); +uint32_t value = xorshift_next_uint(&rng); +``` -### Initialization Functions +### SplitMix64 +```c +#include "splitmix64.h" + +splitmix64_t rng; +splitmix64_init(&rng, 42); +uint64_t value = splitmix64_next_ulong(&rng); +``` + +### WyRandom +```c +#include "wy_random.h" + +wy_random_t rng; +wy_init(&rng, 42); +uint32_t value = wy_next_uint(&rng); +``` + +### PCG +```c +#include "pcg_random.h" + +pcg_random_t rng; +pcg_init(&rng, 42); +uint32_t value = pcg_next_uint(&rng); +``` + +### XoroShiro +```c +#include "xoroshiro_random.h" + +xoroshiro_random_t rng; +xoroshiro_init(&rng, 42, 12345); +uint64_t value = xoroshiro_next_ulong(&rng); +``` -- `photon_spin_init(rng, seed)` - Initialize with a single 32-bit seed -- `photon_spin_init_scalars(rng, seed_a, seed_b, seed_c)` - Initialize with three 32-bit seeds -- `photon_spin_init_ulongs(rng, seed0, seed1)` - Initialize with two 64-bit seeds +## Choosing a Generator -### Generation Functions +| Generator | Speed | Quality | State Size | Use Case | +|-----------|-------|---------|------------|----------| +| XorShift | Fastest | Fair | 4 bytes | Particles, effects, simple randomness | +| SplitMix64 | Very Fast | Very Good | 8 bytes | Seeding, mixing, quick generation | +| WyRandom | Very Fast | Very Good | 8 bytes | General gameplay, hashing-like uses | +| PCG | Fast | Excellent | 16 bytes | High-quality gameplay, simulations | +| XoroShiro | Very Fast | Very Good | 16 bytes | General-purpose, procedural gen | +| PhotonSpin | Fast | Excellent | 80 bytes | Large streams, heavy simulations | -- `photon_spin_next_uint(rng)` - Generate next 32-bit unsigned integer -- `photon_spin_next_float(rng)` - Generate next float in [0, 1) -- `photon_spin_next_double(rng)` - Generate next double in [0, 1) -- `photon_spin_next_int_range(rng, min, max)` - Generate integer in [min, max) +**Note**: None of these generators are cryptographically secure. Do not use for security-sensitive applications. ## License MIT License - Copyright (c) 2025 wallstop -Original C# implementation from: https://github.com/wallstop/unity-helpers +Original C# implementations from: https://github.com/wallstop/unity-helpers Converted to C for the librandom project. diff --git a/source/pcg_random.c b/source/pcg_random.c new file mode 100644 index 0000000..4d01b49 --- /dev/null +++ b/source/pcg_random.c @@ -0,0 +1,94 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + * + * @BAKE cc -O2 -Wall -Wextra -std=c99 -o pcg_random pcg_random.c -lm @STOP + */ + +#include "pcg_random.h" + +static inline uint64_t normalize_increment(uint64_t increment) { + return (increment & 1ULL) == 0 ? increment | 1ULL : increment; +} + +void pcg_init(pcg_random_t *rng, uint64_t seed) { + rng->increment = normalize_increment(6554638469ULL); + rng->state = seed; + /* Warm up the state */ + pcg_next_uint(rng); + rng->increment = normalize_increment((uint64_t)pcg_next_uint(rng) | ((uint64_t)pcg_next_uint(rng) << 32)); +} + +void pcg_init_full(pcg_random_t *rng, uint64_t state, uint64_t increment) { + rng->state = state; + rng->increment = normalize_increment(increment); +} + +uint32_t pcg_next_uint(pcg_random_t *rng) { + uint64_t old_state = rng->state; + rng->state = old_state * 6364136223846793005ULL + rng->increment; + uint32_t xor_shifted = (uint32_t)(((old_state >> 18) ^ old_state) >> 27); + int rot = (int)(old_state >> 59); + return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31)); +} + +uint64_t pcg_next_ulong(pcg_random_t *rng) { + uint64_t high = (uint64_t)pcg_next_uint(rng) << 32; + uint64_t low = (uint64_t)pcg_next_uint(rng); + return high | low; +} + +float pcg_next_float(pcg_random_t *rng) { + uint32_t value = pcg_next_uint(rng); + return (value >> 8) * 0x1.0p-24f; +} + +double pcg_next_double(pcg_random_t *rng) { + uint64_t value = pcg_next_ulong(rng); + return (value >> 11) * 0x1.0p-53; +} + +#ifdef PCG_TEST_MAIN +#include <stdio.h> +#include <time.h> + +int main(void) { + pcg_random_t rng; + + printf("PCG Random C Implementation Test\n"); + printf("=================================\n\n"); + + pcg_init(&rng, 42); + + printf("Testing with seed 42:\n"); + printf("First 10 uint32_t values:\n"); + for (int i = 0; i < 10; i++) { + printf(" %u\n", pcg_next_uint(&rng)); + } + + printf("\nFirst 10 uint64_t values:\n"); + pcg_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %llu\n", (unsigned long long)pcg_next_ulong(&rng)); + } + + printf("\nFirst 10 float values:\n"); + pcg_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %.6f\n", pcg_next_float(&rng)); + } + + printf("\nPerformance test: generating 10 million uint32_t values...\n"); + pcg_init(&rng, (uint64_t)time(NULL)); + clock_t start = clock(); + uint64_t sum = 0; + for (int i = 0; i < 10000000; i++) { + sum += pcg_next_uint(&rng); + } + clock_t end = clock(); + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + printf("Time: %.3f seconds (sum: %llu to prevent optimization)\n", elapsed, (unsigned long long)sum); + + return 0; +} +#endif diff --git a/source/pcg_random.h b/source/pcg_random.h new file mode 100644 index 0000000..ae4886a --- /dev/null +++ b/source/pcg_random.h @@ -0,0 +1,52 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + */ + +#ifndef PCG_RANDOM_H +#define PCG_RANDOM_H + +#include <stdint.h> +#include <stdbool.h> + +/* PCG (Permuted Congruential Generator): High-quality, small-state PRNG. + * + * PCG offers excellent statistical quality with very small state and extremely + * fast generation. This implementation uses a 64-bit state with 32-bit outputs + * and an increment (stream selector). + * + * Pros: + * - Fast and allocation-free; suitable for gameplay hot paths + * - Great statistical quality; passes TestU01 BigCrush and PractRand + * - Deterministic and reproducible across platforms + * - Small state footprint + * + * Cons: + * - Not cryptographically secure + * - 32-bit outputs (use multiple calls for 64-bit) + */ + +typedef struct { + uint64_t state; + uint64_t increment; +} pcg_random_t; + +/* Initialize the generator with a seed */ +void pcg_init(pcg_random_t *rng, uint64_t seed); + +/* Initialize with explicit state and increment */ +void pcg_init_full(pcg_random_t *rng, uint64_t state, uint64_t increment); + +/* Generate next random 32-bit unsigned integer */ +uint32_t pcg_next_uint(pcg_random_t *rng); + +/* Generate next random 64-bit unsigned integer */ +uint64_t pcg_next_ulong(pcg_random_t *rng); + +/* Generate next random float in [0, 1) */ +float pcg_next_float(pcg_random_t *rng); + +/* Generate next random double in [0, 1) */ +double pcg_next_double(pcg_random_t *rng); + +#endif /* PCG_RANDOM_H */ diff --git a/source/splitmix64.c b/source/splitmix64.c new file mode 100644 index 0000000..c840258 --- /dev/null +++ b/source/splitmix64.c @@ -0,0 +1,89 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + * + * @BAKE cc -O2 -Wall -Wextra -std=c99 -o splitmix64 splitmix64.c -lm @STOP + */ + +#include "splitmix64.h" + +void splitmix64_init(splitmix64_t *rng, uint64_t seed) { + rng->state = seed; +} + +uint32_t splitmix64_next_uint(splitmix64_t *rng) { + rng->state += 0x9E3779B97F4A7C15ULL; + + uint64_t z = rng->state; + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; + z ^= z >> 31; + + return (uint32_t)z; +} + +uint64_t splitmix64_next_ulong(splitmix64_t *rng) { + rng->state += 0x9E3779B97F4A7C15ULL; + + uint64_t z = rng->state; + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL; + z ^= z >> 31; + + return z; +} + +float splitmix64_next_float(splitmix64_t *rng) { + uint32_t value = splitmix64_next_uint(rng); + return (value >> 8) * 0x1.0p-24f; +} + +double splitmix64_next_double(splitmix64_t *rng) { + uint64_t value = splitmix64_next_ulong(rng); + return (value >> 11) * 0x1.0p-53; +} + +#ifdef SPLITMIX64_TEST_MAIN +#include <stdio.h> +#include <time.h> + +int main(void) { + splitmix64_t rng; + + printf("SplitMix64 C Implementation Test\n"); + printf("=================================\n\n"); + + splitmix64_init(&rng, 42); + + printf("Testing with seed 42:\n"); + printf("First 10 uint32_t values:\n"); + for (int i = 0; i < 10; i++) { + printf(" %u\n", splitmix64_next_uint(&rng)); + } + + printf("\nFirst 10 uint64_t values:\n"); + splitmix64_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %llu\n", (unsigned long long)splitmix64_next_ulong(&rng)); + } + + printf("\nFirst 10 float values:\n"); + splitmix64_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %.6f\n", splitmix64_next_float(&rng)); + } + + printf("\nPerformance test: generating 10 million uint64_t values...\n"); + splitmix64_init(&rng, (uint64_t)time(NULL)); + clock_t start = clock(); + uint64_t sum = 0; + for (int i = 0; i < 10000000; i++) { + sum += splitmix64_next_ulong(&rng); + } + clock_t end = clock(); + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + printf("Time: %.3f seconds (sum: %llu to prevent optimization)\n", elapsed, (unsigned long long)sum); + + return 0; +} +#endif diff --git a/source/splitmix64.h b/source/splitmix64.h new file mode 100644 index 0000000..27d7eda --- /dev/null +++ b/source/splitmix64.h @@ -0,0 +1,44 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + */ + +#ifndef SPLITMIX64_H +#define SPLITMIX64_H + +#include <stdint.h> +#include <stdbool.h> + +/* SplitMix64: A fast 64-bit generator often used as a high-quality seeding/mixing PRNG. + * + * SplitMix64 is widely used to quickly generate well-distributed 64-bit values + * and as a seed source for other generators. + * + * Pros: + * - Very fast; great as a hash/mixer and for seed generation + * - Deterministic, portable, and simple + * + * Cons: + * - Not cryptographically secure + */ + +typedef struct { + uint64_t state; +} splitmix64_t; + +/* Initialize the generator with a seed */ +void splitmix64_init(splitmix64_t *rng, uint64_t seed); + +/* Generate next random 32-bit unsigned integer */ +uint32_t splitmix64_next_uint(splitmix64_t *rng); + +/* Generate next random 64-bit unsigned integer */ +uint64_t splitmix64_next_ulong(splitmix64_t *rng); + +/* Generate next random float in [0, 1) */ +float splitmix64_next_float(splitmix64_t *rng); + +/* Generate next random double in [0, 1) */ +double splitmix64_next_double(splitmix64_t *rng); + +#endif /* SPLITMIX64_H */ diff --git a/source/wy_random.c b/source/wy_random.c new file mode 100644 index 0000000..dcec829 --- /dev/null +++ b/source/wy_random.c @@ -0,0 +1,105 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + * + * @BAKE cc -O2 -Wall -Wextra -std=c99 -o wy_random wy_random.c -lm @STOP + */ + +#include "wy_random.h" + +#define WY_PRIME0 0xa0761d6478bd642fULL +#define WY_PRIME1 0xe7037ed1a0b428dbULL + +/* Multiply two 64-bit integers and return hi and lo parts of 128-bit result */ +static inline void multiply_64(uint64_t x, uint64_t y, uint64_t *hi, uint64_t *lo) { + uint64_t x0 = (uint32_t)x; + uint64_t x1 = x >> 32; + uint64_t y0 = (uint32_t)y; + uint64_t y1 = y >> 32; + + uint64_t p11 = x1 * y1; + uint64_t p01 = x0 * y1; + uint64_t p10 = x1 * y0; + uint64_t p00 = x0 * y0; + + uint64_t middle = p10 + (p00 >> 32) + (uint32_t)p01; + + *lo = (middle << 32) | (uint32_t)p00; + *hi = p11 + (middle >> 32) + (p01 >> 32); +} + +/* MUM (MUltiply and Mix) operation */ +static inline uint64_t mum(uint64_t x, uint64_t y) { + uint64_t hi, lo; + multiply_64(x, y, &hi, &lo); + return hi ^ lo; +} + +void wy_init(wy_random_t *rng, uint64_t seed) { + rng->state = seed; +} + +uint32_t wy_next_uint(wy_random_t *rng) { + rng->state += WY_PRIME0; + return (uint32_t)mum(rng->state ^ WY_PRIME1, rng->state); +} + +uint64_t wy_next_ulong(wy_random_t *rng) { + rng->state += WY_PRIME0; + return mum(rng->state ^ WY_PRIME1, rng->state); +} + +float wy_next_float(wy_random_t *rng) { + uint32_t value = wy_next_uint(rng); + return (value >> 8) * 0x1.0p-24f; +} + +double wy_next_double(wy_random_t *rng) { + uint64_t value = wy_next_ulong(rng); + return (value >> 11) * 0x1.0p-53; +} + +#ifdef WY_TEST_MAIN +#include <stdio.h> +#include <time.h> + +int main(void) { + wy_random_t rng; + + printf("WyRandom C Implementation Test\n"); + printf("===============================\n\n"); + + wy_init(&rng, 42); + + printf("Testing with seed 42:\n"); + printf("First 10 uint32_t values:\n"); + for (int i = 0; i < 10; i++) { + printf(" %u\n", wy_next_uint(&rng)); + } + + printf("\nFirst 10 uint64_t values:\n"); + wy_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %llu\n", (unsigned long long)wy_next_ulong(&rng)); + } + + printf("\nFirst 10 float values:\n"); + wy_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %.6f\n", wy_next_float(&rng)); + } + + printf("\nPerformance test: generating 10 million uint64_t values...\n"); + wy_init(&rng, (uint64_t)time(NULL)); + clock_t start = clock(); + uint64_t sum = 0; + for (int i = 0; i < 10000000; i++) { + sum += wy_next_ulong(&rng); + } + clock_t end = clock(); + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + printf("Time: %.3f seconds (sum: %llu to prevent optimization)\n", elapsed, (unsigned long long)sum); + + return 0; +} +#endif diff --git a/source/wy_random.h b/source/wy_random.h new file mode 100644 index 0000000..80afb43 --- /dev/null +++ b/source/wy_random.h @@ -0,0 +1,44 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + */ + +#ifndef WY_RANDOM_H +#define WY_RANDOM_H + +#include <stdint.h> +#include <stdbool.h> + +/* WyRandom: A wyhash-inspired PRNG variant leveraging multiply-mix operations. + * + * Designed around 64-bit multiply-and-mix steps, this generator is fast and + * suitable for general-purpose randomness and hashing-like use cases. + * + * Pros: + * - Fast and simple; good distribution for typical gameplay uses + * - Deterministic across platforms + * + * Cons: + * - Not cryptographically secure + */ + +typedef struct { + uint64_t state; +} wy_random_t; + +/* Initialize the generator with a seed */ +void wy_init(wy_random_t *rng, uint64_t seed); + +/* Generate next random 32-bit unsigned integer */ +uint32_t wy_next_uint(wy_random_t *rng); + +/* Generate next random 64-bit unsigned integer */ +uint64_t wy_next_ulong(wy_random_t *rng); + +/* Generate next random float in [0, 1) */ +float wy_next_float(wy_random_t *rng); + +/* Generate next random double in [0, 1) */ +double wy_next_double(wy_random_t *rng); + +#endif /* WY_RANDOM_H */ diff --git a/source/xoroshiro_random.c b/source/xoroshiro_random.c new file mode 100644 index 0000000..4b5d8d3 --- /dev/null +++ b/source/xoroshiro_random.c @@ -0,0 +1,108 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + * + * @BAKE cc -O2 -Wall -Wextra -std=c99 -o xoroshiro_random xoroshiro_random.c -lm @STOP + */ + +#include "xoroshiro_random.h" + +static inline uint64_t rotl(uint64_t x, int k) { + return (x << k) | (x >> (64 - k)); +} + +static void ensure_non_zero_state(xoroshiro_random_t *rng) { + if ((rng->s0 | rng->s1) == 0) { + rng->s0 = 0x9E3779B97F4A7C15ULL; + rng->s1 = 0xD1B54A32D192ED03ULL; + } +} + +void xoroshiro_init(xoroshiro_random_t *rng, uint64_t seed1, uint64_t seed2) { + rng->s0 = seed1; + rng->s1 = seed2; + ensure_non_zero_state(rng); +} + +uint32_t xoroshiro_next_uint(xoroshiro_random_t *rng) { + ensure_non_zero_state(rng); + + uint64_t s0 = rng->s0; + uint64_t s1 = rng->s1; + uint64_t result = s0 + s1; + + s1 ^= s0; + rng->s0 = rotl(s0, 24) ^ s1 ^ (s1 << 16); + rng->s1 = rotl(s1, 37); + + return (uint32_t)result; +} + +uint64_t xoroshiro_next_ulong(xoroshiro_random_t *rng) { + ensure_non_zero_state(rng); + + uint64_t s0 = rng->s0; + uint64_t s1 = rng->s1; + uint64_t result = s0 + s1; + + s1 ^= s0; + rng->s0 = rotl(s0, 24) ^ s1 ^ (s1 << 16); + rng->s1 = rotl(s1, 37); + + return result; +} + +float xoroshiro_next_float(xoroshiro_random_t *rng) { + uint32_t value = xoroshiro_next_uint(rng); + return (value >> 8) * 0x1.0p-24f; +} + +double xoroshiro_next_double(xoroshiro_random_t *rng) { + uint64_t value = xoroshiro_next_ulong(rng); + return (value >> 11) * 0x1.0p-53; +} + +#ifdef XOROSHIRO_TEST_MAIN +#include <stdio.h> +#include <time.h> + +int main(void) { + xoroshiro_random_t rng; + + printf("XoroShiro Random C Implementation Test\n"); + printf("=======================================\n\n"); + + xoroshiro_init(&rng, 42, 12345); + + printf("Testing with seeds 42, 12345:\n"); + printf("First 10 uint32_t values:\n"); + for (int i = 0; i < 10; i++) { + printf(" %u\n", xoroshiro_next_uint(&rng)); + } + + printf("\nFirst 10 uint64_t values:\n"); + xoroshiro_init(&rng, 42, 12345); + for (int i = 0; i < 10; i++) { + printf(" %llu\n", (unsigned long long)xoroshiro_next_ulong(&rng)); + } + + printf("\nFirst 10 float values:\n"); + xoroshiro_init(&rng, 42, 12345); + for (int i = 0; i < 10; i++) { + printf(" %.6f\n", xoroshiro_next_float(&rng)); + } + + printf("\nPerformance test: generating 10 million uint64_t values...\n"); + xoroshiro_init(&rng, (uint64_t)time(NULL), (uint64_t)time(NULL) * 2); + clock_t start = clock(); + uint64_t sum = 0; + for (int i = 0; i < 10000000; i++) { + sum += xoroshiro_next_ulong(&rng); + } + clock_t end = clock(); + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + printf("Time: %.3f seconds (sum: %llu to prevent optimization)\n", elapsed, (unsigned long long)sum); + + return 0; +} +#endif diff --git a/source/xoroshiro_random.h b/source/xoroshiro_random.h new file mode 100644 index 0000000..d8b41b6 --- /dev/null +++ b/source/xoroshiro_random.h @@ -0,0 +1,48 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + */ + +#ifndef XOROSHIRO_RANDOM_H +#define XOROSHIRO_RANDOM_H + +#include <stdint.h> +#include <stdbool.h> + +/* XoroShiro: A fast 128-bit state Xoroshiro-based PRNG with good quality. + * + * Xoroshiro family generators offer an excellent balance between speed and + * quality for real-time applications. This implementation maintains two 64-bit + * state variables. + * + * Pros: + * - Very fast; suitable for gameplay and procedural generation + * - Good statistical properties; long period (~2^128−1) + * - Deterministic and reproducible across platforms + * + * Cons: + * - Not cryptographically secure + * - Low bits may show weaker properties in some variants + */ + +typedef struct { + uint64_t s0; + uint64_t s1; +} xoroshiro_random_t; + +/* Initialize the generator with two seeds */ +void xoroshiro_init(xoroshiro_random_t *rng, uint64_t seed1, uint64_t seed2); + +/* Generate next random 32-bit unsigned integer */ +uint32_t xoroshiro_next_uint(xoroshiro_random_t *rng); + +/* Generate next random 64-bit unsigned integer */ +uint64_t xoroshiro_next_ulong(xoroshiro_random_t *rng); + +/* Generate next random float in [0, 1) */ +float xoroshiro_next_float(xoroshiro_random_t *rng); + +/* Generate next random double in [0, 1) */ +double xoroshiro_next_double(xoroshiro_random_t *rng); + +#endif /* XOROSHIRO_RANDOM_H */ diff --git a/source/xorshift_random.c b/source/xorshift_random.c new file mode 100644 index 0000000..daeecfd --- /dev/null +++ b/source/xorshift_random.c @@ -0,0 +1,75 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + * + * @BAKE cc -O2 -Wall -Wextra -std=c99 -o xorshift_random xorshift_random.c -lm @STOP + */ + +#include "xorshift_random.h" + +static uint32_t normalize_state(uint32_t state) { + return state != 0 ? state : XORSHIFT_DEFAULT_STATE; +} + +void xorshift_init(xorshift_random_t *rng, uint32_t seed) { + rng->state = normalize_state(seed); +} + +uint32_t xorshift_next_uint(xorshift_random_t *rng) { + rng->state = normalize_state(rng->state); + rng->state ^= rng->state << 13; + rng->state ^= rng->state >> 17; + rng->state ^= rng->state << 5; + return rng->state; +} + +float xorshift_next_float(xorshift_random_t *rng) { + uint32_t value = xorshift_next_uint(rng); + return (value >> 8) * 0x1.0p-24f; +} + +double xorshift_next_double(xorshift_random_t *rng) { + uint32_t high = xorshift_next_uint(rng); + uint32_t low = xorshift_next_uint(rng); + uint64_t combined = ((uint64_t)high << 32) | low; + return (combined >> 11) * 0x1.0p-53; +} + +#ifdef XORSHIFT_TEST_MAIN +#include <stdio.h> +#include <time.h> + +int main(void) { + xorshift_random_t rng; + + printf("XorShift Random C Implementation Test\n"); + printf("=====================================\n\n"); + + xorshift_init(&rng, 42); + + printf("Testing with seed 42:\n"); + printf("First 10 uint32_t values:\n"); + for (int i = 0; i < 10; i++) { + printf(" %u\n", xorshift_next_uint(&rng)); + } + + printf("\nFirst 10 float values:\n"); + xorshift_init(&rng, 42); + for (int i = 0; i < 10; i++) { + printf(" %.6f\n", xorshift_next_float(&rng)); + } + + printf("\nPerformance test: generating 10 million uint32_t values...\n"); + xorshift_init(&rng, (uint32_t)time(NULL)); + clock_t start = clock(); + uint64_t sum = 0; + for (int i = 0; i < 10000000; i++) { + sum += xorshift_next_uint(&rng); + } + clock_t end = clock(); + double elapsed = (double)(end - start) / CLOCKS_PER_SEC; + printf("Time: %.3f seconds (sum: %llu to prevent optimization)\n", elapsed, (unsigned long long)sum); + + return 0; +} +#endif diff --git a/source/xorshift_random.h b/source/xorshift_random.h new file mode 100644 index 0000000..40e5724 --- /dev/null +++ b/source/xorshift_random.h @@ -0,0 +1,44 @@ +/* MIT License - Copyright (c) 2025 wallstop + * Original C# implementation from: https://github.com/wallstop/unity-helpers + * Converted to C for librandom project + */ + +#ifndef XORSHIFT_RANDOM_H +#define XORSHIFT_RANDOM_H + +#include <stdint.h> +#include <stdbool.h> + +/* XorShift: A classic, extremely fast PRNG with small state and modest quality. + * + * XorShift generators are known for their simplicity and speed. This variant + * operates on a 32-bit state and produces 32-bit outputs. + * + * Pros: + * - Very fast; tiny state footprint + * - Deterministic and easy to serialize/restore + * + * Cons: + * - Lower statistical quality than newer generators + * - Not cryptographically secure + */ + +#define XORSHIFT_DEFAULT_STATE 2463534242U + +typedef struct { + uint32_t state; +} xorshift_random_t; + +/* Initialize the generator with a seed */ +void xorshift_init(xorshift_random_t *rng, uint32_t seed); + +/* Generate next random 32-bit unsigned integer */ +uint32_t xorshift_next_uint(xorshift_random_t *rng); + +/* Generate next random float in [0, 1) */ +float xorshift_next_float(xorshift_random_t *rng); + +/* Generate next random double in [0, 1) */ +double xorshift_next_double(xorshift_random_t *rng); + +#endif /* XORSHIFT_RANDOM_H */ |
