pixiedust 0.0.5 → 0.0.6

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.
Files changed (4) hide show
  1. data/History.txt +3 -0
  2. data/lib/pixiedust.rb +74 -15
  3. data/test/random_dust.rb +56 -28
  4. metadata +2 -2
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ == 0.0.6 / 2008-05-16
2
+ * Sped up RandomDust some more, using bit masks for 8 and 16 bit values
3
+
1
4
  == 0.0.5 / 2008-05-14
2
5
  * Sped up RandomDust, no longer raising errors and catching, 2x faster
3
6
  * Added
data/lib/pixiedust.rb CHANGED
@@ -5,7 +5,7 @@ rescue LoadError
5
5
  end
6
6
 
7
7
  class PixieDust
8
- VERSION = '0.0.5'
8
+ VERSION = '0.0.6'
9
9
  end
10
10
 
11
11
  class Object
@@ -22,33 +22,92 @@ class RandomDust
22
22
  RandomDust.between(min, max)
23
23
  end
24
24
 
25
+ def RandomDust.bits_needed(val)
26
+ return val.to_s(2).length
27
+ end
28
+
29
+ def RandomDust.ubytes_needed(val)
30
+ bits = bits_needed(val)
31
+ bytes = bits/8
32
+ if bits%8 > 0
33
+ bytes += 1
34
+ end
35
+ return bytes
36
+ end
37
+
38
+ =begin
39
+ uint32s = bytes_allocated / 4
40
+ delta = 2**(8*val.size) - val # diff between num of uint32s and bytes needed
41
+
42
+ if delta >= 2**56 && delta < 2**64 then bytes_needed -= 7; unp="C"
43
+ elsif delta >= 2**48 && delta < 2**56 then bytes_needed -= 6; unp="S"
44
+ elsif delta >= 2**40 && delta < 2**48 then bytes_needed -= 5; unp="SC"
45
+ elsif delta >= 2**32 && delta < 2**40 then bytes_needed -= 4; unp="L"
46
+ #else unp=""
47
+ #end
48
+ #if delta >= 2**24 && delta < 2**32 then bytes_needed -= 3; unp="C"
49
+ elsif delta >= 2**24 && delta < 2**32 then bytes_needed -= 3; unp="C"
50
+ elsif delta >= 2**16 && delta < 2**24 then bytes_needed -= 2; unp="S"
51
+ elsif delta >= 2**8 && range < 2**16 then bytes_needed -= 1; unp="SC"
52
+ else unp=""
53
+ end
54
+ puts "delta is #{delta}, unp is #{unp}"
55
+ =end
56
+
25
57
  def RandomDust.between(min, max)
58
+ if min == max then raise "min == max in RandomDust.between"; end
26
59
  if min > max then raise "min > max in RandomDust.between"; end
27
60
 
28
61
  value = 0
62
+ attempts = 0
29
63
  range = max - min
30
- bytes_used = range.size
31
- uints = range.size/4
32
-
33
- delta = 2**(8*range.size) - range
64
+ byte_count = RandomDust.ubytes_needed(range)
65
+ uint32s = byte_count / 4
66
+ bytes = byte_count % 4
34
67
 
35
- if delta > 16777216 then bytes_used -= 3; unp="C"
36
- elsif delta >= 65536 && delta < 16777216 then bytes_used -= 2; unp="S"
37
- elsif delta >= 256 && range < 65536 then bytes_used -= 1; unp="SC"
38
- else unp=""
68
+ case bytes
69
+ when 3
70
+ unp = 'SC'
71
+ when 2
72
+ unp = 'S'
73
+ when 1
74
+ unp = 'C'
75
+ else
76
+ unp = ''
39
77
  end
40
78
 
41
79
  while true
42
- rand = OpenSSL::Random.random_bytes(bytes_used)
43
- parts = rand.unpack(unp + "L"*(uints))
44
- value = parts.shift
45
- parts.length.upto(uints - 1){ |x|
46
- value += parts[x] << 32*x
47
- }
80
+ attempts += 1
81
+ rand = OpenSSL::Random.random_bytes(byte_count) # XXX: grab extra to save time
82
+
83
+ # handle single byte values specially
84
+ # 255 & 0b0111_1111 # mask the highest bit
85
+ if byte_count == 1
86
+ value = rand.unpack('C').first
87
+
88
+ bits = bits_needed(range)
89
+ value = value & (2**bits - 1)
90
+ elsif byte_count == 2
91
+ value = rand.unpack('S').first
92
+
93
+ bits = bits_needed(range)
94
+ value = value & (2**bits - 1)
95
+ else
96
+ parts = rand.unpack(unp + "L"*(uint32s))
97
+ value = parts.shift
98
+ if bytes == 3
99
+ p = parts.shift
100
+ value += p << 16
101
+ end
102
+ parts.length.upto(uint32s - 1) { |x|
103
+ value += parts[x] << 32 * x
104
+ }
105
+ end
48
106
 
