super_random 1.2.200123 → 2.0.210126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aeb192c166b91d82fb0a4b535d851a5479c5bae43bf4a37d607ae199b128d83d
4
- data.tar.gz: 6fb7ad8629f9775183a65c040dc762d48e178a761c545008028e57c0002162dd
3
+ metadata.gz: 2480d12416526288677a0a9203d7e63ae666eb70b59fc792cc1a67bc2c35978a
4
+ data.tar.gz: 52d9c2e334770c1498f6afffc1bc967db38686a1d5098882f2b1290bbf5f1e60
5
5
  SHA512:
6
- metadata.gz: c51049b6220c0c971146cd2ddc0463dcbd7037c7bc7695aca8a50653d8acd22cc2964d85f66d667ad311526f2e0a8748d0e4a2e501f9621aa64db0f524d78883
7
- data.tar.gz: f583c0095ca47c209730afa445bca8304f041d55de8c0f26e599e9cac7770bbdc3f4001e2e9fc82fe8e24ea70a5c9063d01e12e616a6708bc88dcfd71c990f8d
6
+ metadata.gz: 3dcb0cb30658abd969e2930cc60bc9d0ff33bca7b4dc7ee0e418c43ba6f9815d52c7af4c21346320ff7941422e2ff8fbbb75a3e0453c9172edf3f05e5a0609ff
7
+ data.tar.gz: c0b24a58b9a236992b615a3bad666af0d5377575dabed3fd760cc3b2857904f9bec41ed3ee0f186c11820979c0bea50cf4f52de580ff6a576d3dc253a2644f43
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SuperRandom
2
2
 
