From 076d8bae83cbb762f3c12b77fe95092d3c775418 Mon Sep 17 00:00:00 2001 From: Connor Olding Date: Sun, 2 Apr 2017 01:58:33 +0000 Subject: [PATCH] use a better, external RNG library --- README.md | 8 +- resynth.c | 21 +- rnd.h | 574 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 596 insertions(+), 7 deletions(-) create mode 100644 rnd.h diff --git a/README.md b/README.md index 68a7862..5d9b1f4 100644 --- a/README.md +++ b/README.md @@ -113,10 +113,16 @@ this is the `-R` flag, and the first few values are visualized here: ## notes -includes header libraries from [nothings (stb)][stb] and [notwa (kyaa).][kyaa] +resynth includes the following header libraries: + +* [stb\_image.h by nothings et al.][stb] (public domain / MIT) +* [stb\_image\_write.h by nothings et al.][stb] (public domain / MIT) +* [kyaa.h by notwa][kyaa] (public domain) +* [rnd.h by Mattias Gustavsson][rnd] (public domain / MIT) [stb]: https://github.com/nothings/stb [kyaa]: https://gist.github.com/notwa/5d287d807ffe11bbb553462c9940445c#file-kyaa-md +[rnd]: https://github.com/mattiasgustavsson/libs compile like ``` diff --git a/resynth.c b/resynth.c index 698ef3a..e213add 100644 --- a/resynth.c +++ b/resynth.c @@ -18,7 +18,7 @@ #include #include #include // for file extension mangling -#include // for time(0) as a random seed (srand) +#include // for time(0) as a random seed // decide which features we want from stb_image. // this should cover the most common formats. @@ -52,6 +52,15 @@ #include "kyaa.h" #include "kyaa_extra.h" +// rand() is neither consistent across platforms +// nor guaranteed to have desirable properties, +// so we use this random number generator instead. +#define RND_U32 uint32_t +#define RND_U64 uint64_t +#define RND_IMPLEMENTATION +#include "rnd.h" +rnd_pcg_t pcg; + // convenience macros. hopefully these names don't interfere // with any defined in the standard library headers on any system. // it's technically not an error to redefine macros anyway. @@ -329,8 +338,7 @@ static void run(Resynth_state *s, Parameters parameters) { // shuffle the data points in-place. for (int i = 0; i < data_area; i++) { - // (we could use a better random function here) - int j = rand() % data_area; + int j = rnd_pcg_range(&pcg, 0, data_area - 1); Coord temp = s->data_points[i]; s->data_points[i] = s->data_points[j]; s->data_points[j] = temp; @@ -407,7 +415,8 @@ static void run(Resynth_state *s, Parameters parameters) { // choosing the first couple pixels, since they have no neighbors. // after that, this step is optional. it can improve subjective quality. for (int j = 0; j < parameters.tries && s->best != 0; j++) { - try_point(s, s->corpus_points[rand() % sb_count(s->corpus_points)], parameters.weighted); + int random = rnd_pcg_range(&pcg, 0, sb_count(s->corpus_points) - 1); + try_point(s, s->corpus_points[random], parameters.weighted); } // finally, copy the best pixel to the output image. @@ -567,8 +576,8 @@ int main(int argc, char *argv[]) { stbi_image_free(image); - if (seed) srand(seed); - else srand(time(0)); + if (seed) rnd_pcg_seed(&pcg, seed); + else rnd_pcg_seed(&pcg, time(0)); run(s, parameters); char *out_fn = manipulate_filename(fn, ".resynth.png"); diff --git a/rnd.h b/rnd.h new file mode 100644 index 0000000..8166989 --- /dev/null +++ b/rnd.h @@ -0,0 +1,574 @@ +/* +------------------------------------------------------------------------------ + Licensing information can be found at the end of the file. +------------------------------------------------------------------------------ + +rnd.h - v1.0 - Pseudo-random number generators for C/C++. + +Do this: + #define RND_IMPLEMENTATION +before you include this file in *one* C/C++ file to create the implementation. + +Additional Contributors + Jonatan Hedborg: unsigned int to normalized float conversion +*/ + +#ifndef rnd_h +#define rnd_h + +#ifndef RND_U32 + #define RND_U32 unsigned int +#endif +#ifndef RND_U64 + #define RND_U64 unsigned long long +#endif + +typedef struct rnd_pcg_t { RND_U64 state[ 2 ]; } rnd_pcg_t; +void rnd_pcg_seed( rnd_pcg_t* pcg, RND_U32 seed ); +RND_U32 rnd_pcg_next( rnd_pcg_t* pcg ); +float rnd_pcg_nextf( rnd_pcg_t* pcg ); +int rnd_pcg_range( rnd_pcg_t* pcg, int min, int max ); + +typedef struct rnd_well_t { RND_U32 state[ 17 ]; } rnd_well_t; +void rnd_well_seed( rnd_well_t* well, RND_U32 seed ); +RND_U32 rnd_well_next( rnd_well_t* well ); +float rnd_well_nextf( rnd_well_t* well ); +int rnd_well_range( rnd_well_t* well, int min, int max ); + +typedef struct rnd_gamerand_t { RND_U32 state[ 2 ]; } rnd_gamerand_t; +void rnd_gamerand_seed( rnd_gamerand_t* gamerand, RND_U32 seed ); +RND_U32 rnd_gamerand_next( rnd_gamerand_t* gamerand ); +float rnd_gamerand_nextf( rnd_gamerand_t* gamerand ); +int rnd_gamerand_range( rnd_gamerand_t* gamerand, int min, int max ); + +typedef struct rnd_xorshift_t { RND_U64 state[ 2 ]; } rnd_xorshift_t; +void rnd_xorshift_seed( rnd_xorshift_t* xorshift, RND_U64 seed ); +RND_U64 rnd_xorshift_next( rnd_xorshift_t* xorshift ); +float rnd_xorshift_nextf( rnd_xorshift_t* xorshift ); +int rnd_xorshift_range( rnd_xorshift_t* xorshift, int min, int max ); + +#endif /* rnd_h */ + +/** + +Example +======= + +A basic example showing how to use the PCG set of random functions. + + #define RND_IMPLEMENTATION + #include "rnd.h" + + #include // for printf + #include // for time + + int main( int argc, char** argv ) + { + (void) argc, argv; + + rnd_pcg_t pcg; + rnd_pcg_seed( &pcg, 0u ); // initialize generator + + // print a handful of random integers + // these will be the same on every run, as we + // seeded the rng with a fixed value + for( int i = 0; i < 5; ++i ) + { + RND_U32 n = rnd_pcg_next( &pcg ); + printf( "%08x, ", n ); + } + printf( "\n" ); + + // reseed with a value which is different on each run + time_t seconds; + time( &seconds ); + rnd_pcg_seed( &pcg, (RND_U32) seconds ); + + // print another handful of random integers + // these will be different on every run + for( int i = 0; i < 5; ++i ) + { + RND_U32 n = rnd_pcg_next( &pcg ); + printf( "%08x, ", n ); + } + printf( "\n" ); + + + // print a handful of random floats + for( int i = 0; i < 5; ++i ) + { + float f = rnd_pcg_nextf( &pcg ); + printf( "%f, ", f ); + } + printf( "\n" ); + + // print random integers in the range 1 to 6 + for( int i = 0; i < 15; ++i ) + { + int r = rnd_pcg_range( &pcg, 1, 6 ); + printf( "%d, ", r ); + } + printf( "\n" ); + + return 0; + } + + +API Documentation +================= + +rnd.h is a single-header library, and does not need any .lib files or other binaries, or any build scripts. To use it, +you just include rnd.h to get the API declarations. To get the definitions, you must include rnd.h from *one* single C +or C++ file, and #define the symbol `RND_IMPLEMENTATION` before you do. + +The library is meant for general-purpose use, such as games and similar apps. It is not meant to be used for +cryptography and similar use cases. + + +Customization +------------- +rnd.h allows for specifying the exact type of 32 and 64 bit unsigned integers to be used in its API. By default, these +default to `unsigned int` and `unsigned long long`, but can be redefined by #defining RND_U32 and RND_U64 respectively +before including rnd.h. This is useful if you, for example, use the types from `` in the rest of your program, +and you want rnd.h to use compatible types. In this case, you would include rnd.h using the following code: + + #define RND_U32 uint32_t + #define RND_U64 uint64_t + #include "rnd.h" + +Note that when customizing the data type, you need to use the same definition in every place where you include rnd.h, +as it affect the declarations as well as the definitions. + + +The generators +-------------- + +The library includes four different generators: PCG, WELL, GameRand and XorShift. They all have different +characteristics, and you might want to use them for different things. GameRand is very fast, but does not give a great +distribution or period length. XorShift is the only one returning a 64-bit value. WELL is an improvement of the often +used Mersenne Twister, and has quite a large internal state. PCG is small, fast and has a small state. If you don't +have any specific reason, you may default to using PCG. + +All generators expose their internal state, so it is possible to save this state and later restore it, to resume the +random sequence from the same point. + + +### PCG - Permuted Congruential Generator + +PCG is a family of simple fast space-efficient statistically good algorithms for random number generation. Unlike many +general-purpose RNGs, they are also hard to predict. + +More information can be found here: http://www.pcg-random.org/ + + +### WELL - Well Equidistributed Long-period Linear + +Random number generation, using the WELL algorithm by F. Panneton, P. L'Ecuyer and M. Matsumoto. +More information in the original paper: http://www.iro.umontreal.ca/~panneton/WELLRNG.html + +This code is originally based on WELL512 C/C++ code written by Chris Lomont (published in Game Programming Gems 7) +and placed in the public domain. http://lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf + + +### GameRand + +Based on the random number generator by Ian C. Bullard: +http://www.redditmirror.cc/cache/websites/mjolnirstudios.com_7yjlc/mjolnirstudios.com/IanBullard/files/79ffbca75a75720f066d491e9ea935a0-10.html + +GameRand is a random number generator based off an "Image of the Day" posted by Stephan Schaem. More information here: +http://www.flipcode.com/archives/07-15-2002.shtml + + +### XorShift + +A random number generator of the type LFSR (linear feedback shift registers). This specific implementation uses the +XorShift+ variation, and returns 64-bit random numbers. + +More information can be found here: https://en.wikipedia.org/wiki/Xorshift + + + +rnd_pcg_seed +------------ + + void rnd_pcg_seed( rnd_pcg_t* pcg, RND_U32 seed ) + +Initialize a PCG generator with the specified seed. The generator is not valid until it's been seeded. + + +rnd_pcg_next +------------ + + RND_U32 rnd_pcg_next( rnd_pcg_t* pcg ) + +Returns a random number N in the range: 0 <= N <= 0xffffffff, from the specified PCG generator. + + +rnd_pcg_nextf +------------- + + float rnd_pcg_nextf( rnd_pcg_t* pcg ) + +Returns a random float X in the range: 0.0f <= X < 1.0f, from the specified PCG generator. + + +rnd_pcg_range +------------- + + int rnd_pcg_range( rnd_pcg_t* pcg, int min, int max ) + +Returns a random integer N in the range: min <= N <= max, from the specified PCG generator. + + +rnd_well_seed +------------- + + void rnd_well_seed( rnd_well_t* well, RND_U32 seed ) + +Initialize a WELL generator with the specified seed. The generator is not valid until it's been seeded. + + +rnd_well_next +------------- + + RND_U32 rnd_well_next( rnd_well_t* well ) + +Returns a random number N in the range: 0 <= N <= 0xffffffff, from the specified WELL generator. + + +rnd_well_nextf +-------------- + float rnd_well_nextf( rnd_well_t* well ) + +Returns a random float X in the range: 0.0f <= X < 1.0f, from the specified WELL generator. + + +rnd_well_range +-------------- + + int rnd_well_range( rnd_well_t* well, int min, int max ) + +Returns a random integer N in the range: min <= N <= max, from the specified WELL generator. + + +rnd_gamerand_seed +----------------- + + void rnd_gamerand_seed( rnd_gamerand_t* gamerand, RND_U32 seed ) + +Initialize a GameRand generator with the specified seed. The generator is not valid until it's been seeded. + + +rnd_gamerand_next +----------------- + + RND_U32 rnd_gamerand_next( rnd_gamerand_t* gamerand ) + +Returns a random number N in the range: 0 <= N <= 0xffffffff, from the specified GameRand generator. + + +rnd_gamerand_nextf +------------------ + + float rnd_gamerand_nextf( rnd_gamerand_t* gamerand ) + +Returns a random float X in the range: 0.0f <= X < 1.0f, from the specified GameRand generator. + + +rnd_gamerand_range +------------------ + + int rnd_gamerand_range( rnd_gamerand_t* gamerand, int min, int max ) + +Returns a random integer N in the range: min <= N <= max, from the specified GameRand generator. + + +rnd_xorshift_seed +----------------- + + void rnd_xorshift_seed( rnd_xorshift_t* xorshift, RND_U64 seed ) + +Initialize a XorShift generator with the specified seed. The generator is not valid until it's been seeded. + + +rnd_xorshift_next +----------------- + + RND_U64 rnd_xorshift_next( rnd_xorshift_t* xorshift ) + +Returns a random number N in the range: 0 <= N <= 0xffffffffffffffff, from the specified XorShift generator. + + +rnd_xorshift_nextf +------------------ + + float rnd_xorshift_nextf( rnd_xorshift_t* xorshift ) + +Returns a random float X in the range: 0.0f <= X < 1.0f, from the specified XorShift generator. + + +rnd_xorshift_range +------------------ + + int rnd_xorshift_range( rnd_xorshift_t* xorshift, int min, int max ) + +Returns a random integer N in the range: min <= N <= max, from the specified XorShift generator. + + +**/ + + +/* +---------------------- + IMPLEMENTATION +---------------------- +*/ + +#ifdef RND_IMPLEMENTATION +#undef RND_IMPLEMENTATION + +// Convert a randomized RND_U32 value to a float value x in the range 0.0f <= x < 1.0f. Contributed by Jonatan Hedborg +static float rnd_internal_float_normalized_from_u32( RND_U32 value ) + { + RND_U32 exponent = 127; + RND_U32 mantissa = value >> 9; + RND_U32 result = ( exponent << 23 ) | mantissa; + float fresult = *(float*)( &result ); + return fresult - 1.0f; + } + + +static RND_U32 rnd_internal_murmur3_avalanche32( RND_U32 h ) + { + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + return h; + } + + +static RND_U64 rnd_internal_murmur3_avalanche64( RND_U64 h ) + { + h ^= h >> 33; + h *= 0xff51afd7ed558ccd; + h ^= h >> 33; + h *= 0xc4ceb9fe1a85ec53; + h ^= h >> 33; + return h; + } + + +void rnd_pcg_seed( rnd_pcg_t* pcg, RND_U32 seed ) + { + RND_U64 value = ( ( (RND_U64) seed ) << 1ULL ) | 1ULL; + value = rnd_internal_murmur3_avalanche64( value ); + pcg->state[ 0 ] = 0U; + pcg->state[ 1 ] = ( value << 1ULL ) | 1ULL; + rnd_pcg_next( pcg ); + pcg->state[ 0 ] += rnd_internal_murmur3_avalanche64( value ); + rnd_pcg_next( pcg ); + } + + +RND_U32 rnd_pcg_next( rnd_pcg_t* pcg ) + { + RND_U64 oldstate = pcg->state[ 0 ]; + pcg->state[ 0 ] = oldstate * 0x5851f42d4c957f2dULL + pcg->state[ 1 ]; + RND_U32 xorshifted = (RND_U32)( ( ( oldstate >> 18ULL) ^ oldstate ) >> 27ULL ); + RND_U32 rot = (RND_U32)( oldstate >> 59ULL ); + return ( xorshifted >> rot ) | ( xorshifted << ( ( -(int) rot ) & 31 ) ); + } + + +float rnd_pcg_nextf( rnd_pcg_t* pcg ) + { + return rnd_internal_float_normalized_from_u32( rnd_pcg_next( pcg ) ); + } + + +int rnd_pcg_range( rnd_pcg_t* pcg, int min, int max ) + { + int const range = ( max - min ) + 1; + if( range <= 0 ) return min; + int const value = (int) ( rnd_pcg_nextf( pcg ) * range ); + return min + value; + } + + +void rnd_well_seed( rnd_well_t* well, RND_U32 seed ) + { + RND_U32 value = rnd_internal_murmur3_avalanche32( ( seed << 1U ) | 1U ); + well->state[ 16 ] = 0; + well->state[ 0 ] = value ^ 0xf68a9fc1U; + for( int i = 1; i < 16; ++i ) + well->state[ i ] = ( 0x6c078965U * ( well->state[ i - 1 ] ^ ( well->state[ i - 1 ] >> 30 ) ) + i ); + } + + +RND_U32 rnd_well_next( rnd_well_t* well ) + { + RND_U32 a = well->state[ well->state[ 16 ] ]; + RND_U32 c = well->state[ ( well->state[ 16 ] + 13 ) & 15 ]; + RND_U32 b = a ^ c ^ ( a << 16 ) ^ ( c << 15 ); + c = well->state[ ( well->state[ 16 ] + 9 ) & 15 ]; + c ^= ( c >> 11 ); + a = well->state[ well->state[ 16 ] ] = b ^ c; + RND_U32 d = a ^ ( ( a << 5 ) & 0xda442d24U ); + well->state[ 16 ] = (well->state[ 16 ] + 15 ) & 15; + a = well->state[ well->state[ 16 ] ]; + well->state[ well->state[ 16 ] ] = a ^ b ^ d ^ ( a << 2 ) ^ ( b << 18 ) ^ ( c << 28 ); + return well->state[ well->state[ 16 ] ]; + } + + +float rnd_well_nextf( rnd_well_t* well ) + { + return rnd_internal_float_normalized_from_u32( rnd_well_next( well ) ); + } + + +int rnd_well_range( rnd_well_t* well, int min, int max ) + { + int const range = ( max - min ) + 1; + if( range <= 0 ) return min; + int const value = (int) ( rnd_well_nextf( well ) * range ); + return min + value; + } + + +void rnd_gamerand_seed( rnd_gamerand_t* gamerand, RND_U32 seed ) + { + RND_U32 value = rnd_internal_murmur3_avalanche32( ( seed << 1U ) | 1U ); + gamerand->state[ 0 ] = value; + gamerand->state[ 1 ] = value ^ 0x49616e42U; + } + + +RND_U32 rnd_gamerand_next( rnd_gamerand_t* gamerand ) + { + gamerand->state[ 0 ] = ( gamerand->state[ 0 ] << 16 ) + ( gamerand->state[ 0 ] >> 16 ); + gamerand->state[ 0 ] += gamerand->state[ 1 ]; + gamerand->state[ 1 ] += gamerand->state[ 0 ]; + return gamerand->state[ 0 ]; + } + + +float rnd_gamerand_nextf( rnd_gamerand_t* gamerand ) + { + return rnd_internal_float_normalized_from_u32( rnd_gamerand_next( gamerand ) ); + } + + +int rnd_gamerand_range( rnd_gamerand_t* gamerand, int min, int max ) + { + int const range = ( max - min ) + 1; + if( range <= 0 ) return min; + int const value = (int) ( rnd_gamerand_nextf( gamerand ) * range ); + return min + value; + } + + +void rnd_xorshift_seed( rnd_xorshift_t* xorshift, RND_U64 seed ) + { + RND_U64 value = rnd_internal_murmur3_avalanche64( ( seed << 1ULL ) | 1ULL ); + xorshift->state[ 0 ] = value; + value = rnd_internal_murmur3_avalanche64( value ); + xorshift->state[ 1 ] = value; + } + + +RND_U64 rnd_xorshift_next( rnd_xorshift_t* xorshift ) + { + RND_U64 x = xorshift->state[ 0 ]; + RND_U64 const y = xorshift->state[ 1 ]; + xorshift->state[ 0 ] = y; + x ^= x << 23; + x ^= x >> 17; + x ^= y ^ ( y >> 26 ); + xorshift->state[ 1 ] = x; + return x + y; + } + + +float rnd_xorshift_nextf( rnd_xorshift_t* xorshift ) + { + return rnd_internal_float_normalized_from_u32( (RND_U32)( rnd_xorshift_next( xorshift ) >> 32 ) ); + } + + +int rnd_xorshift_range( rnd_xorshift_t* xorshift, int min, int max ) + { + int const range = ( max - min ) + 1; + if( range <= 0 ) return min; + int const value = (int) ( rnd_xorshift_next( xorshift ) * range ); + return min + value; + } + + + +#endif /* RND_IMPLEMENTATION */ + +/* +revision history: + 1.0 first publicly released version +*/ + +/* +------------------------------------------------------------------------------ + +This software is available under 2 licenses - you may choose the one you like. +Based on public domain implementation - original licenses can be found next to +the relevant implementation sections of this file. + +------------------------------------------------------------------------------ + +ALTERNATIVE A - MIT License + +Copyright (c) 2016 Mattias Gustavsson + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------------------------------------ + +ALTERNATIVE B - Public Domain (www.unlicense.org) + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +------------------------------------------------------------------------------ +*/ \ No newline at end of file