Rust implementation of PRNS
						commit
						3ebded19da
					
				|  | @ -0,0 +1 @@ | |||
| /target | ||||
|  | @ -0,0 +1,7 @@ | |||
| # This file is automatically @generated by Cargo. | ||||
| # It is not intended for manual editing. | ||||
| version = 4 | ||||
| 
 | ||||
| [[package]] | ||||
| name = "prns" | ||||
| version = "0.1.0" | ||||
|  | @ -0,0 +1,6 @@ | |||
| [package] | ||||
| name = "prns" | ||||
| version = "1.0.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
| [dependencies] | ||||
|  | @ -0,0 +1,3 @@ | |||
| A Rust implementation of the [PRNS pseudo-random number generator](https://marc-b-reynolds.github.io/shf/2016/04/19/prns.html). | ||||
| 
 | ||||
| NOT cryptographically secure. Generates about 600,000 u64s per millisecond on a single thread of a 7th-gen Intel Core i5. | ||||
|  | @ -0,0 +1 @@ | |||
| prns_cpp | ||||
|  | @ -0,0 +1,5 @@ | |||
| #!/usr/bin/env bash | ||||
| 
 | ||||
| set -xeuo pipefail | ||||
| 
 | ||||
| c++ -o prns_cpp main.cpp | ||||
|  | @ -0,0 +1,37 @@ | |||
| #include <iostream> | ||||
| 
 | ||||
| #include "prns.h" | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| /*
 | ||||
| Expected output from the original C version of PRNS: | ||||
| 
 | ||||
| 0 | ||||
| 16997136850213553216 | ||||
| 16093987548892232582 | ||||
| 7101631883897567084 | ||||
| 197735962506217616 | ||||
| --- | ||||
| 5 | ||||
| 12951196477222847639 | ||||
| 12951196477222847639 | ||||
| 12951196477222847639 | ||||
|  */ | ||||
| 
 | ||||
| int main() { | ||||
|     prns_t rng{0}; | ||||
| 
 | ||||
|     cerr << PRNS_MIX_S0 << endl; | ||||
| 
 | ||||
|     for(uint32_t i = 0; i < 5; i++) { | ||||
|         cerr << prns_next(&rng) << endl; | ||||
|     } | ||||
|     cerr << "---" << endl; | ||||
|     cerr << prns_tell(&rng) << endl; | ||||
|     cerr << prns_at(5) << endl; | ||||
|     cerr << prns_peek(&rng) << endl; | ||||
|     cerr << prns_prev(&rng) << endl; | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
|  | @ -0,0 +1,262 @@ | |||
| // Marc B. Reynolds, 2013-2025
 | ||||
| // Public Domain under http://unlicense.org, see link for details.
 | ||||
| //
 | ||||
| // Documentation: http://marc-b-reynolds.github.io/shf/2016/04/19/prns.html
 | ||||
| 
 | ||||
| // Short description:
 | ||||
| // *
 | ||||
| 
 | ||||
| #ifndef PRNS_H | ||||
| #define PRNS_H | ||||
| 
 | ||||
| // macro configurations to define (if desired) prior to including
 | ||||
| // this header:
 | ||||
| //
 | ||||
| // PRNS_SMALLCRUSH: weaker (and cheaper) mixing function which is
 | ||||
| // sufficient to pass SmallCrush. If undefined the generator will
 | ||||
| // pass Crush.
 | ||||
| //
 | ||||
| // PRNS_MIX: if defined overrides the mixing functions defined
 | ||||
| // in this file.
 | ||||
| //
 | ||||
| // PRNS_WEYL:
 | ||||
| // PRNS_WEYL_D:
 | ||||
| 
 | ||||
| 
 | ||||
| typedef struct { uint64_t i;  } prns_t; | ||||
| typedef struct { uint64_t i,k;} prns_down_t; | ||||
| 
 | ||||
| 
 | ||||
| //***************************************************************
 | ||||
| //*** mixing function portion (start)
 | ||||
| 
 | ||||
| // only needed if no user defined version provided
 | ||||
| #if !defined(PRNS_MIX) | ||||
| 
 | ||||
| // choose between mixing functions
 | ||||
| #ifndef PRNS_MIX_VERSION | ||||
| #define PRNS_MIX_VERSION 1 | ||||
| #endif | ||||
| 
 | ||||
| #define PRNS_MIX(X) prns_mix(X) | ||||
| 
 | ||||
| #if (PRNS_MIX_VERSION == 1) | ||||
| 
 | ||||
| #if !defined(PRNS_SMALLCRUSH) | ||||
| 
 | ||||
| #ifndef PRNS_MIX_S0 | ||||
| #ifdef  PRNS_MIX_13 | ||||
| #define PRNS_MIX_S0 30 | ||||
| #define PRNS_MIX_S1 27 | ||||
| #define PRNS_MIX_S2 31 | ||||
| #define PRNS_MIX_M0 UINT64_C(0xbf58476d1ce4e5b9) | ||||
| #define PRNS_MIX_M1 UINT64_C(0x94d049bb133111eb) | ||||
| #else | ||||
| #define PRNS_MIX_S0 31 | ||||
| #define PRNS_MIX_S1 27 | ||||
| #define PRNS_MIX_S2 33 | ||||
| #define PRNS_MIX_M0 UINT64_C(0x7fb5d329728ea185) | ||||
| #define PRNS_MIX_M1 UINT64_C(0x81dadef4bc2dd44d) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| static inline uint64_t prns_mix(uint64_t x) | ||||
| { | ||||
|   x ^= (x >> PRNS_MIX_S0); | ||||
|   x *= PRNS_MIX_M0; | ||||
|   x ^= (x >> PRNS_MIX_S1); | ||||
|   x *= PRNS_MIX_M1; | ||||
| 
 | ||||
| #ifndef PRNS_NO_FINAL_XORSHIFT | ||||
|   x ^= (x >> PRNS_MIX_S2); | ||||
| #endif | ||||
| 
 | ||||
|   return x; | ||||
| } | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| static inline uint64_t prns_mix(uint64_t x) | ||||
| { | ||||
|   x ^= (x >> 33); | ||||
|   x *= UINT64_C(0xbf58476d1ce4e5b9); | ||||
| 
 | ||||
|   return x; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| #elif (PRNS_MIX_VERSION == 2) | ||||
| 
 | ||||
| #if defined(PRNS_MIX_DEFAULT)||defined(PRNS_MIX_D_DEFAULT) | ||||
| #if defined(__ARM_ARCH) | ||||
| #if defined(__ARM_FEATURE_CRC32) | ||||
| static inline uint64_t prns_crc32c_64(uint64_t x, uint32_t k) { return __crc32cd(k,x); } | ||||
| #endif | ||||
| #endif | ||||
| #else | ||||
| static inline uint64_t prns_crc32c_64(uint64_t x, uint32_t k) { return _mm_crc32_u64(k,x); } | ||||
| #endif | ||||
| 
 | ||||
| // all wip: need to recheck some math (sigh)
 | ||||
| 
 | ||||
| static inline uint64_t prns_mix(uint64_t x) | ||||
| { | ||||
|   uint64_t r = x; | ||||
| 
 | ||||
|   r = prns_crc32c_64(r,0x9e3d2c1b) ^ (r ^ (r >> 17)); | ||||
|   r ^= (r*r) & UINT64_C(-2); | ||||
|   r = prns_crc32c_64(r,0x9e3d2c1b) ^ (r ^ (r >> 23)); | ||||
|   r ^= (r*r) & UINT64_C(-2); | ||||
| 
 | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static inline uint64_t prns_down_mix(uint64_t x, uint64_t k) | ||||
| { | ||||
|   uint64_t r = x ^ k; | ||||
| 
 | ||||
|   r = prns_crc32c_64(r,0x9e3d2c1b) ^ (r); | ||||
|   r ^= (r*r) & UINT64_C(-2); | ||||
|   r = prns_crc32c_64(r,0x9e3d2c1b) ^ (r); | ||||
|   r ^= (r*r) & UINT64_C(-2); | ||||
| 
 | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #ifndef PRNS_MIX | ||||
| #define PRNS_MIX_DEFAULT | ||||
| #ifndef PRNS_SMALLCRUSH | ||||
| #define PRNS_MIX(X) prns_mix(X) | ||||
| #else | ||||
| #define PRNS_MIX(X) prns_min_mix(X) | ||||
| #endif | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| #if !defined(PRNS_MIX_D) | ||||
| #define PRNS_MIX_D(X,K) prns_mix((X)^(K)) | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| //*** mixing function portion (end)
 | ||||
| //***************************************************************
 | ||||
| 
 | ||||
| // Weyl constant choices
 | ||||
| #ifndef PRNS_WEYL | ||||
| #define PRNS_WEYL   UINT64_C(0x61c8864680b583eb) | ||||
| #define PRNS_WEYL_I UINT64_C(0x0e217c1e66c88cc3) | ||||
| #endif | ||||
| 
 | ||||
| #ifndef PRNS_WEYL_D | ||||
| #define PRNS_WEYL_D   UINT64_C(0x4f1bbcdcbfa54001) | ||||
| #define PRNS_WEYL_D_I UINT64_C(0x4f1bbcdcbfa54001) // NO! compute it and place
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| // return the position in the stream
 | ||||
| static inline uint64_t prns_tell(prns_t* gen) | ||||
| { | ||||
|   return gen->i * PRNS_WEYL_I; | ||||
| } | ||||
| 
 | ||||
| // sets the position in the stream
 | ||||
| static inline void prns_set(prns_t* gen, uint64_t pos) | ||||
| { | ||||
|   gen->i = PRNS_WEYL*pos; | ||||
| } | ||||
| 
 | ||||
| // moves the stream position by 'offset'
 | ||||
| static inline void prns_seek(prns_t* gen, int64_t offset) | ||||
| { | ||||
|   gen->i += PRNS_WEYL*((uint64_t)offset); | ||||
| } | ||||
| 
 | ||||
| // returns the random number at position 'n'
 | ||||
| static inline uint64_t prns_at(uint64_t n) | ||||
| { | ||||
|   return PRNS_MIX(PRNS_WEYL*n); | ||||
| } | ||||
| 
 | ||||
| // returns the current random number without advancing the position
 | ||||
| static inline uint64_t prns_peek(prns_t* gen) | ||||
| { | ||||
|   return PRNS_MIX(gen->i); | ||||
| } | ||||
| 
 | ||||
| // return the current random number and advances the position by one
 | ||||
| static inline uint64_t prns_next(prns_t* gen) | ||||
| { | ||||
|   uint64_t i = gen->i; | ||||
|   uint64_t r = PRNS_MIX(i); | ||||
|   gen->i = i + PRNS_WEYL; | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| // return the current random number and moves the position by backward by one
 | ||||
| static inline uint64_t prns_prev(prns_t* gen) | ||||
| { | ||||
|   uint64_t i = gen->i; | ||||
|   uint64_t r = PRNS_MIX(i); | ||||
|   gen->i = i - PRNS_WEYL; | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| //**** "down" functions
 | ||||
| 
 | ||||
| static inline void prns_down_init(prns_down_t* d, prns_t* s) | ||||
| { | ||||
|   d->i = 0; | ||||
|   d->k = s->i; | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t prns_down_tell(prns_down_t* gen) | ||||
| { | ||||
|   return gen->i * PRNS_WEYL_D_I; | ||||
| } | ||||
| 
 | ||||
| static inline void prns_down_set(prns_down_t* gen, uint64_t pos) | ||||
| { | ||||
|   gen->i = PRNS_WEYL_D*pos; | ||||
| } | ||||
| 
 | ||||
| static inline void prns_down_seek(prns_down_t* gen, int64_t offset) | ||||
| { | ||||
|   gen->i += PRNS_WEYL_D*((uint64_t)offset); | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t prns_down_at(prns_down_t* gen, uint64_t n) | ||||
| { | ||||
|   return PRNS_MIX_D(PRNS_WEYL_D*n, gen->k); | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t prns_down_peek(prns_down_t* gen) | ||||
| { | ||||
|   return PRNS_MIX_D(gen->i, gen->k); | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t prns_down_next(prns_down_t* gen) | ||||
| { | ||||
|   uint64_t i = gen->i; | ||||
|   uint64_t r = PRNS_MIX_D(i, gen->k); | ||||
|   gen->i = i + PRNS_WEYL_D; | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| static inline uint64_t prns_down_prev(prns_down_t* gen) | ||||
| { | ||||
|   uint64_t i = gen->i; | ||||
|   uint64_t r = PRNS_MIX_D(i, gen->k); | ||||
|   gen->i = i - PRNS_WEYL_D; | ||||
|   return r; | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
|  | @ -0,0 +1,74 @@ | |||
| //! Implements the prns PRNG
 | ||||
| //!
 | ||||
| //! https://github.com/Marc-B-Reynolds/Stand-alone-junk/blob/master/src/SFH/prns.h
 | ||||
| 
 | ||||
| use std::num::Wrapping; | ||||
| 
 | ||||
| #[derive(Default)] | ||||
| pub struct Prns { | ||||
|     i: Wrapping<u64>, | ||||
| } | ||||
| 
 | ||||
| const PRNS_MIX_S0: usize = 31; | ||||
| const PRNS_MIX_S1: usize = 27; | ||||
| const PRNS_MIX_S2: usize = 33; | ||||
| const PRNS_MIX_M0: u64 = 0x7fb5d329728ea185; | ||||
| const PRNS_MIX_M1: u64 = 0x81dadef4bc2dd44d; | ||||
| const PRNS_WEYL: Wrapping<u64> = Wrapping(0x61c8864680b583eb); | ||||
| const PRNS_WEYL_I: Wrapping<u64> = Wrapping(0x0e217c1e66c88cc3); | ||||
| 
 | ||||
| impl Prns { | ||||
|     pub fn mix(mut x: Wrapping<u64>) -> u64 { | ||||
|         x ^= x >> PRNS_MIX_S0; | ||||
|         x *= PRNS_MIX_M0; | ||||
|         x ^= x >> PRNS_MIX_S1; | ||||
|         x *= PRNS_MIX_M1; | ||||
|         x ^= x >> PRNS_MIX_S2; | ||||
|         x.0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn tell(&self) -> u64 { | ||||
|         (self.i * PRNS_WEYL_I).0 | ||||
|     } | ||||
| 
 | ||||
|     pub fn at(n: u64) -> u64 { | ||||
|         Self::mix(PRNS_WEYL * Wrapping(n)) | ||||
|     } | ||||
| 
 | ||||
|     pub fn peek(&self) -> u64 { | ||||
|         Self::mix(self.i) | ||||
|     } | ||||
| 
 | ||||
|     #[allow(clippy::should_implement_trait)] | ||||
|     pub fn next(&mut self) -> u64 { | ||||
|         let r = Self::mix(self.i); | ||||
|         self.i += PRNS_WEYL; | ||||
|         r | ||||
|     } | ||||
| 
 | ||||
|     pub fn prev(&mut self) -> u64 { | ||||
|         let r = Self::mix(self.i); | ||||
|         self.i -= PRNS_WEYL; | ||||
|         r | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn it_works() { | ||||
|         let mut rng = Prns::default(); | ||||
|         assert_eq!(rng.next(), 0); | ||||
|         assert_eq!(rng.next(), 16997136850213553216); | ||||
|         assert_eq!(rng.next(), 16093987548892232582); | ||||
|         assert_eq!(rng.next(), 7101631883897567084); | ||||
|         assert_eq!(rng.next(), 197735962506217616); | ||||
| 
 | ||||
|         assert_eq!(rng.tell(), 5); | ||||
|         assert_eq!(Prns::at(5), 12951196477222847639); | ||||
|         assert_eq!(rng.peek(), 12951196477222847639); | ||||
|         assert_eq!(rng.prev(), 12951196477222847639); | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	 _
						_