Pseudo-random numbers/Splitmix64

From Rosetta Code
Task
Pseudo-random numbers/Splitmix64
You are encouraged to solve this task according to the task description, using any language you may know.

Splitmix64 is the default pseudo-random number generator algorithm in Java and is included / available in many other languages. It uses a fairly simple algorithm that, though it is considered to be poor for cryptographic purposes, is very fast to calculate, and is "good enough" for many random number needs. It passes several fairly rigorous PRNG "fitness" tests that some more complex algorithms fail.

Splitmix64 is not recommended for demanding random number requirements, but is often used to calculate initial states for other more complex pseudo-random number generators.

The "standard" splitmix64 maintains one 64 bit state variable and returns 64 bits of random data with each call.

Basic pseudocode algorithm:

    uint64 state                                  /* The state can be seeded with any (upto) 64 bit integer value. */

    next_int() {
        state += 0x9e3779b97f4a7c15               /* increment the state variable */
        uint64 z = state                          /* copy the state to a working variable */
        z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9  /* xor the variable with the variable right bit shifted 30 then multiply by a constant */
        z = (z ^ (z >> 27)) * 0x94d049bb133111eb  /* xor the variable with the variable right bit shifted 27 then multiply by a constant */
        return z ^ (z >> 31)                      /* return the variable xored with itself right bit shifted 31 */
    }

    next_float() {
        return next_int() / (1 << 64)             /* divide by 2^64 to return a value between 0 and 1 */
    }

The returned value should hold 64 bits of numeric data. If your language does not support unsigned 64 bit integers directly you may need to apply appropriate bitmasks during bitwise operations.

In keeping with the general layout of several recent pseudo-random number tasks:


Task
  • Write a class or set of functions that generates pseudo-random numbers using splitmix64.
  • Show the first five integers generated using the seed 1234567.
    6457827717110365317
    3203168211198807973
    9817491932198370423
    4593380528125082431
   16408922859458223821
  
  • Show that for an initial seed of 987654321, the counts of 100_000 repetitions of floor next_float() * 5 is as follows:
   0: 20027, 1: 19892, 2: 20073, 3: 19978, 4: 20030  
  • Show your output here, on this page.


See also


Related tasks


11l

Translation of: Python
T Splitmix64
   UInt64 state

   F seed(seed_state)
      .state = seed_state

   F next_int()
      .state += 9E37'79B9'7F4A'7C15
      V z = .state
      z = (z (+) (z >> 30)) * BF58'476D'1CE4'E5B9
      z = (z (+) (z >> 27)) * 94D0'49BB'1331'11EB
      R z (+) (z >> 31)

   F next_float()
      R Float(.next_int()) / 2.0^64

V random_gen = Splitmix64()
random_gen.seed(1234567)
L 5
   print(random_gen.next_int())

random_gen.seed(987654321)
V hist = Dict(0.<5, i -> (i, 0))
L 100'000
   hist[Int(random_gen.next_float() * 5)]++
print(hist)
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
[0 = 20027, 1 = 19892, 2 = 20073, 3 = 19978, 4 = 20030]

Ada

The random number functions are written in a stand-alone package. The package is split into a package specification defining the interfaces to the public subprograms and a body containing the implementation of the random number generator.

package specification:

with Interfaces; use Interfaces;

package Random_Splitmix64 is

   function next_Int return Unsigned_64;
   function next_float return Float;
   procedure Set_State (Seed : in Unsigned_64);
end Random_Splitmix64;

package body:

package body Random_Splitmix64 is
   Internal : Unsigned_64 := 1234567;

   --------------
   -- next_Int --
   --------------

   function next_Int return Unsigned_64 is
      Z : Unsigned_64;
   begin
      Internal := Internal + 16#9e3779b97f4a7c15#;
      Z := Internal;
      Z := (Z xor Shift_Right(Z, 30)) * 16#bf58476d1ce4e5b9#;
      Z := (Z xor Shift_Right(Z, 27)) * 16#94d049bb133111eb#;
      return Z xor Shift_Right(Z, 31);
   end next_Int;

   ----------------
   -- next_float --
   ----------------

   function next_float return Float is
   begin
      return float(next_int) / (2.0 ** 64);
   end next_float;

   ---------------
   -- Set_State --
   ---------------

   procedure Set_State (Seed : in Unsigned_64) is
   begin
      Internal := Seed;
   end Set_State;

end Random_Splitmix64;

Main procedure:

with Interfaces;        use Interfaces;
with Random_Splitmix64; use Random_Splitmix64;
with Ada.Text_IO;       use Ada.Text_IO;

procedure Main is
   subtype idx is Integer range 0 .. 4;
   type answer_arr is array (idx) of Natural;
   Vec : answer_arr := (others => 0);
   J   : Integer;
   fj  : Float;
