|
import Foundation |
|
|
|
/// Pseudo-Random Number generator utilizing xoroshiro128+ algorithm |
|
/// translated from: http://xoroshiro.di.unimi.it/xoroshiro128plus.c |
|
|
|
/// Example: |
|
/* |
|
var myRNG = RNG() |
|
var x = myRNG.getRandomNumber(0,100) |
|
var r = RNG(seed: 2345) // Seed is the same as {s} below |
|
var s = RNG(seed: 2345) // Seed is the same as {r} above |
|
var t = RNG(seed: 3456) // Seed is different from {s} and {r} |
|
var sameSeedR = r.getRandomNumber() |
|
var sameSeedS = s.getRandomNumber() // sameSeedS == sameSeedR |
|
var diffSeedT = t.getRandomNumber() // totally different |
|
*/ |
|
|
|
/// Main Pseudo-Random Number Generator |
|
public struct RNG { |
|
var seed: UInt64 |
|
var rngState: [UInt64] = [0, 0] |
|
var generator: Xoroshiro128Plus |
|
|
|
public init(seed: UInt64 = UInt64(NSDate().timeIntervalSinceReferenceDate)) { |
|
self.seed = seed |
|
self.generator = Xoroshiro128Plus(state: [0, 0]) |
|
generateSeeds(seed) |
|
self.generator.state = rngState |
|
getRandomNumber() |
|
} |
|
|
|
private mutating func generateSeeds(seed: UInt64){ |
|
var seeder = SplitMix64(state: seed) |
|
var statePart: UInt64 |
|
|
|
for x in 0...10 { |
|
statePart = seeder.nextSeed() |
|
rngState[0] = x == 9 ? statePart : 0 |
|
rngState[1] = x == 10 ? statePart : 0 |
|
} |
|
} |
|
|
|
/// Retrieves a random number with an optional range |
|
/// - parameter min: (OPTIONAL) defines minimum value for a range that return value should be within |
|
/// - parameter max: (OPTIONAL) defines maximum value for a range that return value should be within |
|
/// - returns: returns the next random UInt64 in the sequence |
|
public mutating func getRandomNumber(min: UInt64 = 0, max: UInt64 = UInt64.max - 1) -> UInt64 { |
|
return generator.next() % (max - min + 1) + min |
|
} |
|
} |
|
|
|
/// Main algorithm for generating pseudo-random numbers |
|
internal struct Xoroshiro128Plus { |
|
|
|
var state: [UInt64] |
|
|
|
func rotateLeft(a: UInt64, b: UInt64) -> UInt64 { |
|
return (a << b) | (a >> (64 - b)) |
|
} |
|
|
|
mutating func next() -> UInt64 { |
|
let s0: UInt64 = state[0] |
|
var s1 = state[1] |
|
let result: UInt64 = s0 &+ s1 |
|
|
|
s1 ^= s0 |
|
state[0] = rotateLeft(s0, b: 55) ^ s1 ^ (s1 << 14) |
|
state[1] = rotateLeft(s1, b: 36) |
|
|
|
return result |
|
} |
|
|
|
} |
|
|
|
/// Creates seed values to be used in Xoroshiro128Plus algorithm |
|
internal struct SplitMix64 { |
|
|
|
var state: UInt64 |
|
|
|
mutating func nextSeed() -> UInt64 { |
|
var b: UInt64 = state &+ 0x9E3779B97F4A7C15 |
|
b = (b ^ (b >> 30)) ^ 0xBF58476D1CE4E5B9 |
|
b = (b ^ (b >> 27)) ^ 0x94D049BB133111EB |
|
state = b ^ (b >> 31) |
|
return state |
|
} |
|
} |