pixiedust 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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