begin
   Set_State (1_234_567);
   for I in 1 .. 5 loop
      Put (Unsigned_64'Image (next_Int));
      New_Line;
   end loop;

   Set_State (987_654_321);

   for I in 1 .. 100_000 loop
      fj      := Float'Truncation (next_float * 5.0);
      J       := Integer (fj);
      Vec (J) := Vec (J) + 1;
   end loop;

   for I in Vec'Range loop
      Put_Line (I'Image & ":" & Integer'Image (Vec (I)));
   end loop;

end Main;
Output:
 6457827717110365317
 3203168211198807973
 9817491932198370423
 4593380528125082431
 16408922859458223821
 0: 20027
 1: 19892
 2: 20073
 3: 19978
 4: 20030

ALGOL 68

Works with: ALGOL 68G version Any Tested with release 2.8.3.win32
BEGIN # generate some pseudo random numbers using Splitmix64 #
    # note that although LONG INT is 64 bits in Algol 68G, LONG BITS is longer than 64 bits #
    LONG BITS mask 64    = LONG 16rffffffffffffffff;
    LONG BITS state     := 16r1234567;
    LONG INT  one shl 64 = ABS ( LONG 16r1 SHL 64 );
    # sets the state to the specified seed value #
    PROC seed = ( LONG INT num )VOID: state := BIN num;
    # XOR and assign convenience operator #
    PRIO XORAB = 1;
    OP   XORAB = ( REF LONG BITS x, LONG BITS v )REF LONG BITS:
         x := ( x XOR v ) AND mask 64;
    # add a LONG BITS value to a LONG BITS #
    OP   +:= = ( REF LONG BITS r, LONG BITS v )REF LONG BITS:
         r := SHORTEN ( BIN ( LENG ABS r + LENG ABS v ) AND mask 64 );
    # multiplies a LONG BITS value by a LONG BITS value #
    OP   *:= = ( REF LONG BITS r, LONG BITS v )REF LONG BITS:
         r := SHORTEN ( BIN ( ABS LENG r * LENG ABS v ) AND mask 64 );
    # gets the next pseudo random integer #
    PROC next int = LONG INT:
         BEGIN
            state +:= LONG 16r9e3779b97f4a7c15;
            LONG BITS z := state;
            z XORAB ( z SHR 30 );
            z *:= LONG 16rbf58476d1ce4e5b9;
            z XORAB ( z SHR 27 );
            z *:= LONG 16r94d049bb133111eb;
            z XORAB ( z SHR 31 );
            ABS z
         END # next int # ;
    # gets the next pseudo random real #
    PROC next float = LONG REAL: next int / one shl 64;
    BEGIN # task test cases #
        seed( 1234567 );
        print( ( whole( next int, 0 ), newline ) ); #  6457827717110365317 #
        print( ( whole( next int, 0 ), newline ) ); #  3203168211198807973 #
        print( ( whole( next int, 0 ), newline ) ); #  9817491932198370423 #
        print( ( whole( next int, 0 ), newline ) ); #  4593380528125082431 #
        print( ( whole( next int, 0 ), newline ) ); # 16408922859458223821 #
        # count the number of occurances of 0..4 in a sequence of pseudo random reals scaled to be in [0..5) #
        seed( 987654321 );
        [ 0 : 4 ]INT counts; FOR i FROM LWB counts TO UPB counts DO counts[ i ] := 0 OD;
        TO 100 000 DO counts[ SHORTEN ENTIER ( next float * 5 ) ] +:= 1 OD;
        FOR i FROM LWB counts TO UPB counts DO
            print( ( whole( i, -2 ), ": ", whole( counts[ i ], -6 ) ) )
        OD;
        print( ( newline ) )
    END
END
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
 0:  20027 1:  19892 2:  20073 3:  19978 4:  20030

C

Code copied from the reference C implementation used by Java, and using GNU GCC v7.1.1.

/*  Written in 2015 by Sebastiano Vigna (vigna@acm.org)

To the extent possible under law, the author has dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.

See <http://creativecommons.org/publicdomain/zero/1.0/>. */

#include <stdint.h>
#include <stdio.h>
#include <math.h>

/* This is a fixed-increment version of Java 8's SplittableRandom generator
   See http://dx.doi.org/10.1145/2714064.2660195 and 
   http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html

   It is a very fast generator passing BigCrush, and it can be useful if
   for some reason you absolutely want 64 bits of state. */

static uint64_t x; /* The state can be seeded with any value. */

uint64_t next() {
	uint64_t z = (x += 0x9e3779b97f4a7c15);
	z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
	z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
	return z ^ (z >> 31);
}

double next_float() {
    return next() / pow(2.0, 64);
}

int main() {
    int i, j;
    x = 1234567;
    for(i = 0; i < 5; ++i)
        printf("%llu\n", next()); /* needed to use %lu verb for GCC 7.5.0-3 */
    x = 987654321;
    int vec5[5] = {0, 0, 0, 0, 0};
    for(i = 0; i < 100000; ++i) {
        j = next_float() * 5.0;
        vec5[j] += 1;
    }
    for(i = 0; i < 5; ++i)
        printf("%d: %d  ", i, vec5[i]);
}
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
0: 20027  1: 19892  2: 20073  3: 19978  4: 20030  

C++

#include <iostream>
#include <cmath>
#include <vector>

class Splitmix64 {
public:
	Splitmix64() { state = 0; }
	Splitmix64(const uint64_t seed) : state(seed) {}

	void seed(const uint64_t seed) {
		state = seed;
	}

	uint64_t next_int() {
		uint64_t z = ( state += 0x9e3779b97f4a7c15 );
		z = ( z ^ ( z >> 30 ) ) * 0xbf58476d1ce4e5b9;
		z = ( z ^ ( z >> 27 ) ) * 0x94d049bb133111eb;
		return z ^ ( z >> 31 );
	}

	double next_float() {
	    return next_int() / twoPower64;
	}

private:
	uint64_t state;
	const double twoPower64 = pow(2.0, 64);
};

int main() {
	Splitmix64 random;
	random.seed(1234567);
	for ( int32_t i = 0; i < 5; ++i ) {
		std::cout << random.next_int() << std::endl;
	}
	std::cout << std::endl;

	Splitmix64 rand(987654321);
	std::vector<uint32_t> counts(5, 0);
	for ( int32_t i = 0; i < 100'000; ++i ) {
		uint32_t value = floor(rand.next_float() * 5.0);
		counts[value] += 1;
	}

	for ( int32_t i = 0; i < 5; ++i ) {
		std::cout << i << ": " << counts[i] << "   ";
	}
	std::cout << std::endl;
}
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

0: 20027   1: 19892   2: 20073   3: 19978   4: 20030  

Factor

USING: io kernel math math.bitwise math.functions
math.statistics namespaces prettyprint sequences ;

SYMBOL: state

: seed ( n -- ) 64 bits state set ;

: next-int ( -- n )
    0x9e3779b97f4a7c15 state [ + 64 bits ] change
    state get -30 0xbf58476d1ce4e5b9 -27 0x94d049bb133111eb -31 1
    [ [ dupd shift bitxor ] dip * 64 bits ] 2tri@ ;

: next-float ( -- x ) next-int 64 2^ /f ;

! Test next-int
"Seed: 1234567; first five integer values" print
1234567 seed 5 [ next-int . ] times nl

! Test next-float
"Seed: 987654321; first 100,000 float values histogram" print
987654321 seed 100,000 [ next-float 5 * >integer ] replicate
histogram .
Output:
Seed: 1234567; first five integer values
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

Seed: 987654321; first 100,000 float values histogram
H{ { 0 20027 } { 1 19892 } { 2 20073 } { 3 19978 } { 4 20030 } }


Forth

Works with: gforth version 0.7.3
variable rnd-state

: rnd-base-op ( z factor shift -- u ) 2 pick swap rshift rot xor * ;

: rnd-next ( -- u )
  $9e3779b97f4a7c15 rnd-state +!
  rnd-state @
  $bf58476d1ce4e5b9 #30 rnd-base-op
  $94d049bb133111eb #27 rnd-base-op
  #1 #31 rnd-base-op
;

#1234567 rnd-state !
cr
rnd-next u. cr
rnd-next u. cr
rnd-next u. cr
rnd-next u. cr
rnd-next u. cr


: rnd-next-float ( -- f )
  rnd-next 0 d>f 0 1 d>f f/
;

create counts 0 , 0 , 0 , 0 , 0 ,
: counts-fill
  #987654321 rnd-state !
  100000 0 do
    rnd-next-float 5.0e0 f* f>d drop cells counts + dup @ 1+ swap !
  loop
;
: counts-disp
  5 0 do
    cr i . ': emit bl emit
    counts i cells + @ .
  loop cr
;

counts-fill counts-disp
Output:
6457827717110365317 
3203168211198807973 
9817491932198370423 
4593380528125082431 
16408922859458223821 

0 : 20027 
1 : 19892 
2 : 20073 
3 : 19978 
4 : 20030 
 ok

F#

// Pure F# Implementation of SplitMix64
let a: uint64 = 0x9e3779b97f4a7c15UL

let nextInt (state: uint64) =
    let newstate = state + (0x9e3779b97f4a7c15UL)
    let rand = newstate
    let rand = (rand ^^^ (rand >>> 30)) * 0xbf58476d1ce4e5b9UL
    let rand = (rand ^^^ (rand >>> 27)) * 0x94d049bb133111ebUL
    let rand = rand ^^^ (rand >>> 31)
    (rand, newstate)

let nextFloat (state: uint64) =
    let (rand, newState) = nextInt state
    let randf = (rand / (1UL <<< 64)) |> float
    (randf, newState)

[<EntryPoint>]
let main argv =
    let state = 1234567UL
    let (first, state) = nextInt state
    let (second, state) = nextInt state
    let (third, state) = nextInt state
    let (fourth, state) = nextInt state
    let (fifth, state) = nextInt state
    printfn "%i" first
    printfn "%i" second
    printfn "%i" third
    printfn "%i" fourth
    printfn "%i" fifth
    0
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

Go

package main

import (
    "fmt"
    "math"
)

type Splitmix64 struct{ state uint64 }

func Splitmix64New(state uint64) *Splitmix64 { return &Splitmix64{state} }

func (sm64 *Splitmix64) nextInt() uint64 {
    sm64.state += 0x9e3779b97f4a7c15
    z := sm64.state
    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9
    z = (z ^ (z >> 27)) * 0x94d049bb133111eb
    return z ^ (z >> 31)
}

func (sm64 *Splitmix64) nextFloat() float64 {
    return float64(sm64.nextInt()) / (1 << 64)
}

func main() {
    randomGen := Splitmix64New(1234567)
    for i := 0; i < 5; i++ {
        fmt.Println(randomGen.nextInt())
    }

    var counts [5]int
    randomGen = Splitmix64New(987654321)
    for i := 0; i < 1e5; i++ {
        j := int(math.Floor(randomGen.nextFloat() * 5))
        counts[j]++
    }
    fmt.Println("\nThe counts for 100,000 repetitions are:")
    for i := 0; i < 5; i++ {
        fmt.Printf("  %d : %d\n", i, counts[i])
    }
}
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

The counts for 100,000 repetitions are:
  0 : 20027
  1 : 19892
  2 : 20073
  3 : 19978
  4 : 20030

Haskell

import Data.Bits

import Data.Word
import Data.List

next :: Word64 -> (Word64, Word64)
next state = f4 $ state + 0x9e3779b97f4a7c15
  where
    f1 z = (z `xor` (z `shiftR` 30)) * 0xbf58476d1ce4e5b9
    f2 z = (z `xor` (z `shiftR` 27)) * 0x94d049bb133111eb
    f3 z = z `xor` (z `shiftR` 31)
    f4 s = ((f3 . f2 . f1) s, s)

randoms = unfoldr (pure . next) 

toFloat n = fromIntegral n / (2^64 - 1)
λ> mapM_ print $ take 5 $ randoms 1234567
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

λ> let hist = map length . group . sort
λ> hist . take 100000 $ (floor . (*5) . toFloat) <$> (randoms 987654321)
[20027,19892,20073,19978,20030]

Java

Java integers are signed and so have a maximum value of 2^63 - 1, requiring the use of the BigInteger class for this task.

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class PseudoRandomSplitmix64 {

	public static void main(String[] aArgs) {
		Splitmix64 random = new Splitmix64();
		random.seed(1234567);
		for ( int i = 0; i < 5; i++ ) {
			System.out.println(random.nextInt());
		}
		
		List<Integer> counts = new ArrayList<Integer>(Collections.nCopies(5, 0));
		
		Splitmix64 rand = new Splitmix64(987654321);
	    for ( int i = 0; i < 100_000; i++ ) {	    	
	        BigDecimal value = rand.nextFloat();
	        final int count = value.multiply(BigDecimal.valueOf(5.0)).toBigInteger().intValue();
	        counts.set(count, counts.get(count) + 1);
	    }
	    
	    System.out.println(System.lineSeparator() + "The counts for 100,000 repetitions are: ");
	    for ( int i = 0; i < 5; i++ ) {
	    	System.out.print(i + ": " + counts.get(i) + "   ");
	    }
	    System.out.println();
    }
	
}

final class Splitmix64 {
	
	public Splitmix64() {
		state = BigInteger.ZERO;
	}
	
	public Splitmix64(long aSeed) {
		state = BigInteger.valueOf(aSeed).and(mask64);
	}
	
	public void seed(long aNumber) {
		state = BigInteger.valueOf(aNumber);
	}
	    
	public BigInteger nextInt() {
		state = state.add(constant1).and(mask64);
        BigInteger z = state;
        z = z.xor(z.shiftRight(30)).multiply(constant2).and(mask64);
        z = z.xor(z.shiftRight(27)).multiply(constant3).and(mask64);
        BigInteger result = z.xor(z.shiftRight(31)).and(mask64);
    
        return result;
	}
	    
	public BigDecimal nextFloat() {
		return new BigDecimal(nextInt()).divide(twoPower64, MathContext.DECIMAL64);
	}
	    
	private BigInteger state;
	
	private final BigInteger constant1 = new BigInteger("9e3779b97f4a7c15", 16);
	private final BigInteger constant2 = new BigInteger("bf58476d1ce4e5b9", 16); 
	private final BigInteger constant3 = new BigInteger("94d049bb133111eb", 16);
	private final BigInteger mask64 = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE);
	private final BigDecimal twoPower64 = new BigDecimal(BigInteger.ONE.shiftLeft(64));

}
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

