/
isaac.cr
92 lines (77 loc) · 2.21 KB
/
isaac.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
require "random/secure"
# (c) Bob Jenkins, March 1996, Public Domain
# You may use this code in any way you wish, and it is free. No warrantee.
# http://burtleburtle.net/bob/rand/isaacafa.html
class Random::ISAAC
include Random
private getter counter
private getter aa
private getter bb
private getter cc
private def random_seeds
Random::Secure.rand(StaticArray(UInt32, 8))
end
def initialize(seeds = random_seeds)
@rsl = StaticArray(UInt32, 256).new { 0_u32 }
@mm = StaticArray(UInt32, 256).new { 0_u32 }
@counter = 0
@aa = @bb = @cc = 0_u32
init_by_array(seeds)
end
def new_seed(seeds = random_seeds)
@aa = @bb = @cc = 0_u32
init_by_array(seeds)
end
def next_u
if (@counter -= 1) == -1
isaac
@counter = 255
end
@rsl[counter]
end
private def isaac
@cc &+= 1
@bb &+= cc
256.times do |i|
@aa ^= case i % 4
when 0 then aa << 13
when 1 then aa >> 6
when 2 then aa << 2
else aa >> 16
end
x = @mm[i]
@aa = @mm[(i + 128) % 256] &+ aa
@mm[i] = y = @mm[(x >> 2) % 256] &+ aa &+ bb
@rsl[i] = @bb = @mm[(y >> 10) % 256] &+ x
end
end
private def init_by_array(seeds)
seed_len = seeds.size
256.times { |i| @rsl[i] = i < seed_len ? seeds[i].to_u32 : 0_u32 }
a = b = c = d = e = f = g = h = 0x9e3779b9_u32
mix = ->{
a ^= b << 11; d &+= a; b &+= c
b ^= c >> 2; e &+= b; c &+= d
c ^= d << 8; f &+= c; d &+= e
d ^= e >> 16; g &+= d; e &+= f
e ^= f << 10; h &+= e; f &+= g
f ^= g >> 4; a &+= f; g &+= h
g ^= h << 8; b &+= g; h &+= a
h ^= a >> 9; c &+= h; a &+= b
}
4.times(&mix)
scramble = ->(seed : StaticArray(UInt32, 256)) {
0.step(to: 255, by: 8) do |i|
a &+= seed[i]; b &+= seed[i + 1]; c &+= seed[i + 2]; d &+= seed[i + 3]
e &+= seed[i + 4]; f &+= seed[i + 5]; g &+= seed[i + 6]; h &+= seed[i + 7]
mix.call
@mm[i] = a; @mm[i + 1] = b; @mm[i + 2] = c; @mm[i + 3] = d
@mm[i + 4] = e; @mm[i + 5] = f; @mm[i + 6] = g; @mm[i + 7] = h
end
}
scramble.call(@rsl)
scramble.call(@mm)
isaac
@counter = 256
end
end