Wrapping up my collection on the, xoshiro/xoroshiro algorithms, on this article I current a bundle to return 32-bit values utilizing the next variants.
- xoroshiro64*
- xoroshiro64**
- xoshiro128+
- xoshiro128++
- xoshiro128**
The numbers within the names consult with the scale of the state array that every technique operates on. The 64s function on two 32-bit values, whereas the 128s function on 4 32-bit values.
Utilizing every of them follows the identical sample:
- Select the algorithm mode (the bundle defaults to xoshiro64*)
- Seed the state array
- Retrieve the subsequent quantity from the chosen algorithm as many occasions as wanted
For instance…
SQL> BEGIN 2 xoshiro32.set_mode('xoroshiro64**'); 3 xoshiro32.set_state(xoshiro32.seed_tab(12345, 67890)); 4 5 FOR i IN 0 .. 9 6 LOOP 7 DBMS_OUTPUT.put_line(i || ' ' || LPAD(xoshiro32.get_next, 10)); 8 END LOOP; 9 END; 10 / 0 3157960260 1 4142509522 2 1831851427 3 506054173 4 2910589752 5 1819521659 6 3282141937 7 2257682835 8 2133372007 9 3757018772
Whereas all the 64-bit variants supported soar and long_jump features, solely these with 128-bit state arrays help them within the 32-bit variants. within the state by simulating many (from 2^64 to 2^768 relying on the mode and soar dimension) calls of the get_next operate. As famous by Blackman and Vigna, these are sometimes used to generate non-overlapping sequences for parallel processing. In single threaded use there can be no want for them.
The 64-bit variants included splitmix64, which I ported from Vigna’s authentic c implementation. No corresponding technique was offered for the 32-bit variants, so I went again to the authentic SplitMix paper by Steele, Lea, and Flood. Mirroring the logic of splitmix64, I additionally used a 32-bit model of the MurmurHash3 by Appleby to calculate the Golden Gamma.
SQL> BEGIN 2 xoshiro32.splitmix32_set_state(12345); 3 4 FOR i IN 0 .. 9 5 LOOP 6 DBMS_OUTPUT.put_line(i || ' ' || LPAD(xoshiro32.splitmix32_next, 10)); 7 END LOOP; 8 END; 9 / 0 1200724404 1 818072533 2 996137225 3 2397394836 4 4079075752 5 2274189806 6 2795887828 7 4161515127 8 3291005408 9 722528451
Simply as with splitmix64, utilizing splitmix32 on to generate pseudorandom numbers is just not the standard utilization . Fairly, splitmix32 is greatest used as a way of seeding the state arrays of the opposite algorithms. Beneath, I set the splitmix32 state, after which create a seed of 4 values for the xoshiro128** generator.
SQL> DECLARE 2 v_seed xoshiro32.seed_tab := xoshiro32.seed_tab(); 3 BEGIN 4 xoshiro32.splitmix32_set_state(12345); 5 v_seed.EXTEND(4); 6 7 FOR i IN 1 .. 4 8 LOOP 9 v_seed(i) := xoshiro32.splitmix32_next; 10 END LOOP; 11 12 xoshiro32.set_mode('xoshiro128**'); 13 xoshiro32.set_state(v_seed); 14 15 FOR i IN 0 .. 9 16 LOOP 17 DBMS_OUTPUT.put_line(i || ' ' || LPAD(xoshiro32.get_next, 10)); 18 END LOOP; 19 END; 20 / 0 518667457 1 440444462 2 4232892992 3 3757857622 4 3939018813 5 1334683535 6 3795058715 7 2092637810 8 2829112157 9 779180383
Following is the entire bundle for all the 32-bit algorithms.
CREATE OR REPLACE PACKAGE xoshiro32 IS -- Primarily based on xoshiro/xoroshiro household of pseudorandom quantity turbines -- by by David Blackman and Sebastiano Vigna -- authentic https://prng.di.unimi.it -- .///. -- (0 o) ---------------0000--(_)--0000--------------- -- -- Sean D. Stuber -- sean.stuber@gmail.com -- -- oooO Oooo --------------( )-----( )--------------- -- ( ) / -- _) (_/ -- It might be good to outline an express RANGE 0..4294967295 for the subtype -- however as of 23ai, solely subtypes of PLS_INTEGER might have a spread. SUBTYPE uint32_t IS NUMBER(10, 0); TYPE seed_tab IS TABLE OF uint32_t; SUBTYPE bits_t IS SIMPLE_INTEGER RANGE 0 .. 31; SUBTYPE mode_t IS SIMPLE_INTEGER RANGE 13 .. 17; -- These don't help soar and long_jump operations c_64star CONSTANT mode_t := 13; -- xoroshiro64* c_64starstar CONSTANT mode_t := 14; -- xoroshiro64** -- These do help soar and long_jump operations c_128plus CONSTANT mode_t := 15; -- xoshiro128+ c_128plusplus CONSTANT mode_t := 16; -- xoshiro128++ c_128starstar CONSTANT mode_t := 17; -- xoshiro128** --Exceptions invalid_mode EXCEPTION; -- Choose a mode by numeric worth or by identify PROCEDURE set_mode(p_mode IN mode_t); PROCEDURE set_mode(p_mode IN VARCHAR2); -- Return the present mode quantity or identify FUNCTION get_mode RETURN mode_t; FUNCTION get_mode_name RETURN VARCHAR2; -- Given a set of seed values, initialize the state array for the present mode dimension -- Totally different modes for a specific dimension can share the identical state array. PROCEDURE set_state(p_seed seed_tab); -- Return the subsequent pseudorandom generator worth for the present mode FUNCTION get_next RETURN uint32_t; -- Simulate many calls to "get_next". -- Sometimes used to create non-overlapping sequences from the identical preliminary state. -- xoroshiro64* and xoroshiro64** don't help soar or long_jump operations PROCEDURE soar; -- Simulate much more calls to "get_next". --Additionally used to create non-overlapping sequences from the identical preliminary state. -- xoroshiro64* and xoroshiro64** don't help soar or long_jump operations PROCEDURE long_jump; -- SplitMix32 is not a part of the xoshiro/xoroshiro household -- Nonetheless utilizing a operate prefer it to fill the state arrays as an alternative of a easy -- integer is advisable by the unique authors. PROCEDURE splitmix32_set_state(p_seed uint32_t); FUNCTION splitmix32_next RETURN uint32_t; END; /
As a comparability reference for myself and readers, I included snippets of the unique c code throughout the feedback of the bundle physique for the varied procedures and features of every mode.
CREATE OR REPLACE PACKAGE BODY xoshiro32 IS c_32bitmask CONSTANT INTEGER := POWER(2, 32) - 1; SUBTYPE array_64index_t IS PLS_INTEGER RANGE 0 .. 1; SUBTYPE array_128index_t IS PLS_INTEGER RANGE 0 .. 3; TYPE state64_array_t IS TABLE OF uint32_t INDEX BY array_64index_t; TYPE state128_array_t IS TABLE OF uint32_t INDEX BY array_128index_t; g_mode mode_t := c_64star; g_splitmix32_state uint32_t; g_64_state state64_array_t; g_128_state state128_array_t; FUNCTION bitor32(p_a IN uint32_t, p_b IN uint32_t) RETURN uint32_t IS BEGIN RETURN BITAND(p_a + p_b - BITAND(p_a, p_b), c_32bitmask); END bitor32; FUNCTION bitxor32(p_a IN uint32_t, p_b IN uint32_t) RETURN uint32_t IS BEGIN RETURN BITAND((p_a + p_b) - (BITAND(p_a, p_b) * 2), c_32bitmask); END bitxor32; FUNCTION shl32(p_n IN uint32_t, p_bits IN bits_t) RETURN uint32_t IS BEGIN RETURN CASE p_bits WHEN 0 THEN p_n ELSE BITAND(p_n * (2 ** p_bits), c_32bitmask) END; END shl32; FUNCTION shr32(p_n IN uint32_t, p_bits IN bits_t) RETURN uint32_t IS BEGIN RETURN CASE p_bits WHEN 0 THEN p_n ELSE BITAND(FLOOR(p_n / (2 ** p_bits)), c_32bitmask) END; END shr32; FUNCTION rotl32(p_x uint32_t, p_k IN bits_t) RETURN uint32_t IS -- static inline uint32_t rotl(const uint32_t x, int okay) { -- return (x > (32 - okay)); -- } BEGIN RETURN CASE WHEN p_k = 0 THEN p_x ELSE bitor32(shl32(p_x, p_k), shr32(p_x, 32 - p_k)) END; END rotl32; ----------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------- FUNCTION next64 RETURN uint32_t IS -- uint32_t subsequent(void) { -- const uint32_t s0 = s[0]; -- uint32_t s1 = s[1]; -- const uint32_t outcome = s0 * 0x9E3779BB; -- xoroshiro64* -- const uint32_t outcome = rotl(s0 * 0x9E3779BB, 5) * 5; -- xoroshiro64** -- -- s1 ^= s0; -- s[0] = rotl(s0, 26) ^ s1 ^ (s1 TO_NUMBER('8764000b', 'xxxxxxxx'), 1 => TO_NUMBER('f542d2d3', 'xxxxxxxx'), 2 => TO_NUMBER('6fa035c3', 'xxxxxxxx'), 3 => TO_NUMBER('77f2db5b', 'xxxxxxxx')); v_s0 uint32_t := 0; v_s1 uint32_t := 0; v_s2 uint32_t := 0; v_s3 uint32_t := 0; v_dummy uint32_t; BEGIN CASE WHEN g_mode IN (c_128plus, c_128plusplus, c_128starstar) THEN FOR i IN 0 .. 3 LOOP FOR b IN 0 .. 31 LOOP IF BITAND(v_jump(i), shl32(1, b)) != 0 THEN v_s0 := bitxor32(v_s0, g_128_state(0)); v_s1 := bitxor32(v_s1, g_128_state(1)); v_s2 := bitxor32(v_s2, g_128_state(2)); v_s3 := bitxor32(v_s3, g_128_state(3)); END IF; v_dummy := next128; END LOOP; END LOOP; g_128_state(0) := v_s0; g_128_state(1) := v_s1; g_128_state(2) := v_s2; g_128_state(3) := v_s3; ELSE -- 64-bit state modes do not help soar and long_jump operations RAISE invalid_mode; END CASE; END soar; PROCEDURE long_jump IS -- /* That is the long-jump operate for the generator. It's equal to -- 2^96 calls to subsequent(); it may be used to generate 2^32 beginning factors, -- from every of which soar() will generate 2^32 non-overlapping -- subsequences for parallel distributed computations. */ -- -- void long_jump(void) { -- static const uint32_t LONG_JUMP[] = { 0xb523952e, 0x0b6f099f, 0xccf5a0ef, 0x1c580662 }; -- -- uint32_t s0 = 0; -- uint32_t s1 = 0; -- uint32_t s2 = 0; -- uint32_t s3 = 0; -- for(int i = 0; i TO_NUMBER('b523952e', 'xxxxxxxx'), 1 => TO_NUMBER('0b6f099f', 'xxxxxxxx'), 2 => TO_NUMBER('ccf5a0ef', 'xxxxxxxx'), 3 => TO_NUMBER('1c580662', 'xxxxxxxx')); v_s0 uint32_t := 0; v_s1 uint32_t := 0; v_s2 uint32_t := 0; v_s3 uint32_t := 0; v_dummy uint32_t; BEGIN CASE WHEN g_mode IN (c_128plus, c_128plusplus, c_128starstar) THEN FOR i IN 0 .. 3 LOOP FOR b IN 0 .. 31 LOOP IF BITAND(v_jump(i), shl32(1, b)) != 0 THEN v_s0 := bitxor32(v_s0, g_128_state(0)); v_s1 := bitxor32(v_s1, g_128_state(1)); v_s2 := bitxor32(v_s2, g_128_state(2)); v_s3 := bitxor32(v_s3, g_128_state(3)); END IF; v_dummy := next128; END LOOP; END LOOP; g_128_state(0) := v_s0; g_128_state(1) := v_s1; g_128_state(2) := v_s2; g_128_state(3) := v_s3; ELSE -- 64-bit state modes do not help soar and long_jump operations RAISE invalid_mode; END CASE; END long_jump; ----------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------- PROCEDURE set_mode(p_mode IN mode_t) IS BEGIN g_mode := p_mode; END set_mode; PROCEDURE set_mode(p_mode IN VARCHAR2) IS BEGIN set_mode( CASE LOWER(p_mode) WHEN 'xoroshiro64*' THEN c_64star WHEN 'xoroshiro64**' THEN c_64starstar WHEN 'xoshiro128+' THEN c_128plus WHEN 'xoshiro128++' THEN c_128plusplus WHEN 'xoshiro128**' THEN c_128starstar END); END set_mode; FUNCTION get_mode RETURN mode_t IS BEGIN RETURN g_mode; END get_mode; FUNCTION get_mode_name RETURN VARCHAR2 IS BEGIN RETURN CASE g_mode WHEN c_64star THEN 'xoroshiro64*' WHEN c_64starstar THEN 'xoroshiro64**' WHEN c_128plus THEN 'xoshiro128+' WHEN c_128plusplus THEN 'xoshiro128++' WHEN c_128starstar THEN 'xoshiro128**' END; END get_mode_name; FUNCTION get_next RETURN uint32_t IS BEGIN RETURN CASE WHEN g_mode IN (c_64star, c_64starstar) THEN next64 WHEN g_mode IN (c_128plus, c_128plusplus, c_128starstar) THEN next128 END; END get_next; PROCEDURE set_state(p_seed IN seed_tab) IS BEGIN CASE WHEN g_mode IN (c_64star, c_64starstar) THEN g_64_state := state64_array_t(); FOR i IN 0 .. 1 LOOP g_64_state(i) := p_seed(i + 1); END LOOP; WHEN g_mode IN (c_128plus, c_128plusplus, c_128starstar) THEN g_128_state := state128_array_t(); FOR i IN 0 .. 3 LOOP g_128_state(i) := p_seed(i + 1); END LOOP; ELSE RAISE invalid_mode; END CASE; END set_state; ----------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------- PROCEDURE splitmix32_set_state(p_seed uint32_t) IS BEGIN g_splitmix32_state := p_seed; END splitmix32_set_state; FUNCTION splitmix32_next RETURN uint32_t IS -- splitmix32 is applied utilizing MurmurHash3 -- with the additional step of including the "Golden Gamma" -- This modification is base on the SplitMix algorithm described by -- Man L. Steele Jr., Doug Lea, and Christine H. Flood -- https://gee.cs.oswego.edu/dl/papers/oopsla14.pdf -- -- The MurmurHash3 algorithm by Austin Appleby will be discovered right here. -- https://github.com/aappleby/smhasher/blob/grasp/src/MurmurHash3.cpp -- -- The Golden Gamma is the closest odd quantity to 2^32/golden_ratio -- The place the golden ratio is (1+sqrt(5))/2 c_golden_gamma CONSTANT INTEGER := 2654435769; -- 0x9e3779b9 v_z NUMBER; BEGIN -- In Appleby's code, the state is handed in as a parameter. -- On this bundle, the state is maintained as a bundle variable. g_splitmix32_state := BITAND(g_splitmix32_state + c_golden_gamma, c_32bitmask); v_z := g_splitmix32_state; v_z := bitxor32(v_z, shr32(v_z, 16)); v_z := v_z * TO_NUMBER('85ebca6b', 'xxxxxxxx'); v_z := BITAND(v_z, c_32bitmask); v_z := bitxor32(v_z, shr32(v_z, 13)); v_z := v_z * TO_NUMBER('c2b2ae35', 'xxxxxxxx'); v_z := BITAND(v_z, c_32bitmask); RETURN BITAND(bitxor32(v_z, shr32(v_z, 16)), c_32bitmask); END splitmix32_next; END; /
As a result of the seed numbers and intermediate outcomes are smaller, these algorithm variants can use the native BITAND operate and don’t requite the BIG_BITAND operate for very massive values.
This concludes the collection and I hope you discover the code fascinating and helpful. As talked about in my 64-bit article, I deliberately break up the 32-bit and 64-bit algorithms into distinct packages on the belief they might be unlikely for use collectively. However, in case you discover you do need them to be mixed, I outlined the mode constants such that they’re mutually unique thus might be mixed with out concern of ambiguity from overlapping values.
Questions and feedback, as all the time, are welcome.