The counts for 100,000 repetitions are: 
0: 20027   1: 19892   2: 20073   3: 19978   4: 20030   

Jasmin

u64 c1 = 0x9e3779b97f4a7c15;
u64 c2 = 0xbf58476d1ce4e5b9;
u64 c3 = 0x94d049bb133111eb;

inline
fn next_int(reg u64 state) -> reg u64 {
   reg u64 z a;
   z = [state];
   z += c1;
   [state] = z;
   a = z;
   a >>= 30;
   z ^= a;
   z *= c2;
   a = z;
   a >>= 27;
   z ^= a;
   z *= c3;
   a = z;
   a >>= 31;
   z ^= a;
   return z;
}

inline
fn test() -> reg u64[5] {
  reg u64 seed;
  seed = 0x1000;
  [seed] = 1234567;
  inline int i;
  reg u64[5] result;
  for i = 0 to 5 {
    result[i] = next_int(seed);
  }
  return result;
}
exec test(0x1000:8)

jq

Works with gojq, the Go implementation of jq

Adapted from Wren

This entry assumes sufficiently precise integer arithmetic, e.g. as provided by gojq, which supports infinite-precision integer arithmetic. Unfortunately, though, gojq does not support efficient bitwise operations, and using the following to generate 100,000 random numbers takes about 13 minutes on a 3GHz machine.

The main significance of this entry is thus to increase confidence in the correctness of the definitions as well as in the Go implementation of jq.

In the following, a 'bitarray' is a 0/1 array, and when integers are represented by bitarrays, the first bit is the least-significant one. Unless otherwise indicated, the bitwise operations work on bitarrays.

Generic utilities

# Input: a string in base $b (2 to 35 inclusive)
# Output: a JSON number, being the decimal value corresponding to the input.
def frombase($b):
  def decimalValue:
    if   48 <= . and . <= 57 then . - 48
    elif 65 <= . and . <= 90 then . - 55  # (10+.-65)
    elif 97 <= . and . <= 122 then . - 87 # (10+.-97)
    else "decimalValue" | error
    end;
  reduce (explode|reverse[]|decimalValue) as $x ({p:1};
    .value += (.p * $x)
    | .p *= $b)
  | .value ;

# To take advantage of gojq's arbitrary-precision integer arithmetic:
def power($b): . as $in | reduce range(0;$b) as $i (1; . * $in);

# If the input and $j are integers, then the result will be an integer.
def div($j):
  (. - (. % j)) / $j;

# Convert an integer to a bitarray, least significant bit first
def bitwise:
  recurse( if . >= 2 then div(2) else empty end) | . % 2;

# Essentially the inverse of bitwise,
# i.e. interpret an array of 0s and 1s (with least-significant-bit first ) as a decimal
def to_int:
  . as $in
  # state: [sum, power]
  | reduce .[] as $i ([0, 1]; .[1] as $p | [.[0] + $p * $i, ($p * 2)])
  | .[0];

# $x and $y and output are bitarrays
def xor($x;$y):
   def lxor(a;b):
     if (a==1 or b==1) and ((a==1 and b==1)|not) then 1
     elif a == null then b
     elif b == null then a
     else 0
     end;
   if $x == [0] then $y
   elif $y == [0] then $x
   else
     [ range(0; [($x|length), ($y|length)] | max) as $i
       | lxor($x[$i]; $y[$i]) ]
   end ;

# $x and $y and output are bitarrays
def xand($x;$y):
   def lxand(a;b):
      (a==1 and b==1) | 1 // 0;
   if $x == [0] or $y == [0] then [0]
   else
     [range(0; [($x|length), ($y|length)] | min) as $i
      | lxand($x[$i]; $y[$i]) ]
   end ;

# shift right
def right($n): .[$n:];

def mask64: .[:64];

# input and output: a bitarray
def mult($int):
  ($int * to_int) | [bitwise];

def plus($int):
  ($int + to_int) | [bitwise];

def tabulate(stream):
  reduce stream as $i ([]; .[$i] += 1)
  | range(0;length) as $i
  | " \($i) :  \(.[$i] // 0)" ;

Splitmix64

# input: a bitarray
def nextInt:
  def Const1: "9e3779b97f4a7c15" | frombase(16) ;
  def Const2: "bf58476d1ce4e5b9" | frombase(16) ;
  def Const3: "94d049bb133111eb" | frombase(16) ;

  (plus(Const1) | mask64)
  | . as $state
  |  xor(.; right(30)) | mult(Const2) | mask64
  |  xor(.; right(27)) | mult(Const3) | mask64
  |  xor(.; right(31)) | mask64
  | ., ($state|nextInt) ;

def randomInt64: [bitwise] | nextInt | to_int;

def randomReal:
  pow(2;64) as $d
  | [bitwise] | nextInt | to_int / $d;

### The tasks
(limit(5; 1234567 | randomInt64)),

"\nThe counts for 100,000 repetitions are:",
tabulate( limit(100;  987654321 | randomReal * 5 | floor) )
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

The counts for 100,000 repetitions are:
 0 :  20027
 1 :  19892
 2 :  20073
 3 :  19978
 4 :  20030

Julia

Translation of: Python
const C1 = 0x9e3779b97f4a7c15
const C2 = 0xbf58476d1ce4e5b9
const C3 = 0x94d049bb133111eb

mutable struct Splitmix64
    state::UInt
end

""" return random int between 0 and 2**64 """
function next_int(smx::Splitmix64)
    z = smx.state = smx.state + C1
    z = (z  (z >> 30)) * C2
    z = (z  (z >> 27)) * C3
    return z  (z >> 31)
