weightedpicker 0.1.1 → 0.1.2
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.
- data/CHANGES +7 -1
- data/Gemfile +5 -4
- data/Rakefile +39 -31
- data/VERSION +1 -1
- data/lib/weightedpicker.rb +117 -112
- data/lib/weightedpicker/tree.rb +95 -91
- data/{spec → test}/a-1b256.yaml +0 -0
- data/{spec → test}/a256b0.yaml +0 -0
- data/{spec → test}/a256b1.yaml +0 -0
- data/{spec → test}/a256b128.yaml +0 -0
- data/{spec → test}/a512b64.yaml +0 -0
- data/{spec → test}/a99999b64.yaml +0 -0
- data/{spec → test}/float.yaml +0 -0
- data/test/helper.rb +17 -0
- data/test/test_tree.rb +120 -0
- data/test/test_weightedpicker.rb +239 -0
- metadata +56 -32
- data/spec/spec_helper.rb +0 -12
- data/spec/tree_spec.rb +0 -123
- data/spec/weightedpicker_spec.rb +0 -238
- data/weightedpicker.gemspec +0 -74
data/spec/spec_helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
-
require 'rspec'
|
4
|
-
require 'weightedpicker'
|
5
|
-
|
6
|
-
# Requires supporting files with custom matchers and macros, etc,
|
7
|
-
# in ./support/ and its subdirectories.
|
8
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
-
|
10
|
-
RSpec.configure do |config|
|
11
|
-
|
12
|
-
end
|
data/spec/tree_spec.rb
DELETED
@@ -1,123 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
require "stringio"
|
3
|
-
require "fileutils"
|
4
|
-
|
5
|
-
class WeightedPicker::Tree
|
6
|
-
public :log2_ceil
|
7
|
-
public :depth
|
8
|
-
public :choose
|
9
|
-
public :index
|
10
|
-
public :add_ancestors
|
11
|
-
attr_reader :weights
|
12
|
-
end
|
13
|
-
|
14
|
-
describe "Weightedpicker::Tree" do
|
15
|
-
before do
|
16
|
-
@tree00 = WeightedPicker::Tree.new({"A" => 2, "B" => 1, "C" => 1})
|
17
|
-
@tree01 = WeightedPicker::Tree.new({"A" => 0})
|
18
|
-
@tree02 = WeightedPicker::Tree.new({})
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should do in initialize" do
|
22
|
-
#@tree00
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should return a hash of names_weights" do
|
26
|
-
#@tree00 = WeightedPicker::Tree.new({"A" => 2, "B" => 1, "C" => 1})
|
27
|
-
@tree00.names_weights.should == {"A" => 2, "B" => 1, "C" => 1}
|
28
|
-
end
|
29
|
-
|
30
|
-
it "should pick" do
|
31
|
-
results = {"A" => 0, "B" => 0, "C" => 0}
|
32
|
-
srand(0)
|
33
|
-
300.times do |i|
|
34
|
-
results[@tree00.pick] += 1
|
35
|
-
end
|
36
|
-
#pp results #=> {"A"=> 52, "B"=>76, "C"=>72}
|
37
|
-
results["A"].should be_within(15).of(150)
|
38
|
-
results["B"].should be_within( 8).of( 75)
|
39
|
-
results["C"].should be_within( 8).of( 75)
|
40
|
-
|
41
|
-
lambda{ @tree01.pick}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
42
|
-
lambda{ @tree02.pick}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "should weigh item" do
|
46
|
-
@tree00.weigh "A"
|
47
|
-
@tree00.weights.should == [
|
48
|
-
[6],
|
49
|
-
[5,1],
|
50
|
-
[4,1,1,0],
|
51
|
-
]
|
52
|
-
@tree00.names_weights.should == {"A" => 4, "B" => 1, "C" => 1}
|
53
|
-
lambda{ @tree00.weigh("D")}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should weigh item, but be limited by MAX" do
|
57
|
-
tree10 = WeightedPicker::Tree.new({"A" => 60000, "B" => 1, "C" => 1})
|
58
|
-
|
59
|
-
tree10.weigh "A"
|
60
|
-
tree10.weights.should == [
|
61
|
-
[65538],
|
62
|
-
[65537,1],
|
63
|
-
[65536,1,1,0],
|
64
|
-
]
|
65
|
-
tree10.names_weights.should == {"A" => 65536, "B" => 1, "C" => 1}
|
66
|
-
end
|
67
|
-
|
68
|
-
it "should lighten item" do
|
69
|
-
@tree00.lighten "A"
|
70
|
-
@tree00.weights.should == [
|
71
|
-
[3],
|
72
|
-
[2,1],
|
73
|
-
[1,1,1,0],
|
74
|
-
]
|
75
|
-
@tree00.names_weights.should == {"A" => 1, "B" => 1, "C" => 1}
|
76
|
-
lambda{ @tree00.lighten("D")}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
77
|
-
end
|
78
|
-
|
79
|
-
it "should lighten item, but be limited by MIN" do
|
80
|
-
@tree00.lighten "B"
|
81
|
-
@tree00.weights.should == [
|
82
|
-
[4],
|
83
|
-
[3,1],
|
84
|
-
[2,1,1,0],
|
85
|
-
]
|
86
|
-
@tree00.names_weights.should == {"A" => 2, "B" => 1, "C" => 1}
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
it "should add_ancestors" do
|
91
|
-
@tree00.add_ancestors(1,10)
|
92
|
-
@tree00.weights.should == [
|
93
|
-
[14],
|
94
|
-
[13,1],
|
95
|
-
[2,11,1,0],
|
96
|
-
]
|
97
|
-
end
|
98
|
-
|
99
|
-
it "should log2_ceil" do
|
100
|
-
#lambda{ @tree00.log2_ceil(0)}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
101
|
-
@tree00.log2_ceil( 1).should == 0
|
102
|
-
@tree00.log2_ceil( 2).should == 1
|
103
|
-
@tree00.log2_ceil( 3).should == 2
|
104
|
-
@tree00.log2_ceil( 4).should == 2
|
105
|
-
@tree00.log2_ceil( 8).should == 3
|
106
|
-
@tree00.log2_ceil(16).should == 4
|
107
|
-
@tree00.log2_ceil(20).should == 5
|
108
|
-
end
|
109
|
-
|
110
|
-
#it "should choose" do
|
111
|
-
# @tree00.choose(1,2).should == 0
|
112
|
-
# @tree00.choose(1,2).should == 1
|
113
|
-
# @tree00.choose(1,2).should == 1
|
114
|
-
#end
|
115
|
-
|
116
|
-
it "should get index" do
|
117
|
-
@tree00.index("A").should == 0
|
118
|
-
@tree00.index("B").should == 1
|
119
|
-
#lambda{ @tree00.find("C")}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
data/spec/weightedpicker_spec.rb
DELETED
@@ -1,238 +0,0 @@
|
|
1
|
-
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
-
require "stringio"
|
3
|
-
require "fileutils"
|
4
|
-
|
5
|
-
class WeightedPicker
|
6
|
-
attr_accessor :weights
|
7
|
-
public :merge
|
8
|
-
end
|
9
|
-
|
10
|
-
AB_YAML = "spec/a256b128.yaml"
|
11
|
-
NOT_EXIST_FILE = "not_exist_file"
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
describe "Weightedpicker" do
|
16
|
-
before do
|
17
|
-
@wp00 = WeightedPicker.new({})
|
18
|
-
@wp01 = WeightedPicker.load_file "spec/a256b128.yaml"
|
19
|
-
@wp02 = WeightedPicker.load_file "spec/a512b64.yaml"
|
20
|
-
end
|
21
|
-
|
22
|
-
describe "initialize" do
|
23
|
-
it "should create new file with data of 256 when the file not exist" do
|
24
|
-
FileUtils.rm NOT_EXIST_FILE if File.exist? NOT_EXIST_FILE
|
25
|
-
lambda{
|
26
|
-
WeightedPicker.load_file(NOT_EXIST_FILE)
|
27
|
-
}.should raise_error(Errno::ENOENT)
|
28
|
-
FileUtils.rm NOT_EXIST_FILE if File.exist? NOT_EXIST_FILE
|
29
|
-
end
|
30
|
-
|
31
|
-
#it "should raise exception" do
|
32
|
-
# # 作成できないファイル名。
|
33
|
-
# lambda{WeightedPicker.load_file("")}.should raise_error(Errno::ENOENT)
|
34
|
-
#end
|
35
|
-
|
36
|
-
it "should read correctly" do
|
37
|
-
# 正しく取り込めているか?
|
38
|
-
@wp01.names_weights.should == { "A" => 256, "B" => 128, }
|
39
|
-
@wp02.names_weights.should == { "A" => 512, "B" => 64, }
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should treat as MAX_WEIGHT when the values are over the MAX_WEIGHT" do
|
43
|
-
#Not write
|
44
|
-
WeightedPicker.load_file("spec/a99999b64.yaml").names_weights.should == { "A" => 65536, "B" => 64, }
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should treat as 0 when the values are negative" do
|
48
|
-
#Not write
|
49
|
-
WeightedPicker.load_file("spec/a-1b256.yaml").names_weights.should == { "A" => 0, "B" => 256, }
|
50
|
-
end
|
51
|
-
|
52
|
-
#New item is set by max values in alive entries.
|
53
|
-
it "should merge when keys between weights and items" do
|
54
|
-
@wp01.merge(["B","C"])
|
55
|
-
@wp01.names_weights.should == { "B" => 128, "C" => 256, }
|
56
|
-
@wp02.merge(["B","C"])
|
57
|
-
@wp02.names_weights.should == { "B" => 64, "C" => 256, }
|
58
|
-
end
|
59
|
-
|
60
|
-
#New item is set by max values in alive entries.
|
61
|
-
it "should merge when keys between weights and items" do
|
62
|
-
@wp01.merge(["A","C"])
|
63
|
-
@wp01.names_weights.should == { "A" => 256, "C" => 256, }
|
64
|
-
@wp02.merge(["A","C"])
|
65
|
-
@wp02.names_weights.should == { "A" => 512, "C" => 512, }
|
66
|
-
end
|
67
|
-
|
68
|
-
it "should raise exception if include not integer weight." do
|
69
|
-
weights = { "A" => 1.0, "B" => 0.5, }
|
70
|
-
lambda{
|
71
|
-
WeightedPicker.load_file("spec/float.yaml")
|
72
|
-
}.should raise_error(WeightedPicker::InvalidWeightError)
|
73
|
-
end
|
74
|
-
|
75
|
-
after do
|
76
|
-
#FileUtils.rm AB_YAML if File.exist? AB_YAML
|
77
|
-
FileUtils.rm NOT_EXIST_FILE if File.exist? NOT_EXIST_FILE
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
#before do
|
82
|
-
# @wp01 = WeightedPicker.load_file(AB_YAML)
|
83
|
-
# @wp00 = WeightedPicker.new({})
|
84
|
-
#end
|
85
|
-
|
86
|
-
describe "pick" do
|
87
|
-
srand(0)
|
88
|
-
it "should pick" do
|
89
|
-
lambda{@wp00.pick}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
90
|
-
|
91
|
-
results = {"A" => 0, "B" => 0}
|
92
|
-
300.times do |i|
|
93
|
-
results[@wp01.pick] += 1
|
94
|
-
end
|
95
|
-
#pp @wp01.names_weights #=> {"A"=>256, "B"=>128}
|
96
|
-
results["A"].should be_within(20).of(200)
|
97
|
-
results["B"].should be_within(10).of(100)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
describe "weigh" do
|
102
|
-
it "should weigh A" do
|
103
|
-
@wp01.weigh("A")
|
104
|
-
@wp01.names_weights.should == { "A" => 512, "B" => 128 }
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should weigh B" do
|
108
|
-
@wp01.weigh("B")
|
109
|
-
@wp01.names_weights.should == { "A" => 256, "B" => 256 }
|
110
|
-
end
|
111
|
-
|
112
|
-
it "should raise error" do
|
113
|
-
lambda{Marshal.load(Marshal.dump(@wp01)).weigh("C")}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
114
|
-
end
|
115
|
-
|
116
|
-
end
|
117
|
-
|
118
|
-
describe "Weightedpicker::lighten" do
|
119
|
-
#before do
|
120
|
-
# @wp01 = WeightedPicker.load_file(AB_YAML)
|
121
|
-
#end
|
122
|
-
|
123
|
-
it "should lighten A" do
|
124
|
-
t = Marshal.load(Marshal.dump(@wp01))
|
125
|
-
t.lighten("A")
|
126
|
-
t.names_weights.should == { "A" => 128, "B" => 128 }
|
127
|
-
end
|
128
|
-
|
129
|
-
it "should lighten B" do
|
130
|
-
t = Marshal.load(Marshal.dump(@wp01))
|
131
|
-
t.lighten("B")
|
132
|
-
t.names_weights.should == { "A" => 256, "B" => 64 }
|
133
|
-
end
|
134
|
-
|
135
|
-
it "should raise error" do
|
136
|
-
t = Marshal.load(Marshal.dump(@wp01))
|
137
|
-
lambda{t.lighten("C")}.should raise_error(WeightedPicker::Tree::NoEntryError)
|
138
|
-
end
|
139
|
-
|
140
|
-
end
|
141
|
-
|
142
|
-
describe "include zero weight" do
|
143
|
-
it "should not change zero weight." do
|
144
|
-
wp01 = WeightedPicker.load_file("spec/a256b0.yaml")
|
145
|
-
wp01.names_weights.should == { "A" => 256, "B" => 0 }
|
146
|
-
wp01.lighten("A")
|
147
|
-
wp01.names_weights.should == { "A" => 128, "B" => 0 }
|
148
|
-
wp01.weigh("A")
|
149
|
-
wp01.names_weights.should == { "A" => 256, "B" => 0 }
|
150
|
-
wp01.lighten("B")
|
151
|
-
wp01.names_weights.should == { "A" => 256, "B" => 0 }
|
152
|
-
wp01.weigh("B")
|
153
|
-
wp01.names_weights.should == { "A" => 256, "B" => 0 }
|
154
|
-
end
|
155
|
-
|
156
|
-
end
|
157
|
-
|
158
|
-
describe "include one weight" do
|
159
|
-
it "should not change zero weight." do
|
160
|
-
wp01 = WeightedPicker.load_file("spec/a256b1.yaml")
|
161
|
-
|
162
|
-
wp01.names_weights.should == { "A" => 256, "B" => 1 }
|
163
|
-
|
164
|
-
wp01.lighten("A")
|
165
|
-
wp01.names_weights.should == { "A" => 128, "B" => 1 }
|
166
|
-
|
167
|
-
wp01.weigh("A")
|
168
|
-
wp01.names_weights.should == { "A" => 256, "B" => 1 }
|
169
|
-
|
170
|
-
wp01.lighten("B")
|
171
|
-
wp01.names_weights.should == { "A" => 256, "B" => 1 }
|
172
|
-
|
173
|
-
wp01.weigh("B")
|
174
|
-
wp01.names_weights.should == { "A" => 256, "B" => 2 }
|
175
|
-
end
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
describe "Weightedpicker::dump" do
|
180
|
-
it "should dump yaml." do
|
181
|
-
io = StringIO.new
|
182
|
-
@wp01.dump(io)
|
183
|
-
io.rewind
|
184
|
-
results = YAML.load(io)
|
185
|
-
results.should == { "A" => 256, "B" => 128, }
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
end
|
190
|
-
|
191
|
-
describe "Weightedpicker::names" do
|
192
|
-
it "should return an array of names." do
|
193
|
-
@wp01.names.should == [ "A", "B"]
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
describe "Weightedpicker::dump_histgram" do
|
199
|
-
it "should output histgram to io." do
|
200
|
-
input = {}
|
201
|
-
4.times do |power|
|
202
|
-
num = 10**power
|
203
|
-
num.times do |i|
|
204
|
-
input["#{power}_#{i}"] = num
|
205
|
-
end
|
206
|
-
end
|
207
|
-
#pp input
|
208
|
-
wp20 = WeightedPicker.new(input)
|
209
|
-
#pp wp20
|
210
|
-
io = StringIO.new
|
211
|
-
wp20.dump_histgram(io)
|
212
|
-
io.rewind
|
213
|
-
io.read.should == <<HERE
|
214
|
-
1( 1)|*
|
215
|
-
2( 0)|
|
216
|
-
4( 0)|
|
217
|
-
8( 0)|
|
218
|
-
16( 10)|*
|
219
|
-
32( 0)|
|
220
|
-
64( 0)|
|
221
|
-
128( 100)|*****
|
222
|
-
256( 0)|
|
223
|
-
512( 0)|
|
224
|
-
1024(1000)|**************************************************
|
225
|
-
2048( 0)|
|
226
|
-
4096( 0)|
|
227
|
-
8192( 0)|
|
228
|
-
16384( 0)|
|
229
|
-
32768( 0)|
|
230
|
-
65536( 0)|
|
231
|
-
HERE
|
232
|
-
|
233
|
-
end
|
234
|
-
|
235
|
-
end
|
236
|
-
|
237
|
-
end
|
238
|
-
|
data/weightedpicker.gemspec
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = "weightedpicker"
|
8
|
-
s.version = "0.1.1"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["ippei94da"]
|
12
|
-
s.date = "2013-04-18"
|
13
|
-
s.description = "This library enables to pick out items at the rate of their weight.\n Weight data is storaged as a YAML file.\n You can use this library for music player, wallpaper changer, language training.\n "
|
14
|
-
s.email = "ippei94da@gmail.com"
|
15
|
-
s.executables = ["weight"]
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
"LICENSE.txt",
|
18
|
-
"README.rdoc"
|
19
|
-
]
|
20
|
-
s.files = [
|
21
|
-
".document",
|
22
|
-
".rspec",
|
23
|
-
"CHANGES",
|
24
|
-
"Gemfile",
|
25
|
-
"LICENSE.txt",
|
26
|
-
"README.rdoc",
|
27
|
-
"Rakefile",
|
28
|
-
"VERSION",
|
29
|
-
"bin/weight",
|
30
|
-
"lib/weightedpicker.rb",
|
31
|
-
"lib/weightedpicker/tree.rb",
|
32
|
-
"spec/a-1b256.yaml",
|
33
|
-
"spec/a256b0.yaml",
|
34
|
-
"spec/a256b1.yaml",
|
35
|
-
"spec/a256b128.yaml",
|
36
|
-
"spec/a512b64.yaml",
|
37
|
-
"spec/a99999b64.yaml",
|
38
|
-
"spec/float.yaml",
|
39
|
-
"spec/spec_helper.rb",
|
40
|
-
"spec/tree_spec.rb",
|
41
|
-
"spec/weightedpicker_spec.rb",
|
42
|
-
"weightedpicker.gemspec"
|
43
|
-
]
|
44
|
-
s.homepage = "http://github.com/ippei94da/weightedpicker"
|
45
|
-
s.licenses = ["MIT"]
|
46
|
-
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = "1.8.11"
|
48
|
-
s.summary = "Picking one item from list at the rate of its weight."
|
49
|
-
|
50
|
-
if s.respond_to? :specification_version then
|
51
|
-
s.specification_version = 3
|
52
|
-
|
53
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
-
s.add_development_dependency(%q<rspec>, ["~> 2.13.0"])
|
55
|
-
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
56
|
-
s.add_development_dependency(%q<bundler>, ["~> 1.3.4"])
|
57
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
58
|
-
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
59
|
-
else
|
60
|
-
s.add_dependency(%q<rspec>, ["~> 2.13.0"])
|
61
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
62
|
-
s.add_dependency(%q<bundler>, ["~> 1.3.4"])
|
63
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
64
|
-
s.add_dependency(%q<simplecov>, [">= 0"])
|
65
|
-
end
|
66
|
-
else
|
67
|
-
s.add_dependency(%q<rspec>, ["~> 2.13.0"])
|
68
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
69
|
-
s.add_dependency(%q<bundler>, ["~> 1.3.4"])
|
70
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
71
|
-
s.add_dependency(%q<simplecov>, [">= 0"])
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|