multiset 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,629 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- # This version is a direct porting from version 0.202 (not for Rubygems).
4
- # Unit test code will be added from the next version.
3
+ # Note: NOT ALL METHODS ARE TESTED!
5
4
 
6
5
  require "multiset"
7
6
 
8
7
  describe Multiset do
8
+ # Generation methods
9
+ describe "being generated" do
10
+ describe "by Multiset.parse(Hash)" do
11
+ it "should regard keys as elements and values as counts" do
12
+ Multiset.parse({:a => 3, :b => 2}).should == Multiset[:a, :a, :a, :b, :b]
13
+ end
14
+ end
15
+
16
+ describe "by Multiset.parse(NonHashEnumerable)" do
17
+ it "should regard each of the elements as an element in the Multiset" do
18
+ Multiset.parse([:a, 3, :b, 2]).should == Multiset[:a, 3, :b, 2]
19
+ end
20
+ end
21
+
22
+ describe "by Multiset.parse(NonEnumerable)" do
23
+ it "should be rejected" do
24
+ lambda{ Multiset.parse(83) }.should raise_error(ArgumentError)
25
+ lambda{ Multiset.parse("hoge\npiyo\n") }.should raise_error(ArgumentError)
26
+ end
27
+ end
28
+
29
+ describe "by Multiset.parse_force(NonEnumerable)" do
30
+ it "should be rejected except for strings" do
31
+ lambda{ Multiset.parse_force(83) }.should raise_error(ArgumentError)
32
+ Multiset.parse_force("hoge\npiyo\n").should == Multiset["hoge\n", "piyo\n"]
33
+ end
34
+ end
35
+
36
+ describe "by Multiset#dup" do
37
+ it "should be the same as the original Multiset" do
38
+ tmp = Multiset.new([:a,:a,:b])
39
+ tmp.should == tmp.dup
40
+
41
+ tmp = Multiset[]
42
+ tmp.should == tmp.dup
43
+ end
44
+ end
45
+ end
46
+
47
+ it "should be replaced by another Multiset by Multiset#replace" do
48
+ tmp = Multiset[]
49
+ tmp.replace(Multiset[:a,:b,:b]).should == Multiset[:a,:b,:b]
50
+ end
51
+
52
+ # Comparatory/merging methods
53
+ describe "of two" do
54
+ before do
55
+ @ms1 = Multiset.new(%w'a a a a b b b c c d')
56
+ @ms2 = Multiset.new(%w'b c c d d d e e e e')
57
+ end
58
+
59
+ it "should judge inclusions correctly" do
60
+ tmp1 = Multiset.new(%w'a a a a b b b c c d d')
61
+ tmp2 = Multiset.new(%w'a a a a b b b c c d')
62
+ tmp3 = Multiset.new(%w'a a a b b b c c d')
63
+
64
+ (@ms1 == @ms2).should be_false
65
+ (@ms1 == tmp1).should be_false
66
+ (@ms1 == tmp2).should be_true
67
+ (@ms1 == tmp3).should be_false
68
+
69
+ (@ms1.subset?(@ms2)).should be_false
70
+ (@ms1.subset?(tmp1)).should be_true
71
+ (@ms1.subset?(tmp2)).should be_true
72
+ (@ms1.subset?(tmp3)).should be_false
73
+ (@ms2.subset?(@ms1)).should be_false
74
+ (tmp1.subset?(@ms1)).should be_false
75
+ (tmp2.subset?(@ms1)).should be_true
76
+ (tmp3.subset?(@ms1)).should be_true
77
+
78
+ (@ms1.proper_subset?(@ms2)).should be_false
79
+ (@ms1.proper_subset?(tmp1)).should be_true
80
+ (@ms1.proper_subset?(tmp2)).should be_false
81
+ (@ms1.proper_subset?(tmp3)).should be_false
82
+ (@ms2.proper_subset?(@ms1)).should be_false
83
+ (tmp1.proper_subset?(@ms1)).should be_false
84
+ (tmp2.proper_subset?(@ms1)).should be_false
85
+ (tmp3.proper_subset?(@ms1)).should be_true
86
+
87
+ (@ms1.superset?(@ms2)).should be_false
88
+ (@ms1.superset?(tmp1)).should be_false
89
+ (@ms1.superset?(tmp2)).should be_true
90
+ (@ms1.superset?(tmp3)).should be_true
91
+ (@ms2.superset?(@ms1)).should be_false
92
+ (tmp1.superset?(@ms1)).should be_true
93
+ (tmp2.superset?(@ms1)).should be_true
94
+ (tmp3.superset?(@ms1)).should be_false
95
+
96
+ (@ms1.proper_superset?(@ms2)).should be_false
97
+ (@ms1.proper_superset?(tmp1)).should be_false
98
+ (@ms1.proper_superset?(tmp2)).should be_false
99
+ (@ms1.proper_superset?(tmp3)).should be_true
100
+ (@ms2.proper_superset?(@ms1)).should be_false
101
+ (tmp1.proper_superset?(@ms1)).should be_true
102
+ (tmp2.proper_superset?(@ms1)).should be_false
103
+ (tmp3.proper_superset?(@ms1)).should be_false
104
+ end
105
+
106
+ it "should compute the intersection correctly" do
107
+ (@ms1 & @ms2).should == Multiset.new(%w'b c c d')
108
+ end
109
+
110
+ it "should compute the union correctly" do
111
+ (@ms1 | @ms2).should == Multiset.new(%w'a a a a b b b c c d d d e e e e')
112
+ end
113
+
114
+ it "should compute the sum correctly" do
115
+ (@ms1 + @ms2).should == Multiset.new(%w'a a a a b b b b c c c c d d d d e e e e')
116
+
117
+ tmp = @ms1.dup
118
+ tmp.merge!(@ms2)
119
+ tmp.should == Multiset.new(%w'a a a a b b b b c c c c d d d d e e e e')
120
+ end
121
+
122
+ it "should compute the difference correctly" do
123
+ (@ms1 - @ms2).should == Multiset.new(%w'a a a a b b')
124
+
125
+ tmp = @ms1.dup
126
+ tmp.subtract!(@ms2)
127
+ tmp.should == Multiset.new(%w'a a a a b b')
128
+ end
129
+ end
130
+
131
+ # Iteration methods
132
+ describe "being iterated for entries" do
133
+ before do
134
+ @ms = Multiset.new(%w'a a b b b b c d d d')
135
+ end
136
+
137
+ it "should have the same result between Multiset\#{all?, any?, none?, one?} and Enumerable\#{all?, any?, none?, one?}" do
138
+ @ms.all?{ |x| x == "a" }.should be_false
139
+ @ms.any?{ |x| x == "a" }.should be_true
140
+ @ms.none?{ |x| x == "a" }.should be_false
141
+ @ms.none?{ |x| x.instance_of?(Integer) }.should be_true
142
+ @ms.one?{ |x| x == "a" }.should be_false
143
+ @ms.one?{ |x| x == "c" }.should be_true
144
+ end
145
+
146
+ it "should have the same result between Multiset#count and Enumerable#count" do
147
+ @ms.count("a").should == 2
148
+ @ms.count("x").should == 0
149
+ @ms.count.should == 10
150
+ @ms.count{ |x| x == "b" }.should == 4
151
+ end
152
+
153
+ it "should find an items by the specified condition with Multiset#find / Multiset#detect" do
154
+ @ms.find{ |item| item =~ /d/ }.should == "d"
155
+ end
156
+
157
+ it "should find an item by the specified condition with Multiset#find_with / Multiset#detect_with" do
158
+ @ms.find_with{ |item, count| count == 2 }.should == "a"
159
+ end
160
+
161
+ it "should find an items by the specified condition with Multiset#find_all / Multiset#select" do
162
+ @ms.find_all{ |item| item =~ /d/ }.should == Multiset.new(%w[d d d])
163
+ end
164
+
165
+ it "should find an items by the specified condition with Multiset#find_all_with / Multiset#select_with" do
166
+ @ms.find_all_with{ |item, count| count <= 2 }.should == Multiset.new(%w[a a c])
167
+ end
168
+
169
+ it "should find an items by the specified condition with Multiset#reject" do
170
+ @ms.reject{ |item| item =~ /d/ }.should == Multiset.new(%w[a a b b b b c])
171
+ end
172
+
173
+ it "should find an items by the specified condition with Multiset#reject!" do
174
+ @ms.reject!{ |item| item =~ /x/ }.should be_nil
175
+ @ms.reject!{ |item| item =~ /d/ }.object_id.should == @ms.object_id
176
+ @ms.should == Multiset.new(%w[a a b b b b c])
177
+ end
178
+
179
+ it "should find an items by the specified condition with Multiset#delete_if" do
180
+ @ms.delete_if{ |item| item =~ /x/ }.object_id.should == @ms.object_id
181
+ @ms.delete_if{ |item| item =~ /d/ }.object_id.should == @ms.object_id
182
+ @ms.should == Multiset.new(%w[a a b b b b c])
183
+ end
184
+
185
+ it "should find an items by the specified condition with Multiset#reject_with" do
186
+ @ms.reject_with{ |item, count| item =~ /d/ || count > 3 }.should == Multiset.new(%w[a a c])
187
+ end
188
+
189
+ it "should find an items by the specified condition with Multiset#grep" do
190
+ @ms.grep(/d/).should == Multiset.new(%w[d d d])
191
+ @ms.grep(/d/){ |item| item + "x" }.should == Multiset.new(%w[dx dx dx])
192
+ end
193
+
194
+ it "should repeat for items by the specified condition with Multiset#inject_with / Multiset#reduce_with" do
195
+ result = @ms.inject_with({ :item_sum => "", :count_sum => 0 }) do |obj, item, count|
196
+ obj[:item_sum] += item
197
+ obj[:count_sum] += count
198
+ obj
199
+ end
200
+ result[:item_sum].each_char.sort.should == %w[a b c d]
201
+ result[:count_sum].should == 10
202
+ end
203
+
204
+ it "should return a new Multiset by the specified condition with Multiset#map / Multiset#collect" do
205
+ @ms.map{ |item| item * 2 }.should == Multiset.new(%w'aa aa bb bb bb bb cc dd dd dd')
206
+ end
207
+
208
+ it "should return a new Multiset by the specified condition with Multiset#map_with / Multiset#collect_with" do
209
+ @ms.map_with{ |item, count| [item * 2, count * 2] }.should == Multiset.new(%w'aa aa aa aa bb bb bb bb bb bb bb bb cc cc dd dd dd dd dd dd')
210
+ end
211
+
212
+ it "should return the maximum/mininum value by max/min/minmax" do
213
+ @ms.max.should == "d"
214
+ @ms.min.should == "a"
215
+ @ms.minmax.should == %w[a d]
216
+ @ms.max{ |a, b| b <=> a }.should == "a"
217
+ @ms.min{ |a, b| b <=> a }.should == "d"
218
+ @ms.minmax{ |a, b| b <=> a }.should == %w[d a]
219
+ end
220
+
221
+ it "should return the maximum/mininum value by max_by/min_by/minmax_by" do
222
+ @ms.max_by{ |item| item }.should == "d"
223
+ @ms.min_by{ |item| item }.should == "a"
224
+ @ms.minmax_by{ |item| item }.should == %w[a d]
225
+ end
226
+
227
+ it "should return the maximum/mininum value by max_with/min_with/minmax_with" do
228
+ @ms.max_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == "b"
229
+ @ms.max_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == "c"
230
+ @ms.min_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == "c"
231
+ @ms.min_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == "b"
232
+ @ms.minmax_with{ |a_item, a_count, b_item, b_count| a_count <=> b_count }.should == %w[c b]
233
+ end
234
+
235
+ it "should return the maximum/mininum value by max_by_with/min_by_with/minmax_by_with" do
236
+ @ms.max_by_with{ |item, count| count }.should == "b"
237
+ @ms.min_by_with{ |item, count| count }.should == "c"
238
+ @ms.minmax_by_with{ |item, count| count }.should == %w[c b]
239
+ end
240
+
241
+ it "should return sorted array by Multiset#sort / Multiset#sort_with" do
242
+ @ms.sort.should == %w[a a b b b b c d d d]
243
+ @ms.sort{ |a, b| b <=> a }.should == %w[d d d c b b b b a a]
244
+ @ms.sort_with{ |a_item, a_count, b_item, b_count| b_count <=> a_count }.should == %w[b b b b d d d a a c]
245
+ end
246
+
247
+ it "should return sorted array by Multiset#sort_by / Multiset#sort_by_with" do
248
+ @ms.sort_by{ |item| item }.should == %w[a a b b b b c d d d]
249
+ @ms.sort_by_with{ |item, count| count }.should == %w[c a a d d d b b b b]
250
+ end
251
+ end
252
+
253
+ # Updating methods
254
+ describe "being updated" do
255
+ before do
256
+ @ms1 = Multiset.new(%w'a a a a b b b c c d')
257
+ end
258
+
259
+ it "should add an element correctly" do
260
+ tmp = @ms1.dup
261
+ tmp << "a"
262
+ tmp.should == Multiset.new(%w'a a a a a b b b c c d')
263
+ end
264
+
265
+ it "should add multiple element correctly" do
266
+ tmp = @ms1.dup
267
+ tmp.add("e", 3)
268
+ tmp.should == Multiset.new(%w'a a a a b b b c c d e e e')
269
+
270
+ tmp = @ms1.dup
271
+ tmp.add("a", 3)
272
+ tmp.should == Multiset.new(%w'a a a a a a a b b b c c d')
273
+ end
274
+
275
+ it "should remove an element correctly" do
276
+ tmp = @ms1.dup
277
+ tmp.delete("a")
278
+ tmp.should == Multiset.new(%w'a a a b b b c c d')
279
+
280
+ tmp = @ms1.dup
281
+ tmp.delete("e") # nothing deleted because `tmp' does not contain "e"
282
+ tmp.should == @ms1
283
+ end
284
+
285
+ it "should remove multiple element correctly" do
286
+ tmp = @ms1.dup
287
+ tmp.delete("a", 3)
288
+ tmp.should == Multiset.new(%w'a b b b c c d')
289
+
290
+ tmp = @ms1.dup
291
+ tmp.delete("a", 6) # in case `tmp' does not contain "a" less than 6 times
292
+ tmp.should == Multiset.new(%w'b b b c c d')
293
+ end
294
+
295
+ it "should remove all element of specified value correctly" do
296
+ tmp = @ms1.dup
297
+ tmp.delete_all("a")
298
+ tmp.should == Multiset.new(%w'b b b c c d')
299
+
300
+ tmp = @ms1.dup
301
+ tmp.delete_all("e") # nothing deleted because `tmp' does not contain "e"
302
+ tmp.should == @ms1
303
+ end
304
+ end
305
+ end
306
+
307
+ describe Hash, "when converted to a multiset" do
308
+ it "should regard keys as elements and values as counts by Hash#to_multiset" do
309
+ {:a => 2, :b => 1}.to_multiset.should == Multiset[:a,:a,:b]
310
+ {:x => 2, :y => 2, :z => 0}.to_multiset.should == Multiset[:x,:x,:y,:y]
311
+ end
9
312
  end