end

""" return random float between 0 and 1 """
next_float(smx::Splitmix64) = next_int(smx) / one(Int128) << 64

function testSplitmix64()
    random_gen = Splitmix64(1234567)
    for i in 1:5
        println(next_int(random_gen))
    end

    random_gen = Splitmix64(987654321)
    hist = fill(0, 5)
    for _ in 1:100_000
        hist[Int(floor(next_float(random_gen) * 5)) + 1] += 1
    end
    foreach(n -> print(n - 1, ": ", hist[n], "  "), 1:5)
end

testSplitmix64()
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
0: 20027  1: 19892  2: 20073  3: 19978  4: 20030

Mathematica/Wolfram Language

ClearAll[BitShiftLevelUint, MultiplyUint, GenerateRandomNumbers]
BitShiftLevelUint[z_, n_] := BitShiftRight[z, n]
MultiplyUint[z_, n_] := Mod[z n, 2^64]
GenerateRandomNumbers[st_, n_] := Module[{state = st},
  Table[
   state += 16^^9e3779b97f4a7c15;
   state = Mod[state, 2^64];
   z = state;
   z = MultiplyUint[BitXor[z, BitShiftLevelUint[z, 30]], 16^^bf58476d1ce4e5b9];
   z = MultiplyUint[BitXor[z, BitShiftLevelUint[z, 27]], 16^^94d049bb133111eb];
   Mod[BitXor[z, BitShiftLevelUint[z, 31]], 2^64]
   ,
   {n}
   ]
  ]
GenerateRandomNumbers[1234567, 5]
nums = GenerateRandomNumbers[987654321, 10^5];
KeySort[Counts[Floor[5 nums/N[2^64]]]]
Output:
{6457827717110365317, 3203168211198807973, 9817491932198370423, 4593380528125082431, 16408922859458223821}
<|0->20027, 1->19892, 2->20073, 3->19978, 4->20030|>

Nim

import math, sequtils, strutils

const Two64 = 2.0^64

type Splitmix64 = object
  state: uint64

func initSplitmix64(seed: uint64): Splitmix64 =
  ## Initialize a Splitmiax64 PRNG.
  Splitmix64(state: seed)

func nextInt(r: var Splitmix64): uint64 =
  ## Return the next pseudorandom integer (actually a uint64 value).
  r.state += 0x9e3779b97f4a7c15u
  var z = r.state
  z = (z xor z shr 30) * 0xbf58476d1ce4e5b9u
  z = (z xor z shr 27) * 0x94d049bb133111ebu
  result = z xor z shr 31

func nextFloat(r: var Splitmix64): float =
  ## Retunr the next pseudorandom float (between 0.0 and 1.0 excluded).
  r.nextInt().float / Two64