49
107
  value += min
50
108
 
51
109
  unless value < min || value > max
110
+ open('pixiedust-attempts.txt', "a+") { |f| f << "#{attempts} needed (#{min}-#{max} range\n" }
52
111
  return value
53
112
  end
54
113
  end
data/test/random_dust.rb CHANGED
@@ -3,40 +3,70 @@ require 'spec'
3
3
  require 'statarray'
4
4
  begin
5
5
  require 'ruby-prof'
6
- profile = false
6
+ profile = true
7
7
  rescue LoadError
8
8
  end
9
9
 
10
10
  require "#{File.dirname(__FILE__)}/../lib/pixiedust"
11
11
 
12
12
  describe RandomDust do
13
- it "Shouldn't generate numbers outside of range" do
14
- min = -5
15
- max = 15
16
- if profile then RubyProf.start; end
17
- 1000.times {
18
- val = RandomDust.between(min,max)
19
- val.should <= max
20
- val.should >= min
13
+ #it "Shouldn't generate numbers outside of range" do
14
+ # min = -5
15
+ # max = 15
16
+ # if profile then RubyProf.start; end
17
+ # 1000.times {
18
+ # val = RandomDust.between(min,max)
19
+ # val.should <= max
20
+ # val.should >= min
21
+ # }
22
+ # if profile
23
+ # profile_data = RubyProf.stop
24
+ # printer = RubyProf::FlatPrinter.new(profile_data)
25
+ # printer.print(open("profile-#{DateDust.now_str}.txt", "w+"), 0)
26
+ # end
27
+ #end
28
+
29
+ it "Should know how many bytes are really needed by a number" do
30
+ RandomDust.ubytes_needed(1).should == 1
31
+ RandomDust.ubytes_needed(255).should == 1
32
+ RandomDust.ubytes_needed(256).should == 2
33
+ RandomDust.ubytes_needed(65535).should == 2
34
+ RandomDust.ubytes_needed(65536).should == 3
35
+ RandomDust.ubytes_needed(16777215).should == 3
36
+ RandomDust.ubytes_needed(16777216).should == 4
37
+ RandomDust.ubytes_needed(18446744073709551616).should == 9
38
+
39
+ end
40
+
41
+ it "Should generate numbers at ends of range" do
42
+ min = 0
43
+ max = [1, 4, 555]
44
+
45
+ max.each { |x|
46
+ vlist = []
47
+ 2000.times {
48
+ vlist << RandomDust.between(min, x)
49
+ }
50
+ vstat = vlist.to_statarray
51
+ #puts "min: #{min}, min seen: #{vstat.min}"
52
+ #puts "max: #{x}, max seen: #{vstat.max}"
53
+ vstat.min.should == min
54
+ vstat.max.should == x
21
55
  }
22
- if profile
23
- profile_data = RubyProf.stop
24
- printer = RubyProf::FlatPrinter.new(profile_data)
25
- printer.print(open("profile-#{DateDust.now_str}.txt", "w+"), 0)
26
- end
27
56
  end
28
57
 
29
58
  it "Should generate numbers at ends of range" do
30
59
  min = -5
31
60
  max = 5
32
- hit_min = false
33
- hit_max = false
61
+ vlist = []
34
62
  100.times {
35
- if RandomDust.between(min, max) == min; then hit_min = true; end
36
- if RandomDust.between(min, max) == max; then hit_max = true; end
63
+ vlist << RandomDust.between(min, max)
37
64
  }
38
- hit_min.should == true
39
- hit_max.should == true
65
+ vstat = vlist.to_statarray
66
+ #puts "min: #{vstat.min}"
67
+ #puts "max: #{vstat.max}"
68
+ vstat.min.should == min
69
+ vstat.max.should == max
40
70
  end
41
71
 
42
72
  it "Should have roughly equal counts of numbers in range" do
@@ -52,16 +82,14 @@ describe RandomDust do
52
82
  }
53
83
 
54
84
  counts = []
55
- values.keys.each { |k|
85
+ values.keys.sort.each { |k|
56
86
  counts << values[k]
87
+ #puts "#{k}: #{values[k]}"
57
88
  }
58
89
 
59
- ca = counts.to_statarray
60
- #puts "min: #{ca.min}"
61
- #puts "max: #{ca.max}"
62
- #puts "median: #{ca.median}"
63
- #puts "stddev: #{ca.stddev}"
64
- ca.stddev.should < 100
65
-
90
+ cstat = counts.to_statarray
91
+ #puts "median: #{cstat.median}"
92
+ #puts "stddev: #{cstat.stddev}"
93
+ cstat.stddev.should < 50
66
94
  end
67
95
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pixiedust
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Wilkins
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-14 00:00:00 -07:00
12
+ date: 2008-05-16 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency