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