10
313
 
11
314
  describe Multimap do
315
+ describe "generated by adding items one by one" do
316
+ before do
317
+ @empty_multiset = Multiset.new
318
+ @mm = Multimap.new
319
+ end
320
+
321
+ it "should be empty for any key when generated without parameters" do
322
+ @mm[:a].should == @empty_multiset
323
+ @mm[:b].should == @empty_multiset
324
+ end
325
+
326
+ it "should be a singleton multiset by adding a scalar value" do
327
+ @mm[:a].add "bar"
328
+ @mm[:a].should == Multiset["bar"]
329
+ end
330
+
331
+ it "should not make any unsolicited change" do
332
+ @mm[:a].add "bar"
333
+ @mm[:b].should == @empty_multiset
334
+ end
335
+
336
+ it "should be a multiset constructed by the given array" do
337
+ @mm[:a] = %w[foo foo bar]
338
+ @mm[:a].should == Multiset.new(%w[foo foo bar])
339
+ end
340
+
341
+ it "should be a multiset constructed by the given hash (key: element, value: count)" do
342
+ @mm[:a] = {"foo" => 3, "bar" => 2}
343
+ @mm[:a].should == Multiset["foo", "foo", "bar", "foo", "bar"]
344
+ end
345
+
346
+ it "should not be updated by setting a scalar (other than 'Enumerable' object)" do
347
+ lambda{ @mm[:a] = "foobar" }.should raise_error(ArgumentError)
348
+ lambda{ @mm[:a] = 56 }.should raise_error(ArgumentError)
349
+ end
350
+
351
+ it "should be duplicated by Multimap#dup" do
352
+ @mm.dup.should == @mm
353
+ end
354
+ end
355
+
356
+ describe "being compared" do
357
+ it "should be correctly compared" do
358
+ mm1 = Multimap.new
359
+ mm1[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
360
+ mm1[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
361
+ mm2 = Multimap.new
362
+ mm2[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
363
+ mm2[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
364
+ mm3 = Multimap.new
365
+ mm3[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
366
+ mm3[:b] = ["foo", "bar", "hoge", "hoge", "bar", "buz"]
367
+
368
+ mm1.should == mm2
369
+ mm1.should_not == mm3
370
+
371
+ mm3.dup.should == mm3
372
+ end
373
+
374
+ it "should be correctly decided whether it is empty or not" do
375
+ mm = Multimap.new
376
+
377
+ mm[:a] = []
378
+ mm[:b] = [:a, :b]
379
+ mm.should_not be_empty
380
+
381
+ mm[:a] = []
382
+ mm[:b] = []
383
+ mm.should be_empty
384
+ end
385
+ end
386
+
387
+ describe "being referred" do
388
+ before do
389
+ @mm = Multimap.new
390
+ @mm[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
391
+ @mm[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
392
+ end
393
+
394
+ it "should be correctly iterated by 'each_pair'" do
395
+ tmp_a = []
396
+ tmp_b = []
397
+ @mm.each_pair do |key, sval|
398
+ case key
399
+ when :a
400
+ tmp_a << sval
401
+ when :b
402
+ tmp_b << sval
403
+ else
404
+ raise
405
+ end
406
+ end
407
+ tmp_a.sort.should == ["foo", "foo", "bar", "hoge", "hoge", "moe"].sort
408
+ tmp_b.sort.should == ["foo", "bar", "hoge", "hoge", "bar"].sort
409
+ end
410
+
411
+ it "should be correctly iterated by 'each_pair_with'" do
412
+ tmp_a = []
413
+ tmp_b = []
414
+ @mm.each_pair_with do |key, val, cnt|
415
+ case key
416
+ when :a
417
+ tmp_a << val
418
+ when :b
419
+ tmp_b << val
420
+ else
421
+ raise
422
+ end
423
+ end
424
+ tmp_a.sort.should == ["foo", "foo", "bar", "hoge", "hoge", "moe"].uniq.sort
425
+ tmp_b.sort.should == ["foo", "bar", "hoge", "hoge", "bar"].uniq.sort
426
+ end
427
+
428
+ it "should be correctly iterated by 'each_pair_list'" do
429
+ flag = 0
430
+ @mm.each_pair_list do |key, vals|
431
+ case key
432
+ when :a
433
+ vals.should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
434
+ flag += 1
435
+ when :b
436
+ vals.should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
437
+ flag += 1
438
+ else
439
+ raise
440
+ end
441
+ end
442
+ flag.should == 2
443
+ end
444
+
445
+ it "should ignore non-existent key by 'each_key'" do
446
+ @mm[:c] = []
447
+
448
+ ms = Multiset.new
449
+ @mm.each_key{ |key| ms << key }
450
+ ms.should == Multiset[:a, :b]
451
+ end
452
+
453
+ it "should be correctly iterated by 'each_value'" do
454
+ ms = Multiset.new
455
+ @mm.each_value{ |sval| ms << sval }
456
+ ms.should == {"foo" => 3, "bar" => 3, "hoge" => 4, "moe" => 1}.to_multiset
457
+ end
458
+ end
459
+
460
+ describe "being updated" do
461
+ before do
462
+ @mm = Multimap.new
463
+ @mm[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
464
+ @mm[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
465
+ end
466
+
467
+ it "should release items when 'delete'd" do
468
+ @mm.delete(:a).should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
469
+ @mm[:a].should == Multiset[]
470
+ @mm[:b].should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
471
+ end
472
+
473
+ it "should not release items when 'reject'ed ('reject'ed from only return value)" do
474
+ mm1 = @mm.reject{ |key, sval| sval =~ /o/ }
475
+
476
+ mm1[:a].should == Multiset["bar"]
477
+ mm1[:b].should == Multiset["bar", "bar"]
478
+ mm1.should_not == @mm
479
+ end
480
+
481
+ it "should equal to 'reject!'ed multimap when 'delete_if' is applied" do
482
+ mm1 = @mm.dup
483
+ mm2 = @mm.dup
484
+
485
+ mm1.delete_if{ |key, sval| sval =~ /o/ }
486
+ mm2.reject!{ |key, sval| sval =~ /o/ }
487
+ mm1.should == mm2
488
+
489
+ mm1.delete_if{ |key, sval| sval =~ /x/ }
490
+ mm2.reject!{ |key, sval| sval =~ /x/ }
491
+ mm1.should == mm2
492
+ end
493
+
494
+ it "should return nil when 'reject!' rejects nothing but 'delete_if' not" do
495
+ mm1 = @mm.dup
496
+ mm2 = @mm.dup
497
+
498
+ mm1.delete_if{ |key, sval| sval =~ /o/ }.should == mm1
499
+ mm2.reject!{ |key, sval| sval =~ /o/ }.should == mm2
500
+
501
+ mm1.delete_if{ |key, sval| sval =~ /x/ }.should == mm1
502
+ mm2.reject!{ |key, sval| sval =~ /x/ }.should == nil
503
+ end
504
+
505
+ it "should not release items when 'reject_with' is applied ('reject'ed from only return value)" do
506
+ # reject_with
507
+ mm1 = @mm.reject_with{ |key, val, cnt| cnt >= 2 }
508
+
509
+ mm1[:a].should == Multiset["bar", "moe"]
510
+ mm1[:b].should == Multiset["foo"]
511
+ mm1.should_not == @mm
512
+ end
513
+
514
+ it "should release items when 'delete_with' is applied" do
515
+ retval = @mm.delete_with{ |key, val, cnt| cnt >= 2 }
516
+ retval.should == @mm
517
+ @mm[:a].should == Multiset["bar", "moe"]
518
+ @mm[:b].should == Multiset["foo"]
519
+ end
520
+
521
+ it "should release items when 'delete_with' is applied (but not deleted anything)" do
522
+ retval = @mm.delete_with{ |key, val, cnt| cnt >= 2 && val.length > 4 }
523
+ retval.should == @mm
524
+ @mm[:a].should == Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"]
525
+ @mm[:b].should == Multiset["foo", "bar", "hoge", "hoge", "bar"]
526
+ end
527
+ end
528
+
529
+ describe "being checked its properties" do
530
+ before do
531
+ @mm_base = Multimap.new
532
+ @mm_base[:a] = ["foo", "foo", "bar", "hoge", "hoge", "moe"]
533
+ @mm_base[:b] = ["foo", "bar", "hoge", "hoge", "bar"]
534
+ end
535
+
536
+ # keys, values
537
+ it "should return existing (nonzero) keys by Multimap#keys" do
538
+ mm = @mm_base.dup
539
+ mm[:c] = []
540
+ mm.keys.should satisfy{ |ks| ks == [:a, :b] || ks == [:b, :a] }
541
+ end
542
+
543
+ it "should return all values by Multimap#values" do
544
+ @mm_base.values.should == {"foo" => 3, "bar" => 3, "hoge" => 4, "moe" => 1}.to_multiset
545
+ end
546
+
547
+ # empty?
548
+ it "should return true by Multimap#empty? if and only if it is empty" do
549
+ mm = @mm_base.dup
550
+ mm[:a] = []
551
+ mm.delete :b
552
+ mm.should be_empty
553
+
554
+ mm[:c] = [:x]
555
+ mm.should_not be_empty
556
+ end
557
+
558
+ # has_key?
559
+ it "should return true by Multimap#has_key? if and only if it has the given key" do
560
+ mm = @mm_base.dup
561
+ mm[:c] = []
562
+ mm[:d] = [:z]
563
+ mm.should have_key(:a)
564
+ mm.should have_key(:b)
565
+ mm.should_not have_key(:c)
566
+ mm.should be_member(:d)
567
+ mm.should_not be_member(:x)
568
+ end
569
+
570
+ # has_value?
571
+ it "should return true by Multimap#has_value? if and only if it has the given value" do
572
+ mm = @mm_base.dup
573
+ mm[:c] = []
574
+ mm[:d] = [:z]
575
+ mm.should have_value("foo")
576
+ mm.should have_value(:z)
577
+ mm.should_not have_value("boo")
578
+ mm.should be_value("hoge")
579
+ mm.should_not be_value(:d)
580
+ end
581
+
582
+ # index
583
+ it "should find a value and return a key by Multimap#index" do
584
+ mm = @mm_base.dup
585
+ mm.index("moe").should == :a
586
+ [:a, :b].should be_member(mm.index("hoge"))
587
+ mm.index("boo").should == nil
588
+ end
589
+
590
+ # values_at
591
+ it "should find a corresponding values as multisets by Multimap#values_at" do
592
+ mm = @mm_base.dup
593
+ mm[:x] = []
594
+
595
+ mm.values_at(:b, :x, :a).should == [
596
+ Multiset["foo", "bar", "hoge", "hoge", "bar"],
597
+ Multiset[],
598
+ Multiset["foo", "foo", "bar", "hoge", "hoge", "moe"],
599
+ ]
600
+
601
+ mm.indexes(:b, :x, :a).should == mm.values_at(:b, :x, :a)
602
+ end
603
+
604
+ # length, size
605
+ it "should return the collect number of entries by Multimap#length/size" do
606
+ mm = @mm_base.dup
607
+ mm.length.should == 11
608
+ mm[:c] = [:y]
609
+ mm.length.should == 12
610
+ mm.length.should == mm.size
611
+ end
612
+ end
613
+ end
614
+
615
+ describe Hash, "when converted to a multimap" do
616
+ it "should regard keys as multimap keys and values as multisets by Hash#to_multimap" do
617
+ # to_multimap / multimap
618
+ mm = {:a => [3, 3], :b => [5, 3]}.to_multimap
619
+ mm[:a].should == Multiset[3, 3]
620
+ mm[:b].should == Multiset[5, 3]
621
+ end
622
+
623
+ it "should regard keys as multimap keys and values as multisets by Hash#multimap" do
624
+ mm = {:a => [3, 3], :b => [5, 3]}.multimap
625
+ mm[:a].should == Multiset[[3, 3]]
626
+ mm[:b].should == Multiset[[5, 3]]
627
+ end
12
628
  end
629
+