3
- * [VERSION 1.2.200123](https://github.com/carlosjhr64/super_random/releases)
3
+ * [VERSION 2.0.210126](https://github.com/carlosjhr64/super_random/releases)
4
4
  * [github](https://github.com/carlosjhr64/super_random)
5
5
  * [rubygems](https://rubygems.org/gems/super_random)
6
6
 
@@ -8,45 +8,51 @@
8
8
 
9
9
  You can't get more random than random, but you can try really, really, really hard.
10
10
 
11
- SuperRandom combines five online real random services to create a more perfect random byte.
12
-
13
- ## SYNOPSIS:
14
-
15
- require 'super_random'
16
- super_random = SuperRandom.new
17
-
18
- super_random.bytes(32) #~> ^\[\d+(, \d+){31}\]$
19
- # Example:
20
- # [142, 36, 107, 199, 1, 222, 69, 238, 130, 159, 236, 201, 199,
21
- # 33, 237, 166, 189, 166, 95, 246, 111, 103, 113, 126, 27, 31,
22
- # 244, 215, 200, 60, 255, 184]
23
-
24
- super_random.hexadecimal(32) #~> ^\h{64}$
25
- # Example:
26
- # "3e0dffe42c08b849dc3c1290e7aa87dff4ad3037b29694136786a4db1e3efab8"
27
-
28
- super_random.random_number(100.0) #~> ^\d{1,2}\.\d+$
29
- # Example:
30
- # 16.882225652425537
31
-
32
- super_random.random_number(100) #~> ^\d{1,2}$
33
- # Example:
34
- # 85
35
-
36
- # The "services" attribute gives the number of online services used.
37
- # It's possible for a service to fail.
38
- # Ultimately, SuperRandom uses SecureRandom as a failsafe.
39
- super_random.services #=> 5
11
+ SuperRandom combines three online real random services to create a more perfect random byte.
40
12
 
41
13
  ## INSTALL:
42
14
 
43
15
  $ gem install super_random
44
16
 
17
+ ## SYNOPSIS:
18
+
19
+ ```ruby
20
+ require 'super_random'
21
+ super_random = SuperRandom.new
22
+
23
+ super_random.bytes(32) #~> ^\[\d+(, \d+){31}\]$
24
+ # Example:
25
+ # [142, 36, 107, 199, 1, 222, 69, 238, 130, 159, 236, 201, 199,
26
+ # 33, 237, 166, 189, 166, 95, 246, 111, 103, 113, 126, 27, 31,
27
+ # 244, 215, 200, 60, 255, 184]
28
+
29
+ sleep 1 # rate limit to be nice
30
+ super_random.hexadecimal(32) #~> ^\h{64}$
31
+ # Example:
32
+ # "3e0dffe42c08b849dc3c1290e7aa87dff4ad3037b29694136786a4db1e3efab8"
33
+
34
+ sleep 1
35
+ super_random.random_number(100.0) #~> ^\d{1,2}\.\d+$
36
+ # Example:
37
+ # 16.882225652425537
38
+
39
+ sleep 1
40
+ super_random.random_number(100) #~> ^\d{1,2}$
41
+ # Example:
42
+ # 85
43
+
44
+ # The "services" attribute gives the number of online services used.
45
+ # It's possible for a service to fail.
46
+ # Ultimately, SuperRandom uses SecureRandom as a failsafe.
47
+ super_random.services #=> 3
48
+ super_random.randomness #=> 3.0
49
+ ```
50
+
45
51
  ## LICENSE:
46
52
 
47
53
  (The MIT License)
48
54
 
49
- Copyright (c) 2020 carlosjhr64
55
+ Copyright (c) 2021 carlosjhr64
50
56
 
51
57
  Permission is hereby granted, free of charge, to any person obtaining
52
58
  a copy of this software and associated documentation files (the
@@ -1,15 +1,15 @@
1
- class SuperRandom
2
- VERSION = '1.2.200123'
3
- end
4
-
5
1
  # Standard Libraries
6
2
  require 'timeout'
7
3
  require 'securerandom'
8
4
  require 'net/http'
9
5
  require 'json'
10
6
 
11
- # This Gem
12
- require 'super_random/super_random.rb'
7
+ class SuperRandom
8
+ VERSION = '2.0.210126'
9
+ # This Gem
10
+ require 'super_random/services'
11
+ require 'super_random/generator'
12
+ end
13
13
 
14
14
  # Requires:
15
15
  #`ruby`
@@ -0,0 +1,99 @@
1
+ class SuperRandom
2
+ DEFAULT_BYTES = 32
3
+
4
+ attr_accessor :first_timeout, :second_timeout, :nevermind
5
+ attr_reader :randomness, :services
6
+
7
+ def initialize
8
+ @first_timeout = 3
9
+ @second_timeout = 6
10
+ @nevermind = true
11
+ @randomness = 0.0
12
+ @services = 0
13
+ end
14
+
15
+ def bytes(n=DEFAULT_BYTES)
16
+ @randomness = 0.0
17
+ @services = 0
18
+
19
+ m = SuperRandom.services
20
+ a = Array.new m.length
21
+ t = Array.new m.length
22
+ m.each_with_index do |k,i|
23
+ t[i] = Thread.new{ a[i] = SuperRandom.send(k, n) }
24
+ end
25
+
26
+ begin
27
+ Timeout.timeout(@first_timeout) do
28
+ # Initially, would like to get them all.
29
+ t.each{_1.join}
30
+ end
31
+ rescue Timeout::Error
32
+ begin
33
+ Timeout.timeout(@second_timeout) do
34
+ # But at this point,
35
+ # would like to get at least one.
36
+ while a.all?{_1.nil?} and t.any?{_1.alive?}
37
+ Thread.pass
38
+ end
39
+ end
40
+ rescue Timeout::Error
41
+ # If we don't care that we got nothing, go on.
42
+ raise $! unless @nevermind
43
+ end
44
+ end
45
+
46
+ r = Array.new n
47
+ n.times{|i| r[i] = SecureRandom.random_number(256)}
48
+
49
+ a.each do |b|
50
+ next if b.nil?
51
+ l = b.length
52
+ @randomness += l.to_f/n.to_f
53
+ @services += 1
54
+ n.times{|i|r[i]=(r[i]+b[i%l])%256}
55
+ end
56
+
57
+ return r
58
+ end
59
+
60
+ def hexadecimal(n=DEFAULT_BYTES)
61
+ bytes(n).map{|i|i.to_s(16).rjust(2,'0')}.join
62
+ end
63
+
64
+ def random_number(scale=1.0, minbytes=6, maxbytes=[minbytes,DEFAULT_BYTES].max)
65
+ case scale
66
+ when Float
67
+ div = minbytes.times.inject(''){|s,i| s+'FF'}.to_i(16)
68
+ den = hexadecimal(minbytes).to_i(16)
69
+ return scale * den.to_f / div.to_f
70
+ when Integer
71
+ n = n0 = Math.log(scale, 256).ceil
72
+ e = e0 = 256**n
73
+ r = r0 = e0 % scale
74
+ while r > 0
75
+ n0 += 1
76
+ e0 *= 256
77
+ r0 = e0 % scale
78
+ if r0 <= r
79
+ # break if repeating pattern with big enough integer
80
+ break if r0 == r and n0 > minbytes
81
+ r,n,e = r0,n0,e0
82
+ end
83
+ break if n0 >= maxbytes
84
+ end
85
+ max = (e/scale)*scale
86
+ loop do
87
+ number = hexadecimal(n).to_i(16)
88
+ return number % scale if number < max
89
+ # On a relatively small chance that we're above max...
90
+ if @nevermind
91
+ warn "using SecureRandom.random_number(#{scale})"
92
+ return SecureRandom.random_number(scale)
93
+ end
94
+ end
95
+ end
96
+ raise "rand(scale Integer|Float)"
97
+ end
98
+ alias rand random_number
99
+ end
@@ -0,0 +1,45 @@
1
+ class SuperRandom
2
+ def self.services
3
+ [:quantum, :atmospheric, :hotbits]
4
+ end
5
+
6
+ # https://qrng.anu.edu.au/
7
+ # https://qrng.anu.edu.au/contact/api-documentation/
8
+ def self.quantum(n)
9
+ s = Net::HTTP.get(URI(
10
+ "https://qrng.anu.edu.au/API/jsonI.php?length=#{n}&type=uint8"))
11
+ a = JSON.parse(s)['data']
12
+ raise unless a.is_a?(Array) and a.length==n
13
+ raise unless a.all?{|i| i.is_a?(Integer) and i.between?(0,255)}
14
+ return a
15
+ rescue StandardError
16
+ warn "quantum (qrng.anu.edu.au) failed."
17
+ return nil
18
+ end
19
+
20
+ # https://www.random.org/
21
+ # https://www.random.org/integers/
22
+ def self.atmospheric(n)
23
+ s = Net::HTTP.get(URI(
24
+ "https://www.random.org/integers/?num=#{n}&min=0&max=255&col=1&base=10&format=plain&rnd=new"))
25
+ a = s.strip.split(/\s+/).map{|j|j.to_i}
26
+ raise unless a.length==n
27
+ raise unless a.all?{|i| i.between?(0,255)}
28
+ return a
29
+ rescue StandardError
30
+ warn "atmospheric (www.random.org) failed."
31
+ return nil
32
+ end
33
+
34
+ # https://www.fourmilab.ch/hotbits/
35
+ def self.hotbits(n, k='Pseudorandom')
36
+ s = Net::HTTP.get(URI(
37
+ "https://www.fourmilab.ch/cgi-bin/Hotbits.api?nbytes=#{n}&fmt=bin&apikey=#{k}"))
38
+ a = s.bytes
39
+ raise unless a.length==n
40
+ return a
41
+ rescue StandardError
42
+ warn "hotbits (www.fourmilab.ch) failed."
43
+ return nil
44
+ end
45
+ end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: super_random
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.200123
4
+ version: 2.0.210126
5
5
  platform: ruby
6
6
  authors:
7
7
  - carlosjhr64
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-23 00:00:00.000000000 Z
11
+ date: 2021-01-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  You can't get more random than random, but you can try really, really, really hard.
15
15
 
16
- SuperRandom combines five online real random services to create a more perfect random byte.
16
+ SuperRandom combines three online real random services to create a more perfect random byte.
17
17
  email: carlosjhr64@gmail.com
18
18
  executables: []
19
19
  extensions: []
@@ -21,12 +21,13 @@ extra_rdoc_files: []
21
21
  files:
22
22
  - README.md
23
23
  - lib/super_random.rb
24
- - lib/super_random/super_random.rb
24
+ - lib/super_random/generator.rb
25
+ - lib/super_random/services.rb
25
26
  homepage: https://github.com/carlosjhr64/super_random
26
27
  licenses:
27
28
  - MIT
28
29
  metadata: {}
29
- post_install_message:
30
+ post_install_message:
30
31
  rdoc_options: []
31
32
  require_paths:
32
33
  - lib
@@ -41,9 +42,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
41
42
  - !ruby/object:Gem::Version
42
43
  version: '0'
43
44
  requirements:
44
- - 'ruby: ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-linux]'
45
- rubygems_version: 3.1.2
46
- signing_key:
45
+ - 'ruby: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [x86_64-linux]'
46
+ rubygems_version: 3.2.3
47
+ signing_key:
47
48
  specification_version: 4
48
49
  summary: You can't get more random than random, but you can try really, really, really
49
50
  hard.
@@ -1,165 +0,0 @@
1
- class SuperRandom
2
- DEFAULT_BYTES = 32
3
-
4
- # http://qrng.anu.edu.au/index.php
5
- # https://qrng.anu.edu.au/API/api-demo.php
6
- def self.quantum(n)
7
- s = Net::HTTP.get(URI(
8
- "https://qrng.anu.edu.au/API/jsonI.php?length=#{n}&type=uint8"))
9
- a = JSON.parse(s)['data']
10
- raise unless a.is_a?(Array) and a.length==n
11
- raise unless a.all?{|i| i.is_a?(Integer) and i.between?(0,255)}
12
- return a
13
- rescue StandardError
14
- warn "quantum (qrng.anu.edu.au) failed."
15
- return nil
16
- end
17
-
18
- # https://www.random.org/
19
- # https://www.random.org/integers/
20
- def self.atmospheric(n)
21
- s = Net::HTTP.get(URI(
22
- "https://www.random.org/integers/?num=#{n}&min=0&max=255&col=1&base=10&format=plain&rnd=new"))
23
- a = s.strip.split(/\s+/).map{|j|j.to_i}
24
- raise unless a.length==n
25
- raise unless a.all?{|i| i.between?(0,255)}
26
- return a
27
- rescue StandardError
28
- warn "atmospheric (www.random.org) failed."
29
- return nil
30
- end
31
-
32
- # http://random.hd.org/
33
- # http://random.hd.org/getBits.jsp
34
- def self.entropy_pool(n)
35
- s = Net::HTTP.get(URI(
36
- "http://random.hd.org/getBits.jsp?numBytes=#{n}&type=bin"))
37
- a = s.bytes
38
- # As of the time of this writting, not guaranteed to get more than 8 bytes.
39
- raise unless (n>8)? a.length.between?(8,n) : a.length==n
40
- return a
41
- rescue StandardError
42
- warn "entropy_pool (random.hd.org) failed."
43
- return nil
44
- end
45
-
46
- # https://www.fourmilab.ch/hotbits/
47
- def self.hotbits(n, k='Pseudorandom')
48
- s = Net::HTTP.get(URI(
49
- "https://www.fourmilab.ch/cgi-bin/Hotbits.api?nbytes=#{n}&fmt=bin&apikey=#{k}"))
50
- a = s.bytes
51
- raise unless a.length==n
52
- return a
53
- rescue StandardError
54
- warn "hotbits (www.fourmilab.ch) failed."
55
- return nil
56
- end
57
-
58
- # http://www.randomnumbers.info
59
- def self.quantis(n)
60
- s = Net::HTTP.get(URI(
61
- "http://www.randomnumbers.info/cgibin/wqrng.cgi?amount=#{n}&limit=255"))
62
- a = s.scan( /\s\d\d?\d?\b/ ).map{|i| i.to_i}
63
- raise unless a.length == n and a.all?{|i| i.between?(0,255)}
64
- return a
65
- rescue
66
- warn "quantis (www.randomnumbers.info) failed."
67
- return nil
68
- end
69
-
70
- attr_accessor :first_timeout, :second_timeout, :nevermind
71
- attr_reader :randomness, :services
72
-
73
- def initialize
74
- @first_timeout = 3
75
- @second_timeout = 6
76
- @nevermind = true
77
- @randomness = 0.0
78
- @services = 0
79
- end
80
-
81
- def bytes(n=DEFAULT_BYTES)
82
- @randomness = 0.0
83
- @services = 0
84
-
85
- a1 = a2 = a3 = a4 = a5 = nil
86
-
87
- t1 = Thread.new{ a1 = SuperRandom.quantum(n)}
88
- t2 = Thread.new{ a2 = SuperRandom.atmospheric(n)}
89
- t3 = Thread.new{ a3 = SuperRandom.entropy_pool(n)}
90
- t4 = Thread.new{ a4 = SuperRandom.hotbits(n)}
91
- t5 = Thread.new{ a5 = SuperRandom.quantis(n)}
92
-
93
- begin
94
- Timeout.timeout(@first_timeout) do
95
- # Initially, would like to get them all.
96
- t1.join and t2.join and t3.join and t4.join and t5.join
97
- end
98
- rescue Timeout::Error
99
- begin
100
- Timeout.timeout(@second_timeout) do
101
- # But at this point,
102
- # would like to get at least one.
103
- while [a1,a2,a3,a4,a5].all?{|a|a.nil?} and [t1,t2,t3,t4,t5].any?{|t|t.alive?}
104
- Thread.pass
105
- end
106
- end
107
- rescue Timeout::Error
108
- # If we don't care that we got nothing, go on.
109
- raise $! unless @nevermind
110
- end
111
- end
112
-
113
- a = n.times.inject([]){|b,i|b.push(SecureRandom.random_number(256))}
114
- [a1, a2, a3, a4, a5].each do |b|
115
- if b
116
- bl = b.length
117
- @randomness += bl.to_f/n.to_f
118
- @services += 1
119
- n.times{|i|a[i]=(a[i]+b[i%bl])%256}
120
- end
121
- end
122
-
123
- return a
124
- end
125
-
126
- def hexadecimal(n=DEFAULT_BYTES)
127
- bytes(n).map{|i|i.to_s(16).rjust(2,'0')}.join
128
- end
129
-
130
- def random_number(scale=1.0, minbytes=6, maxbytes=[minbytes,DEFAULT_BYTES].max)
131
- case scale
132
- when Float
133
- div = minbytes.times.inject(''){|s,i| s+'FF'}.to_i(16)
134
- den = hexadecimal(minbytes).to_i(16)
135
- return scale * den.to_f / div.to_f
136
- when Integer
137
- n = n0 = Math.log(scale, 256).ceil
138
- e = e0 = 256**n
139
- r = r0 = e0 % scale
140
- while r > 0
141
- n0 += 1
142
- e0 *= 256
143
- r0 = e0 % scale
144
- if r0 <= r
145
- # break if repeating pattern with big enough integer
146
- break if r0 == r and n0 > minbytes
147
- r,n,e = r0,n0,e0
148
- end
149
- break if n0 >= maxbytes
150
- end
151
- max = (e/scale)*scale
152
- loop do
153
- number = hexadecimal(n).to_i(16)
154
- return number % scale if number < max
155
- # On a relatively small chance that we're above max...
156
- if @nevermind
157
- warn "using SecureRandom.random_number(#{scale})"
158
- return SecureRandom.random_number(scale)
159
- end
160
- end
161
- end
162
- raise "rand(scale Integer|Float)"
163
- end
164
- alias rand random_number
165
- end