redis_recipes 0.3.0

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.
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.platform = Gem::Platform::RUBY
4
+ s.required_ruby_version = '>= 1.9.0'
5
+ s.required_rubygems_version = ">= 1.3.6"
6
+
7
+ s.name = "redis_recipes"
8
+ s.summary = "Redis LUA recipes."
9
+ s.description = "Require Redis 2.6.0 or higher"
10
+ s.version = "0.3.0"
11
+
12
+ s.authors = ["Black Square Media"]
13
+ s.email = "info@blacksquaremedia.com"
14
+ s.homepage = "https://github.com/bsm/redis_recipes"
15
+
16
+ s.require_path = ['lib']
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- spec/*`.split("\n")
19
+
20
+ s.add_development_dependency "redis", "~> 3.0.0"
21
+ s.add_development_dependency "rake"
22
+ s.add_development_dependency "rspec"
23
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Range Lookup: Adding" do
4
+
5
+ def state(c = :to_i)
6
+ ranges = {}
7
+ redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
8
+ ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
9
+ end
10
+ ranges
11
+ end
12
+
13
+ def add(member, min, max)
14
+ evalsha :add, keys: ["my_store"], argv: [member, min, max]
15
+ end
16
+
17
+ describe "general" do
18
+
19
+ it 'should catch invalid arguments' do
20
+ lambda { evalsha :add }.should raise_error(Redis::CommandError, /wrong number of arguments/)
21
+ lambda { evalsha :add, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
22
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
23
+ lambda { evalsha :add, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
24
+ end
25
+
26
+ it 'should catch non-numeric ranges' do
27
+ lambda { evalsha :add, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
28
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
29
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
30
+ end
31
+
32
+ end
33
+
34
+ describe "adding" do
35
+ before { add("A", 8, 17) }
36
+
37
+ it 'should support single-value ranges' do
38
+ lambda { add("B", 10, 10) }.should change { state }.
39
+ to 8 => "A", 10 => "AB", 17 => "A"
40
+ end
41
+
42
+ it 'should support float/decimal ranges' do
43
+ lambda { add("B", 10, 10.5) }.should change { state(:to_f) }.
44
+ to 8.0 => "A", 10.0 => "AB", 10.5 => "AB", 17.0 => "A"
45
+ end
46
+
47
+ it 'should support zero bounds' do
48
+ lambda { add("B", 0, 5) }.should change { state }.
49
+ to 0 => "B", 5 => "B", 8 => "A", 17 => "A"
50
+
51
+ lambda { add("C", -5, 0) }.should change { state }.
52
+ to -5 => "C", 0 => "BC", 5 => "B", 8 => "A", 17 => "A"
53
+ end
54
+
55
+ it 'should maintain an index' do
56
+ redis.zrange('my_store:~', 0, -1, with_scores: true).should == [["8", 8], ["17", 17]]
57
+ end
58
+
59
+ it 'should store range values' do
60
+ redis.keys.should =~ ["my_store:~", "my_store:8", "my_store:17"]
61
+ state.should == { 8 => "A", 17 => "A" }
62
+ end
63
+
64
+ it 'should return OK if successful' do
65
+ add("B", 9, 18).should == "OK"
66
+ end
67
+
68
+ end
69
+
70
+ describe "merging" do
71
+ before { add("A", 8, 17) }
72
+
73
+ it 'should add non overlapping, left (B B A A)' do
74
+ lambda { add("B", 5, 6) }.should change { state }.
75
+ to 5 => "B", 6 => "B", 8 => "A", 17 => "A"
76
+ end
77
+
78
+ it 'should add non overlapping, right (A A B B)' do
79
+ lambda { add("B", 18, 23) }.should change { state }.
80
+ to 8 => "A", 17 => "A", 18 => "B", 23 => "B"
81
+ end
82
+
83
+ it 'should add non overlapping, inner (A B B A)' do
84
+ lambda { add("B", 9, 12) }.should change { state }.
85
+ to 8 => "A", 9 => "AB", 12 => "AB", 17 => "A"
86
+ end
87
+
88
+ it 'should add overlapping, left (B A B A)' do
89
+ lambda { add("B", 6, 10) }.should change { state }.
90
+ to 6 => "B", 8 => "AB", 10 => "AB", 17 => "A"
91
+ end
92
+
93
+ it 'should add overlapping, right (A B A B)' do
94
+ lambda { add("B", 15, 23) }.should change { state }.
95
+ to 8 => "A", 15 => "AB", 17 => "AB", 23 => "B"
96
+ end
97
+
98
+ it 'should add overlapping, both (AB BA)' do
99
+ lambda { add("B", 8, 17) }.should change { state }.
100
+ to 8 => "AB", 17 => "AB"
101
+ end
102
+
103
+ it 'should add tangent, outer left (B BA A)' do
104
+ lambda { add("B", 6, 8) }.should change { state }.
105
+ to 6 => "B", 8 => "AB", 17 => "A"
106
+ end
107
+
108
+ it 'should add tangent, inner left (AB B A)' do
109
+ lambda { add("B", 8, 10) }.should change { state }.
110
+ to 8 => "AB", 10 => "AB", 17 => "A"
111
+ end
112
+
113
+ it 'should add tangent, outer right (A AB B)' do
114
+ lambda { add("B", 17, 22) }.should change { state }.
115
+ to 8 => "A", 17 => "AB", 22 => "B"
116
+ end
117
+
118
+ it 'should add tangent, inner right (A B BA)' do
119
+ lambda { add("B", 12, 17) }.should change { state }.
120
+ to 8 => "A", 12 => "AB", 17 => "AB"
121
+ end
122
+
123
+ end
124
+
125
+ it 'should bring it all together' do
126
+ add("A", 8, 17)
127
+ add("B", 5, 6)
128
+ add("C", 18, 23)
129
+ add("D", 9, 12)
130
+ add("E", 6, 10)
131
+ add("F", 15, 23)
132
+ add("G", 8, 17)
133
+ add("H", 6, 8)
134
+ add("I", 8, 10)
135
+ add("J", 7, 22)
136
+ add("K", 12, 17)
137
+
138
+ state.should == {
139
+ 5 => "B", 6 => "BEH", 7 => "EHJ",
140
+ 8 => "AEGHIJ", 9 => "ADEGIJ", 10 => "ADEGIJ",
141
+ 12 => "ADGJK", 15 => "AFGJK", 17 => "AFGJK",
142
+ 18 => "CFJ", 22 => "CFJ", 23 => "CF"
143
+ }
144
+ end
145
+
146
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Range Lookup: Lookup" do
4
+
5
+ def add(member, min, max)
6
+ evalsha :add, keys: ["my_store"], argv: [member, min, max]
7
+ end
8
+
9
+ def lookup(value)
10
+ evalsha :lookup, keys: ["my_store"], argv: [value]
11
+ end
12
+
13
+ before do
14
+ add("A", 0, 8)
15
+ add("B", 4, 6)
16
+ add("C", 2, 9)
17
+ add("D", 7, 10)
18
+ end
19
+
20
+ def state
21
+ ranges = {}
22
+ redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
23
+ ranges[score.to_f] = redis.smembers("my_store:#{value}").sort
24
+ end
25
+ ranges
26
+ end
27
+
28
+ it 'should catch invalid arguments' do
29
+ lambda { evalsha :lookup }.should raise_error(Redis::CommandError, /wrong number of arguments/)
30
+ lambda { evalsha :lookup, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
31
+ lambda { evalsha :lookup, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
32
+ end
33
+
34
+ it 'should catch non-numeric ranges' do
35
+ lambda { evalsha :lookup, keys: ["one"], argv: ["one"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
36
+ end
37
+
38
+ it 'should return the members when found' do
39
+ lookup(0).should =~ ["A"]
40
+ lookup(1).should =~ ["A"]
41
+ lookup(2).should =~ ["A", "C"]
42
+ lookup(3).should =~ ["A", "C"]
43
+ lookup(4).should =~ ["A", "B", "C"]
44
+ lookup(5).should =~ ["A", "B", "C"]
45
+ lookup(6).should =~ ["A", "B", "C"]
46
+ lookup(7).should =~ ["A", "C", "D"]
47
+ lookup(8).should =~ ["A", "C", "D"]
48
+ lookup(9).should =~ ["C", "D"]
49
+ lookup(10).should =~ ["D"]
50
+ end
51
+
52
+ it 'should return an empty set if not found' do
53
+ lookup(-1).should =~ []
54
+ lookup(11).should =~ []
55
+ lookup(111).should =~ []
56
+ end
57
+
58
+ it 'should support float lookups' do
59
+ lookup(6.5).should =~ ["A", "C"]
60
+ lookup(7.5).should =~ ["A", "C", "D"]
61
+ lookup(10.1).should =~ []
62
+ end
63
+
64
+ end
@@ -0,0 +1,189 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Range Lookup: Removing" do
4
+
5
+ def state(c = :to_i)
6
+ ranges = {}
7
+ redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
8
+ ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
9
+ end
10
+ ranges
11
+ end
12
+
13
+ def brackets
14
+ redis.zrange('my_store:~', 0, -1)
15
+ end
16
+
17
+ def add(member, min, max)
18
+ evalsha :add, keys: ["my_store"], argv: [member, min, max]
19
+ end
20
+
21
+ def remove(member, min, max)
22
+ evalsha :remove, keys: ["my_store"], argv: [member, min, max]
23
+ end
24
+
25
+ def scenario(pairs)
26
+ pairs.each do |member, range|
27
+ add member.to_s, range.first, range.last
28
+ end
29
+ end
30
+
31
+ describe "general" do
32
+
33
+ it 'should catch invalid arguments' do
34
+ lambda { evalsha :remove }.should raise_error(Redis::CommandError, /wrong number of arguments/)
35
+ lambda { evalsha :remove, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
36
+ lambda { evalsha :remove, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
37
+ lambda { evalsha :remove, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
38
+ end
39
+
40
+ it 'should catch non-numeric ranges' do
41
+ lambda { evalsha :remove, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
42
+ lambda { evalsha :remove, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
43
+ lambda { evalsha :remove, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
44
+ end
45
+
46
+ it 'should return OK if successful' do
47
+ remove("A", 8, 17).should == "OK"
48
+ end
49
+
50
+ end
51
+
52
+ describe "removing" do
53
+ before { add("A", 8, 17) }
54
+
55
+ it 'should support zero bounds' do
56
+ scenario B: -5..0, C: 0..5
57
+
58
+ lambda {
59
+ remove("B", -5, 0)
60
+ remove("C", 0, 5)
61
+ }.should change { state }.
62
+ from(-5 => "B", 0 => "BC", 5 => "C", 8 => "A", 17 => "A").
63
+ to(8 => "A", 17 => "A")
64
+ end
65
+
66
+ it 'should clean up index' do
67
+ lambda {
68
+ add("B", 15, 22)
69
+ add("C", 15, 23)
70
+ }.should change { brackets }.to ["8", "15", "17", "22", "23"]
71
+
72
+ lambda {
73
+ remove("B", 15, 22)
74
+ }.should change { brackets }.to ["8", "15", "17", "23"]
75
+
76
+ lambda {
77
+ remove("C", 15, 23)
78
+ }.should change { brackets }.to ["8", "17"]
79
+
80
+ lambda {
81
+ remove("A", 8, 17)
82
+ }.should change { brackets }.to []
83
+ end
84
+
85
+ end
86
+
87
+ describe "use cases" do
88
+
89
+ it "should remove from [A: 1-2, B: 3-4]" do
90
+ scenario A: 1..2, B: 3..4
91
+ lambda { remove("A", 1, 2) }.should change { state }.
92
+ from(1 => "A", 2 => "A", 3 => "B", 4 => "B").
93
+ to(3 => "B", 4 => "B")
94
+ end
95
+
96
+ it "should remove from [A: 3-4, B: 1-2]" do
97
+ scenario A: 3..4, B: 1..2
98
+ lambda { remove("A", 3, 4) }.should change { state }.
99
+ from(1 => "B", 2 => "B", 3 => "A", 4 => "A").
100
+ to(1 => "B", 2 => "B")
101
+ end
102
+
103
+ it "should remove from [A: 1-3, B: 2-4]" do
104
+ scenario A: 1..3, B: 2..4
105
+ lambda { remove("A", 1, 3) }.should change { state }.
106
+ from(1 => "A", 2 => "AB", 3 => "AB", 4 => "B").
107
+ to(2 => "B", 4 => "B")
108
+ end
109
+
110
+ it "should remove from [A: 2-4, B: 1-3]" do
111
+ scenario A: 2..4, B: 1..3
112
+ lambda { remove("A", 2, 4) }.should change { state }.
113
+ from(1 => "B", 2 => "AB", 3 => "AB", 4 => "A").
114
+ to(1 => "B", 3 => "B")
115
+ end
116
+
117
+ it "should remove from [A: 1-4, B: 2-3]" do
118
+ scenario A: 1..4, B: 2..3
119
+ lambda { remove("A", 1, 4) }.should change { state }.
120
+ from(1 => "A", 2 => "AB", 3 => "AB", 4 => "A").
121
+ to(2 => "B", 3 => "B")
122
+ end
123
+
124
+ it "should remove from [A: 2-3, B: 1-4]" do
125
+ scenario A: 2..3, B: 1..4
126
+ lambda { remove("A", 2, 3) }.should change { state }.
127
+ from(1 => "B", 2 => "AB", 3 => "AB", 4 => "B").
128
+ to(1 => "B", 4 => "B")
129
+ end
130
+
131
+ it "should remove from [A: 1-3, B: 3-4]" do
132
+ scenario A: 1..3, B: 3..4
133
+ lambda { remove("A", 1, 3) }.should change { state }.
134
+ from(1 => "A", 3 => "AB", 4 => "B").
135
+ to(3 => "B", 4 => "B")
136
+ end
137
+
138
+ it "should remove from [A: 1-4, B: 3-4]" do
139
+ scenario A: 1..4, B: 3..4
140
+ lambda { remove("A", 1, 4) }.should change { state }.
141
+ from(1 => "A", 3 => "AB", 4 => "AB").
142
+ to(3 => "B", 4 => "B")
143
+ end
144
+
145
+ it "should remove from [A: 3-4, B: 1-3]" do
146
+ scenario A: 3..4, B: 1..3
147
+ lambda { remove("A", 3, 4) }.should change { state }.
148
+ from(1 => "B", 3 => "AB", 4 => "A").
149
+ to(1 => "B", 3 => "B")
150
+ end
151
+
152
+ it "should remove from [A: 3-4, B: 1-4]" do
153
+ scenario A: 3..4, B: 1..4
154
+ lambda { remove("A", 3, 4) }.should change { state }.
155
+ from(1 => "B", 3 => "AB", 4 => "AB").
156
+ to(1 => "B", 4 => "B")
157
+ end
158
+
159
+ it "should remove from [A: 1-4, B: 1-2]" do
160
+ scenario A: 1..4, B: 1..2
161
+ lambda { remove("A", 1, 4) }.should change { state }.
162
+ from(1 => "AB", 2 => "AB", 4 => "A").
163
+ to(1 => "B", 2 => "B")
164
+ end
165
+
166
+ it "should remove from [A: 2-4, B: 1-2]" do
167
+ scenario A: 2..4, B: 1..2
168
+ lambda { remove("A", 2, 4) }.should change { state }.
169
+ from(1 => "B", 2 => "AB", 4 => "A").
170
+ to(1 => "B", 2 => "B")
171
+ end
172
+
173
+ it "should remove from [A: 1-4, B: 1-4]" do
174
+ scenario A: 1..4, B: 1..4
175
+ lambda { remove("A", 1, 4) }.should change { state }.
176
+ from(1 => "AB", 4 => "AB").
177
+ to(1 => "B", 4 => "B")
178
+ end
179
+
180
+ it "should remove from [A: 1-4]" do
181
+ scenario A: 1..4
182
+ lambda { remove("A", 1, 4) }.should change { state }.
183
+ from(1 => "A", 4 => "A").
184
+ to({})
185
+ end
186
+
187
+ end
188
+
189
+ end
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Range Lookup X: Adding" do
4
+
5
+ def state(c = :to_i)
6
+ ranges = {}
7
+ redis.zrange('my_store:~', 0, -1, with_scores: true).each do |value, score|
8
+ ranges[score.send(c)] = redis.smembers("my_store:#{value}").sort.join
9
+ end
10
+ ranges
11
+ end
12
+
13
+ def add(member, min, max)
14
+ evalsha :add, keys: ["my_store"], argv: [member, min, max]
15
+ end
16
+
17
+ describe "general" do
18
+
19
+ it 'should catch invalid arguments' do
20
+ lambda { evalsha :add }.should raise_error(Redis::CommandError, /wrong number of arguments/)
21
+ lambda { evalsha :add, keys: ["one"] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
22
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 2] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
23
+ lambda { evalsha :add, keys: ["one", "two"], argv: ["one", 2, 3] }.should raise_error(Redis::CommandError, /wrong number of arguments/)
24
+ end
25
+
26
+ it 'should catch non-numeric ranges' do
27
+ lambda { evalsha :add, keys: ["one"], argv: ["one", "two", 3] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
28
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 2, "three"] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
29
+ lambda { evalsha :add, keys: ["one"], argv: ["one", 3, 2] }.should raise_error(Redis::CommandError, /not numeric or out of range/)
30
+ end
31
+
32
+ it 'should NOT support single-value ranges' do
33
+ lambda { add("B", 10, 10) }.should raise_error(Redis::CommandError, /not numeric or out of range/)
34
+ end
35
+
36
+ end
37
+
38
+ describe "adding" do
39
+
40
+ it 'should store ranges' do
41
+ lambda { add("A", 8, 17) }.should change { state }.
42
+ to 8 => "A", 17 => ""
43
+ end
44
+
45
+ it 'should support float/decimal ranges' do
46
+ lambda { add("A", 7.5, 17.9); add("B", 8.1, 10.6) }.should change { state(:to_f) }.
47
+ to 7.5 => "A", 8.1 => "AB", 10.6 => "A", 17.9 => ""
48
+ end
49
+
50
+ it 'should support nagative ranges and bounds' do
51
+ lambda { add("B", 0, 5) }.should change { state }.
52
+ to 0 => "B", 5 => ""
53
+
54
+ lambda { add("C", -5, 0) }.should change { state }.
55
+ to -5 => "C", 0 => "B", 5 => ""
56
+ end
57
+
58
+ it 'should maintain an index' do
59
+ lambda { add("A", 8, 17) }.should change {
60
+ redis.zrange('my_store:~', 0, -1, with_scores: true)
61
+ }.to [["8", 8.0], ["17", 17.0]]
62
+ end
63
+
64
+ it 'should store range members' do
65
+ lambda { add("A", 8, 17) }.should change { redis.keys.sort }.
66
+ to ["my_store:8", "my_store:~"]
67
+ end
68
+
69
+ it 'should return OK if successful' do
70
+ add("A", 8, 17).should == "OK"
71
+ end
72
+
73
+ end
74
+
75
+ describe "merging" do
76
+ before { add("A", 8, 17) }
77
+
78
+ it 'should add non overlapping, left (B B A A)' do
79
+ lambda { add("B", 5, 6) }.should change { state }.
80
+ to 5 => "B", 6 => "", 8 => "A", 17 => ""
81
+ end
82
+
83
+ it 'should add non overlapping, right (A A B B)' do
84
+ lambda { add("B", 18, 23) }.should change { state }.
85
+ to 8 => "A", 17 => "", 18 => "B", 23 => ""
86
+ end
87
+
88
+ it 'should add non overlapping, inner (A B B A)' do
89
+ lambda { add("B", 9, 12) }.should change { state }.
90
+ to 8 => "A", 9 => "AB", 12 => "A", 17 => ""
91
+ end
92
+
93
+ it 'should add overlapping, left (B A B A)' do
94
+ lambda { add("B", 6, 10) }.should change { state }.
95
+ to 6 => "B", 8 => "AB", 10 => "A", 17 => ""
96
+ end
97
+
98
+ it 'should add overlapping, right (A B A B)' do
99
+ lambda { add("B", 15, 23) }.should change { state }.
100
+ to 8 => "A", 15 => "AB", 17 => "B", 23 => ""
101
+ end
102
+
103
+ it 'should add overlapping, both (AB BA)' do
104
+ lambda { add("B", 8, 17) }.should change { state }.
105
+ to 8 => "AB", 17 => ""
106
+ end
107
+
108
+ it 'should add tangent, outer left (B BA A)' do
109
+ lambda { add("B", 6, 8) }.should change { state }.
110
+ to 6 => "B", 8 => "A", 17 => ""
111
+ end
112
+
113
+ it 'should add tangent, inner left (AB B A)' do
114
+ lambda { add("B", 8, 10) }.should change { state }.
115
+ to 8 => "AB", 10 => "A", 17 => ""
116
+ end
117
+
118
+ it 'should add tangent, outer right (A AB B)' do
119
+ lambda { add("B", 17, 22) }.should change { state }.
120
+ to 8 => "A", 17 => "B", 22 => ""
121
+ end
122
+
123
+ it 'should add tangent, inner right (A B BA)' do
124
+ lambda { add("B", 12, 17) }.should change { state }.
125
+ to 8 => "A", 12 => "AB", 17 => ""
126
+ end
127
+
128
+ it 'should work with multiple ranges' do
129
+ add("B", 5, 6)
130
+ add("C", 18, 23)
131
+ add("D", 9, 12)
132
+ add("E", 6, 10)
133
+ add("F", 15, 23)
134
+ add("G", 8, 17)
135
+ add("H", 6, 8)
136
+ add("I", 8, 10)
137
+ add("J", 7, 22)
138
+ add("K", 12, 17)
139
+
140
+ state.should == {
141
+ 5 => "B", 6 => "EH", 7 => "EHJ",
142
+ 8 => "AEGIJ", 9 => "ADEGIJ", 10 => "ADGJ",
143
+ 12 => "AGJK", 15 => "AFGJK", 17 => "FJ",
144
+ 18 => "CFJ", 22 => "CF", 23 => ""
145
+ }
146
+ end
147
+ end
148
+
149
+ end