when isMainModule:

  echo "Seed = 1234567:"
  var prng = initSplitmix64(1234567)
  for i in 1..5:
    echo i, ": ", ($prng.nextInt).align(20)

  echo "\nSeed = 987654321:"
  var counts: array[0..4, int]
  prng = initSplitmix64(987654321)
  for _ in 1..100_000:
    inc counts[int(prng.nextFloat * 5)]
  echo toSeq(counts.pairs).mapIt(($it[0]) & ": " & ($it[1])).join(", "
Output:
Seed = 1234567:
1:  6457827717110365317
2:  3203168211198807973
3:  9817491932198370423
4:  4593380528125082431
5: 16408922859458223821

Seed = 987654321:
0: 20027, 1: 19892, 2: 20073, 3: 19978, 4: 20030

Object Pascal

program splitmix64;

{$IF Defined(FPC)}{$MODE Delphi}{$ENDIF}
{$INLINE ON}
{$Q-}{$R-}

{
  Written in 2015 by Sebastiano Vigna (vigna@acm.org)
  http://prng.di.unimi.it/splitmix64.c

  Onject Pascal port written in 2020 by I. Kakoulidis

  To the extent possible under law, the author has dedicated all copyright
  and related and neighboring rights to this software to the public domain
  worldwide. This software is distributed without any warranty.

  See <http://creativecommons.org/publicdomain/zero/1.0/>. 
}

{
  This is a fixed-increment version of Java 8's SplittableRandom generator
  See http://dx.doi.org/10.1145/2714064.2660195 and
  http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html

  It is a very fast generator passing BigCrush, and it can be useful if
  for some reason you absolutely want 64 bits of state.
}
uses Math;

type
  TSplitMix64 = record
    state: UInt64;
    procedure Init(seed: UInt64); inline;
    function Next(): UInt64; inline;
    function NextFloat(): double; inline;
  end;

procedure TSplitMix64.Init(seed: UInt64);
begin
  state := seed;
end;

function TSplitMix64.Next(): UInt64;
begin
  state := state + UInt64($9e3779b97f4a7c15);
  Result := state;
  Result := (Result xor (Result shr 30)) * UInt64($bf58476d1ce4e5b9);
  Result := (Result xor (Result shr 27)) * UInt64($94d049bb133111eb);
  Result := Result xor (Result shr 31);
end;

function TSplitMix64.NextFloat(): Double;
begin
  Result := Next() / 18446744073709551616.0;
end;

var
  r: TSplitMix64;
  i, j: Integer;
  vec: array[0..4] of Integer;

begin
  j := 0;
  r.Init(1234567);
  for i := 0 to 4 do
    WriteLn(r.Next());

  r.Init(987654321); 
  for i := 0 to 99999 do
  begin
    j := Trunc(r.NextFloat() * 5.0);
    Inc(vec[j]);
  end;

  for i := 0 to 4 do
    Write(i, ': ', vec[i], '  ');
end.
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
0: 20027  1: 19892  2: 20073  3: 19978  4: 20030  

Odin

Based on the C reference implementation.

package main

import "core:fmt"
import "core:math"

TWO64 :f64: 1 << 64

SplitMix64 :: struct {
    state: u64,
}

next_int :: proc(rng: ^SplitMix64) -> u64 {
    rng.state += 0x9e3779b97f4a7c15
    z := rng.state
    z = (z ~ (z >> 30)) * 0xbf58476d1ce4e5b9
    z = (z ~ (z >> 27)) * 0x94d049bb133111eb
    return z ~ (z >> 31)
}

next_float :: proc(rng: ^SplitMix64) -> f64 {
    return f64(next_int(rng)) / TWO64
}

main :: proc() {
    rng := SplitMix64{1234567}

    for i in 0..<5 {
        fmt.println(next_int(&rng))
    }

    rng.state = 987654321
    
    vec5 := [5]int{0,0,0,0,0}
    for i in 0..<100000 {
        j := int(math.floor(next_float(&rng) * 5.))
        vec5[j] += 1
    }

    for v,i in vec5 {
        fmt.printf("%d: %d   ", i, v)
    }
}
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
0: 20027   1: 19892   2: 20073   3: 19978   4: 20030

Perl

use strict;
use warnings;
no warnings 'portable';
use feature 'say';
use Math::AnyNum qw(:overload);

package splitmix64 {

    sub new {
        my ($class, %opt) = @_;
        bless {state => $opt{seed}}, $class;
    }

    sub next_int {
        my ($self) = @_;
        my $next = $self->{state} = ($self->{state} + 0x9e3779b97f4a7c15) & (2**64 - 1);
        $next = ($next ^ ($next >> 30)) * 0xbf58476d1ce4e5b9 & (2**64 - 1);
        $next = ($next ^ ($next >> 27)) * 0x94d049bb133111eb & (2**64 - 1);
        ($next ^ ($next >> 31)) & (2**64 - 1);
    }

    sub next_float {
        my ($self) = @_;
        $self->next_int / 2**64;
    }
}

say 'Seed: 1234567, first 5 values:';
my $rng = splitmix64->new(seed => 1234567);
say $rng->next_int for 1 .. 5;

my %h;
say "\nSeed: 987654321, values histogram:";
$rng = splitmix64->new(seed => 987654321);
$h{int 5 * $rng->next_float}++ for 1 .. 100_000;
say "$_ $h{$_}" for sort keys %h;
Output:
Seed: 1234567, first 5 values:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

Seed: 987654321, values histogram:
0 20027
1 19892
2 20073
3 19978
4 20030

Phix

As per Pseudo-random_numbers/PCG32#Phix, resorting to mpfr/gmp

with javascript_semantics
include mpfr.e
mpz state = mpz_init(),
    shift = mpz_init("0x9e3779b97f4a7c15"),
    mult1 = mpz_init("0xbf58476d1ce4e5b9"),
    mult2 = mpz_init("0x94d049bb133111eb"),
      b64 = mpz_init("0x10000000000000000"),  -- (truncate to 64 bits)
      tmp = mpz_init(),
        z = mpz_init()
 
procedure seed(integer num)
    mpz_set_si(state,num)
end procedure
 
procedure next_int()
    mpz_add(state, state, shift)    -- state += shift
    mpz_fdiv_r(state, state, b64)   -- state := remainder(z,b64) 
    mpz_set(z, state)               -- z := state
    mpz_tdiv_q_2exp(tmp, z, 30)     -- tmp := trunc(z/2^30)
    mpz_xor(z, z, tmp)              -- z := xor_bits(z,tmp)
    mpz_mul(z, z, mult1)            -- z *= mult1
    mpz_fdiv_r(z, z, b64)           -- z := remainder(z,b64) 
    mpz_tdiv_q_2exp(tmp, z, 27)     -- tmp := trunc(z/2^27)
    mpz_xor(z, z, tmp)              -- z := xor_bits(z,tmp)
    mpz_mul(z, z, mult2)            -- z *= mult2
    mpz_fdiv_r(z, z, b64)           -- z := remainder(z,b64) 
    mpz_tdiv_q_2exp(tmp, z, 31)     -- tmp := trunc(z/2^31)
    mpz_xor(z, z, tmp)              -- z := xor_bits(z,tmp)
    -- (result left in z)
end procedure
 
function next_float()
    next_int()
    mpfr f = mpfr_init_set_z(z)
    mpfr_div_z(f, f, b64)
    return mpfr_get_d(f)
end function
 
seed(1234567)
for i=1 to 5 do
    next_int()
    printf(1,"%s\n",mpz_get_str(z))
end for
seed(987654321)
sequence r = repeat(0,5)
for i=1 to 100000 do
    integer rdx = floor(next_float()*5)+1
    r[rdx] += 1
end for
?r
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
{20027,19892,20073,19978,20030}

PicoLisp

(zero *Split)     # global state

(de mod64 (N)
   (& N `(hex "FFFFFFFFFFFFFFFF")) )
(de mod64+ (A B)
   (mod64 (+ A B)) )
(de mod64* (A B)
   (mod64 (* A B)) )
(de roundf (N)    # rounds down
   (/ N (** 10 *Scl)) )
(de nextSplit ()
   (setq *Split (mod64+ *Split `(hex "9e3779b97f4a7c15")))
   (let Z *Split
      (setq
         Z (mod64* `(hex "bf58476d1ce4e5b9") (x| Z (>> 30 Z)))
         Z (mod64* `(hex "94d049bb133111eb") (x| Z (>> 27 Z))) )
      (x| Z (>> 31 Z)) ) )

(prinl "First 5 numbers:")
(setq *Split 1234567)
(do 5
   (println (nextSplit)) )

(prinl "The counts for 100,000 repetitions are:")
(scl 12)
(off R)
(setq *Split 987654321)
(do 100000
   (accu
      'R
      (roundf (* 5 (*/ (nextSplit) 1.0 18446744073709551616)))
      1 ) )
(mapc println (sort R))
Output:
First 5 numbers:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
The counts for 100,000 repetitions are:
(0 . 20027)
(1 . 19892)
(2 . 20073)
(3 . 19978)
(4 . 20030)

Python

MASK64 = (1 << 64) - 1
C1 = 0x9e3779b97f4a7c15
C2 = 0xbf58476d1ce4e5b9
C3 = 0x94d049bb133111eb



class Splitmix64():
    
    def __init__(self, seed=0):
        self.state = seed & MASK64

    def seed(self, num):
        self.state =  num & MASK64
    
    def next_int(self):
        "return random int between 0 and 2**64"
        z = self.state = (self.state + C1) & MASK64
        z = ((z ^ (z >> 30)) * C2) & MASK64
        z = ((z ^ (z >> 27)) * C3) & MASK64
        answer = (z ^ (z >> 31)) & MASK64

        return answer
    
    def  next_float(self):
        "return random float between 0 and 1"
        return self.next_int() / (1 << 64)
    

if __name__ == '__main__':
    random_gen = Splitmix64()
    random_gen.seed(1234567)
    for i in range(5):
        print(random_gen.next_int())
        
    random_gen.seed(987654321)
    hist = {i:0 for i in range(5)}
    for i in range(100_000):
        hist[int(random_gen.next_float() *5)] += 1
    print(hist)
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
{0: 20027, 1: 19892, 2: 20073, 3: 19978, 4: 20030}

Quackery

Note that Quackery does not have floating point, so this code uses rational numbers and approximates them to 16 places after the decimal point (when displayed as decimal fractions), so is at least as good as 64 bit floating point.

  [ $ "bigrat.qky" loadfile ] now!

  [ stack hex DEFACEABADFACADE ]  is state    (   --> s   )

  [ state replace ]               is seed     ( n --->    )

  [ state take
    hex 9E3779B97F4A7C15 + 64bits
    dup state put
    dup 30 >> ^
    hex BF58476D1CE4E5B9 * 64bits
    dup 27 >> ^
    hex 94D049BB133111EB * 64bits
    dup 31 >> ^ ]                 is nextint  (   --> n   )

  [ nextint
    hex 10000000000000000
    10000000000000000 round ]     is nextfrac (   --> n/d )

  1234567 seed
  5 times [ nextint echo cr ]
  cr
  987654321 seed
  ' [ 0 0 0 0 0 ]
  100000 times
    [ nextfrac 5 1 v* proper 2drop
      2dup peek 1+ unrot poke ]
  echo
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

[ 20027 19892 20073 19978 20030 ]

Raku

Works with: Rakudo version 2020.07
class splitmix64 {
    has $!state;

    submethod BUILD ( Int :$seed where * >= 0 = 1 ) { $!state = $seed }

    method next-int {
        my $next = $!state = ($!state + 0x9e3779b97f4a7c15) +& (2⁶⁴ - 1);
        $next = ($next +^ ($next +> 30)) * 0xbf58476d1ce4e5b9 +& (2⁶⁴ - 1);
        $next = ($next +^ ($next +> 27)) * 0x94d049bb133111eb +& (2⁶⁴ - 1);
        ($next +^ ($next +> 31)) +& (2⁶⁴ - 1);
    }

    method next-rat { self.next-int / 2⁶⁴ }
}

# Test next-int
say 'Seed: 1234567; first five Int values';
my $rng = splitmix64.new( :seed(1234567) );
.say for $rng.next-int xx 5;


# Test next-rat (since these are rational numbers by default)
say "\nSeed: 987654321; first 1e5 Rat values histogram";
$rng = splitmix64.new( :seed(987654321) );
say ( ($rng.next-rat * 5).floor xx 100_000 ).Bag;
Output:
Seed: 1234567; first five Int values
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

Seed: 987654321; first 1e5 Rat values histogram
Bag(0(20027) 1(19892) 2(20073) 3(19978) 4(20030))

REXX

/*REXX program  generates   pseudo─random numbers   using the  split mix 64 bit  method.*/
numeric digits 200                               /*ensure enough decimal digs for mult. */
parse arg n reps pick seed1 seed2 .              /*obtain optional arguments from the CL*/
if     n=='' |     n==","  then    n=          5 /*Not specified?  Then use the default.*/
if  reps=='' |  reps==","  then reps=     100000 /* "      "         "   "   "     "    */
if  pick=='' |  pick==","  then pick=          5 /* "      "         "   "   "     "    */
if seed1=='' | seed1==","  then seed1=   1234567 /* "      "         "   "   "     "    */
if seed2=='' | seed2==","  then seed2= 987654321 /* "      "         "   "   "     "    */
const.1= x2d( 9e3779b97f4a7c15 )                 /*initialize 1st constant to be used.  */
const.2= x2d('bf58476d1ce4e5b9')                 /*    "      2nd     "     "  "   "    */
const.3= x2d( 94d049bb133111eb )                 /*    "      3rd     "     "  "   "    */
o.30= copies(0, 30)                              /*construct  30  bits of zeros.        */
o.27= copies(0, 27)                              /*     "     27    "   "   "           */
o.31= copies(0, 31)                              /*     "     31    "   "   "           */
w= max(3, length(n) )                            /*for aligning the left side of output.*/
state= seed1                                     /*     "     the   state  to seed #1.  */
             do j=1  for n
             if j==1  then do;   say center('n', w)     "     pseudo─random number   "
                                 say copies('═', w)     " ════════════════════════════"
                           end
             say right(j':', w)" "  right(commas(next()), 27)  /*display a random number*/
             end   /*j*/
say
if reps==0  then exit 0                          /*stick a fork in it,  we're all done. */
say center('#', w)   "   count of pseudo─random #"
say copies('═', w)   " ════════════════════════════"
state= seed2                                     /*     "     the   state  to seed #2.  */
@.= 0;                         div= pick / 2**64 /*convert division to inverse multiply.*/
             do k=1  for reps
             parse value next()*div  with  _ '.' /*get random #, floor of a "division". */
             @._= @._ + 1                        /*bump the counter for this random num.*/
             end   /*k*/

             do #=0  for pick
             say right(#':', w)" "  right(commas(@.#), 15) /*show count of a random num.*/
             end   /*#*/
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg _;   do ?=length(_)-3  to 1  by -3; _= insert(',', _, ?); end;  return _
b2d:    parse arg ?; return        x2d( b2x(?) )             /*convert bin──►decimal.   */
d2b:    parse arg ?; return right( x2b( d2x(?) ),  64, 0)    /*convert dec──►64 bit bin.*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
next: procedure expose state const. o.
      state= state + const.1        ; z= d2b(state)          /*add const1──►STATE; conv.*/
      z= xor(z, left(o.30 || z, 64)); z= d2b(b2d(z)*const.2) /*shiftR 30 bits & XOR;  " */
      z= xor(z, left(o.27 || z, 64)); z= d2b(b2d(z)*const.3) /*   "   27  "   "  "    " */
      z= xor(z, left(o.31 || z, 64));        return b2d(z)   /*   "   31  "   "  "    " */
/*──────────────────────────────────────────────────────────────────────────────────────*/
xor:  parse arg a, b;                    $=                  /*perform a bit─wise  XOR. */
                do !=1  for length(a);   $= $  ||  (substr(a,!,1)  &&  substr(b,!,1) )
                end   /*!*/;      return $
output   when using the default inputs:
 n       pseudo─random number
═══  ════════════════════════════
 1:    6,457,827,717,110,365,317
 2:    3,203,168,211,198,807,973
 3:    9,817,491,932,198,370,423
 4:    4,593,380,528,125,082,431
 5:   16,408,922,859,458,223,821

 #     count of pseudo─random #
═══  ════════════════════════════
 0:           20,027
 1:           19,892
 2:           20,073
 3:           19,978
 4:           20,030

RPL

RPL offers hundreds of instructions and commands but, strangely, bitwise operations are limited to the set defined for the HP-16C RPN calculator in 1982. So, to shift an integer n times, we can either stay in low gear, shifting one bit at a time:

≪ 1 SWAP START SR NEXT
≫ 'SRn' STO

or play with the gearbox:

≪ 
  8 MOD LAST / IP ROT                          @ q, r = divmod(n,8)
  IF SWAP THEN 1 LAST START SRB NEXT END       @ q, r = divmod(n,8)
  IF SWAP THEN 1 LAST START SR NEXT END        @ shift & bit right, r times
≫ 'SRn' STO

The first version is less efficient in terms of response time, but more idiomatic because it takes much less time to type on a calculator keyboard.

Works with: HP version 28
≪ # 9E3779B97F4A7C15h 'STATE' STO+
   STATE DUP 30 SRn XOR # BF58476D1CE4E5B9h *
   DUP 27 SRn XOR # 94D049BB133111EBh *
   DUP 31 SRn XOR
≫ 'NEXTINT' STO

≪ NEXTINT B→R 2 64 ^ /
≫ 'NEXTFLOAT' STO

≪ # 1234567d 'STATE' STO
   1 5 START NEXTINT NEXT
≫ 'TASK1' STO

≪ { 5 } O CON
   1 10000 START 
      NEXTFLOAT 5 * CEIL DUP2 GET 1 + PUT NEXT
≫ 'TASK2' STO

The last task requirement takes too much time to be run on a calculator. On a emulator, we had to split it into 10 runs to not wake up the watchdog timer.

TASK1
# 987654321d 'STATE' STO
{ 5 } O CON
TASK2 TASK2 .. TASK2        @ 10 times
Output:
6: # 6457827717110365317d
5: # 3203168211198807973d
4: # 9817491932198370423d
3: # 4593380528125082431d
2: # 16408922859458223821d
1: [ 20027 19892 20073 19978 20030 ]

Ruby

class Splitmix64
  MASK64 = (1 << 64) - 1
  C1, C2, C3 = 0x9e3779b97f4a7c15, 0xbf58476d1ce4e5b9, 0x94d049bb133111eb
  
  def initialize(seed = 0) =  @state = seed & MASK64
    
  def rand_i
    z = @state = (@state + C1) & MASK64
    z = ((z ^ (z >> 30)) * C2) & MASK64
    z = ((z ^ (z >> 27)) * C3) & MASK64
    (z ^ (z >> 31)) & MASK64
  end
  
  def rand_f = rand_i.fdiv(1<<64)
    
end
    
rand_gen = Splitmix64.new(1234567)
5.times{ puts rand_gen.rand_i }
    
rand_gen = Splitmix64.new(987654321)
p 100_000.times.lazy.map{(rand_gen.rand_f * 5).floor}.tally.sort.to_h
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821
{0=>20027, 1=>19892, 2=>20073, 3=>19978, 4=>20030}

Sidef

Translation of: Perl
class Splitmix64(state) {

    define (
        mask64 = (2**64 - 1)
    )

    method next_int {
        var n = (state = ((state + 0x9e3779b97f4a7c15) & mask64))
        n = ((n ^ (n >> 30)) * 0xbf58476d1ce4e5b9 & mask64)
        n = ((n ^ (n >> 27)) * 0x94d049bb133111eb & mask64)
        (n ^ (n >> 31)) & mask64
    }

    method next_float {
        self.next_int / (mask64+1)
    }
}

say 'Seed: 1234567, first 5 values:'
var rng = Splitmix64(1234567)
5.of { rng.next_int.say }

say "\nSeed: 987654321, values histogram:"
var rng = Splitmix64(987654321)
var histogram = Bag(1e5.of { floor(5*rng.next_float) }...)
histogram.pairs.sort.each { .join(": ").say }
Output:
Seed: 1234567, first 5 values:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

Seed: 987654321, values histogram:
0: 20027
1: 19892
2: 20073
3: 19978
4: 20030

Wren

Library: Wren-big

No 64 bit integers so we use BigInt with a mask.

import "./big" for BigInt

var Const1 = BigInt.fromBaseString("9e3779b97f4a7c15", 16) 
var Const2 = BigInt.fromBaseString("bf58476d1ce4e5b9", 16) 
var Const3 = BigInt.fromBaseString("94d049bb133111eb", 16)
var Mask64 = (BigInt.one << 64) - BigInt.one

class Splitmix64 {
    construct new(state) {
        _state  = state
    }

    nextInt {
        _state = (_state + Const1) & Mask64
        var z = _state
        z = ((z ^ (z >> 30)) * Const2) & Mask64
        z = ((z ^ (z >> 27)) * Const3) & Mask64
        return (z ^ (z >> 31)) & Mask64
    }

    nextFloat { nextInt.toNum / 2.pow(64) }
}

var randomGen = Splitmix64.new(BigInt.new(1234567))
for (i in 0..4) System.print(randomGen.nextInt)

var counts = List.filled(5, 0)
randomGen = Splitmix64.new(BigInt.new(987654321))
for (i in 1..1e5) {
    var i = (randomGen.nextFloat * 5).floor
    counts[i] = counts[i] + 1
}
System.print("\nThe counts for 100,000 repetitions are:")
for (i in 0..4) System.print("  %(i) : %(counts[i])")
Output:
6457827717110365317
3203168211198807973
9817491932198370423
4593380528125082431
16408922859458223821

The counts for 100,000 repetitions are:
  0 : 20027
  1 : 19892
  2 : 20073
  3 : 19978
  4 : 20030