setfu 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/setfu.rb CHANGED
@@ -1,13 +1,256 @@
1
1
  require "setfu/version"
2
2
  require "prime"
3
+ require "betterobject"
4
+ require "yieldhelper"
5
+ require "primitive_wrapper"
6
+
7
+ ####
8
+ #### DOC add eql?
9
+ ####
10
+
11
+ class Object
12
+ better_install_as!(:push_unique, :setfu_push_unique)
13
+ better_install_as!(:tag, :setfu_tag)
14
+ better_install_as!(:tag, :setfu_count)
15
+ better_install_as!(:delete_many_at, :setfu_delete_many_at)
16
+ end
17
+
18
+ module Setfu
19
+ def self.bset_elements(ary)
20
+ ary.count.times do |ii|
21
+ ary[ii] = ary[ii].to_bset
22
+ end
23
+ end
24
+ def self.untag_bset_elements(ary)
25
+ ary.count.times do |ii|
26
+ ary[ii].remove_instance_variable :@bo_setfu_tag rescue :never_mind
27
+ ary[ii].remove_instance_variable :@bo_setfu_count rescue :never_mind
28
+ end
29
+ end
30
+ def self.find_tuple(ary, tup_size) # assumes ary is comprised of sets
31
+ candidates = {}
32
+ entropy = 0
33
+ ary.count.times do |ii|
34
+ entropy = ary[ii].entropy > entropy ? ary[ii].entropy : entropy
35
+ unless ary[ii].setfu_count?
36
+ ary[ii].setfu_count = ary[ii].count # store this as #count is slow
37
+ end
38
+ next unless ary[ii].setfu_count == tup_size
39
+ tup = ary[ii].to_i # integer keys are good Hash keys, BitSets are not
40
+ candidates[tup] = 0 # store integer version of set in candidate hash if size matches
41
+ ary.count.times do |jj|
42
+ candidates[tup] +=1 if ary[ii]==ary[jj] # vote count each match
43
+ end
44
+ end
45
+ valid = []
46
+ error = []
47
+ candidates.each_pair do |key,val|
48
+ bset = BitSet.new
49
+ bset.set_bits!(key)
50
+ bset.entropy=entropy
51
+ if val==tup_size
52
+ bset.setfu_tag
53
+ valid.push bset
54
+ elsif val>tup_size
55
+ error.push bset
56
+ end
57
+ end
58
+ group = []
59
+ ary.count.times do |ii|
60
+ next if ary[ii].setfu_tag?
61
+ next if ary[ii].setfu_count > tup_size
62
+ next if ary[ii] <= valid
63
+ next if ary[ii] <= error
64
+ group.push ary[ii]
65
+ end
66
+
67
+ if (tup_size >=3) && (group.count >= tup_size) # stardard match finds smaller things ...
68
+ ptup = []
69
+ group.combination(tup_size) do |combo|
70
+ ta = combo.to_bset
71
+ next unless ta.count == tup_size
72
+ next if ta <= error
73
+ if ta <= ptup # we have an error! There can only be one tuple
74
+ pp = []
75
+ ptup.count.times do |ii|
76
+ pp.push ptup[ii] unless ta==ptup[ii]
77
+ end
78
+ ptup = pp
79
+ error.push ta
80
+ else
81
+ ptup.push ta
82
+ end
83
+ end
84
+ valid.concat ptup
85
+ end
86
+
87
+ # ok everything is in the bin ... what about the shorts of the previous calls?
88
+ # let's just mark what we know for next time ...
89
+ ary.count.times do |ii|
90
+ next if ary[ii].setfu_tag?
91
+ if ary[ii] <= valid
92
+ ary[ii].setfu_tag = :valid
93
+ elsif ary[ii] <= error
94
+ ary[ii].setfu_tag = :error
95
+ end
96
+ end
97
+
98
+ return [valid,error]
99
+ end
100
+
101
+ def self.tuples(ary) # errors include empty sets
102
+ src = []
103
+ valid = []
104
+ error = []
105
+ ary.each do |bs|
106
+ if bs.empty?
107
+ error.setfu_push_unique bs
108
+ else
109
+ src.push bs
110
+ end
111
+ end
112
+ src.count.times do |tup_no|
113
+ rslt = find_tuple(src, tup_no)
114
+ valid.concat rslt[0]
115
+ error.concat rslt[1] # was 2 ... but removed short
116
+ end
117
+ src.count.times do |idx|
118
+ next if src[idx] <= valid
119
+ next if src[idx] <= error
120
+ end
121
+ return [valid,error] if true
122
+ end
123
+
124
+ def self.reduce(ary)
125
+ elms = ary.count
126
+ # process tupples ... first check for errors
127
+ tups = tuples(ary)
128
+ unless (tups.last.empty?)
129
+ ary.setfu_tag = tups.last # tag with broken set items
130
+ return -1 # can't reduce a system of errors
131
+ end
132
+ if (tups.first.empty?)
133
+ return 0
134
+ end
135
+ tups = tups[0].reverse # we only need the good ones.
136
+ reductions = 0
137
+ loop do
138
+ tup = tups.pop
139
+ elms.times do |idx|
140
+ if (tup ** ary[idx]) && !(ary[idx] <= tup)
141
+ ary[idx] -= tup
142
+ reductions += 1
143
+ end
144
+ end
145
+ break if tups.empty?
146
+ end
147
+ return reductions
148
+ end
149
+
150
+ # returns the tuple, else nil
151
+ def self.tuple(ary)
152
+ tup = ary.to_bset
153
+ return nil unless tup.size==ary.count
154
+ return tup if ary.size == 1 # trivial case
155
+ if ary.size==2
156
+ return tup if ary.first==ary.last
157
+ end
158
+ ary.count.times do |idx|
159
+ ta = ary - [ary[idx]]
160
+ return nil if tuple?(ta)
161
+ end
162
+ return tup
163
+ end
164
+
165
+ # the whole thing must be a tuple else false
166
+ def self.tuple?(ary) # from documentation
167
+ return false if ary.empty?
168
+ tup = ary.to_bset
169
+ return false unless tup.size==ary.count
170
+ return true if ary.size == 1 # trivial case
171
+ if ary.size==2
172
+ return true if ary.first==ary.last
173
+ end
174
+ ary.count.times do |idx|
175
+ ta = ary - [ary[idx]]
176
+ return false if tuple?(ta)
177
+ end
178
+ return true
179
+ end
180
+ end
181
+
182
+ class BitSet
183
+ def untag
184
+ BitSet.new | self
185
+ end
186
+ def untag!
187
+ remove_instance_variable :@bo_setfu_tag rescue :never_mind
188
+ remove_instance_variable :@bo_setfu_count rescue :never_mind
189
+ end
190
+ end
191
+
192
+ class Array
193
+ def members_to_bset
194
+ Setfu::bset_elements(self)
195
+ self
196
+ end
197
+ def tuples
198
+ members_to_bset
199
+ rtn = Setfu::tuples(self)
200
+ rtn
201
+ end
202
+ def tuple?
203
+ Setfu::tuple?(self)
204
+ end
205
+ def tuple
206
+ Setfu::tuple(self)
207
+ end
208
+ def reduce_tuples(passes = 1)
209
+ Setfu::bset_elements(self)
210
+ Setfu::untag_bset_elements(self)
211
+ total_count = 0
212
+ loop do
213
+ tc = Setfu::reduce(self)
214
+ if tc < 0
215
+ total_count = -1
216
+ break
217
+ end
218
+ break if tc == 0
219
+ total_count += tc
220
+ passes -= 1
221
+ break if passes <= 0
222
+ end
223
+ total_count
224
+ end
225
+ def untag_bset_elements
226
+ Setfu::untag_bset_elements(self)
227
+ end
228
+ end
3
229
 
4
230
  class BitSet
5
231
  attr_reader :entropy
232
+
233
+ # can't do this ... already using operators for something else
234
+ =begin
235
+ include Comparable
236
+ def <=>(other)
237
+ @bits <=> other.to_bset.to_i
238
+ end
239
+ =end
6
240
 
7
241
  def entropy=(ent) # entropy only gets bigger, just like the universe!
8
242
  @entropy = ent unless @entropy > ent
9
243
  end
10
244
 
245
+ def self.fill(n_elms, start=0)
246
+ set = BitSet.new
247
+ bits = (1 << n_elms) - 1
248
+ set.set_bits!(bits)
249
+ set.entropy=n_elms
250
+ set.inc!(start.ord)
251
+ return set
252
+ end
253
+
11
254
  def self.uppercase_chars
12
255
  set = BitSet.new
13
256
  set.set_bits!(2475880041677272402379145216)
@@ -176,6 +419,10 @@ class BitSet
176
419
  self
177
420
  end
178
421
 
422
+ def size
423
+ count
424
+ end
425
+
179
426
  def inc!(n=1)
180
427
  raise "illegal negative parameter in #inc" if n < 0
181
428
  @entropy += n
@@ -237,7 +484,7 @@ class BitSet
237
484
 
238
485
  def ^(item)
239
486
  rtn = self.dup
240
- if(item.class==BitSet)
487
+ if(item.type_of? BitSet)
241
488
  rtn.set_bits!(rtn.to_i ^ item.to_i)
242
489
  else
243
490
  rtn = BitSet.new(item)
@@ -248,7 +495,7 @@ class BitSet
248
495
 
249
496
  def |(item)
250
497
  rtn = self.dup
251
- if(item.class==BitSet)
498
+ if(item.type_of? BitSet)
252
499
  rtn.set_bits!(rtn.to_i | item.to_i)
253
500
  self.entropy=item.entropy
254
501
  else
@@ -260,7 +507,7 @@ class BitSet
260
507
 
261
508
  def &(item)
262
509
  rtn = self.dup
263
- if(item.class==BitSet)
510
+ if(item.type_of? BitSet)
264
511
  rtn.set_bits!(rtn.to_i & item.to_i)
265
512
  else
266
513
  rtn = BitSet.new(item)
@@ -273,7 +520,7 @@ class BitSet
273
520
  rtn = BitSet.new
274
521
  rtn.entropy = self.entropy
275
522
  a = self.to_i
276
- if(item.class==BitSet)
523
+ if(item.type_of? BitSet)
277
524
  b = item.to_i
278
525
  rtn.entropy = item.entropy
279
526
  else
@@ -295,7 +542,7 @@ class BitSet
295
542
 
296
543
  # comparison operators:
297
544
  def ==(item)
298
- if(item.class==BitSet)
545
+ if(item.type_of? BitSet)
299
546
  rtn = item.to_i == self.to_i
300
547
  else
301
548
  rtn = BitSet.new(item).to_i == self.to_i
@@ -304,7 +551,7 @@ class BitSet
304
551
  end
305
552
 
306
553
  def !=(item)
307
- if(item.class==BitSet)
554
+ if(item.type_of? BitSet)
308
555
  rtn = item.to_i != self.to_i
309
556
  else
310
557
  rtn = BitSet.new(item).to_i != self.to_i
@@ -312,6 +559,10 @@ class BitSet
312
559
  rtn
313
560
  end
314
561
 
562
+ def eql?(item)
563
+ self.==(item)
564
+ end
565
+
315
566
  def set_case(mode=:mode_equal)
316
567
  @mode = mode
317
568
  self
@@ -352,13 +603,15 @@ class BitSet
352
603
  end
353
604
 
354
605
  def add!(items)
355
- if(items.class==BitSet)
606
+ if(items.type_of? BitSet)
356
607
  @bits |= items.to_i
357
608
  entropy = items.entropy
358
- elsif(items.class==Range)
359
- f=items.first.ord
360
- l=items.last.ord
361
- f,l = l,f if f>l
609
+ elsif(items.type_of? Range)
610
+ ran = items.reorder.simplify
611
+ return add!(ran.to_a) unless ran.simple?
612
+ f=ran.first.ord
613
+ l=ran.last.ord
614
+ # f,l = l,f if f>l
362
615
  t = (l-f)+1
363
616
  t = (1 << t)-1
364
617
  @bits |= t << f
@@ -392,14 +645,16 @@ class BitSet
392
645
  def include?(items)
393
646
  return false if items.nil? # sets never include nil
394
647
  return false if @bits == 0 # empty sets include nothing including other empty sets
395
- if(items.class==BitSet)
648
+ if(items.type_of? BitSet)
396
649
  tmp = @bits & items.to_i
397
650
  return false if tmp==0
398
651
  return (tmp) == items.to_i
399
- elsif(items.class==Range)
400
- f=items.first.ord
401
- l=items.last.ord
402
- f,l = l,f if f>l
652
+ elsif(items.type_of? Range)
653
+ ran = items.reorder.simplify
654
+ return(include?(ran.to_a)) unless ran.simple?
655
+ f=ran.first.ord
656
+ l=ran.last.ord
657
+ # f,l = l,f if f>l
403
658
  t = (l-f)+1
404
659
  t = (1 << t)-1
405
660
  t = t << f
@@ -423,6 +678,7 @@ class BitSet
423
678
  end
424
679
 
425
680
  def reverse_each(*prms) # do a benchmark and see which is faster, reverse_each or each
681
+ return Enumerator.enumerate_yields(self, :reverse_each, *prms) unless block_given?
426
682
  int_mode = true
427
683
  set = self.dup
428
684
  prms.each do |prm|
@@ -433,20 +689,13 @@ class BitSet
433
689
  end
434
690
  end
435
691
  ary = set.to_a(int_mode)
436
- unless block_given?
437
- enu = Enumerator.new do |yy|
438
- while !ary.empty?
439
- yy << ary.pop
440
- end
441
- return enu
442
- end
443
- end
444
692
  while !ary.empty?
445
693
  yield ary.pop
446
694
  end
447
695
  end
448
696
 
449
697
  def each(*prms)
698
+ return Enumerator.enumerate_yields(self, :each, *prms) unless block_given?
450
699
  bits = @bits
451
700
  pos = 0
452
701
  stop = nil
@@ -463,21 +712,6 @@ class BitSet
463
712
  chr_mode = !prm
464
713
  end
465
714
  end
466
- unless block_given?
467
- enu = Enumerator.new do |yy|
468
- while bits > 0
469
- if ((bits & 1) == 1)
470
- yy << chr_mode ? pos.chr(Encoding::UTF_8) : pos
471
- end
472
- pos += 1
473
- unless stop.nil?
474
- break if pos > stop
475
- end
476
- bits >>= 1
477
- end # while
478
- end # do
479
- return enu
480
- end # unless
481
715
  while bits > 0
482
716
  if ((bits & 1) == 1)
483
717
  yield chr_mode ? pos.chr(Encoding::UTF_8) : pos
@@ -575,9 +809,9 @@ class BitSet
575
809
  def [](*pset)
576
810
  idx = nil
577
811
  if pset.count==1 # check for single instance inst[5], inst['t']
578
- if pset.first.kind_of? Integer
812
+ if pset.first.type_of? Integer
579
813
  idx = pset.first
580
- elsif pset.first.kind_of? String
814
+ elsif pset.first.type_of? String
581
815
  if pset.first.length == 1
582
816
  idx = pset.first.ord
583
817
  end
@@ -605,8 +839,7 @@ class BitSet
605
839
  end
606
840
 
607
841
  # :array :array_chars :string :set
608
- def rand(elm_count, format = :set)
609
- raise "rand minimum count too low" if elm_count < 1
842
+ def rand(elm_count, format = :bset)
610
843
  ary = self.to_a
611
844
  ary.shuffle!
612
845
  ary = ary[0..(elm_count-1)]
@@ -625,10 +858,36 @@ class BitSet
625
858
  rtn.push elm.chr(Encoding::UTF_8)
626
859
  end
627
860
  return rtn.join ""
628
- else # :set
861
+ else # :bset
629
862
  return ary.to_bset
630
863
  end
631
- end
864
+ end
865
+
866
+ def rand!(elm_count, format = :bset)
867
+ ary = self.to_a
868
+ rna = ary.shuffle[0..(elm_count-1)]
869
+ replace rna.to_bset
870
+ case format
871
+ when :array
872
+ return (ary - rna).shuffle
873
+ when :array_chars
874
+ src = (ary - rna).shuffle
875
+ rtn = []
876
+ src.each do |elm|
877
+ rtn.push elm.chr(Encoding::UTF_8)
878
+ end
879
+ return rtn
880
+ when :string
881
+ src = (ary - rna).shuffle
882
+ rtn = []
883
+ src.each do |elm|
884
+ rtn.push elm.chr(Encoding::UTF_8)
885
+ end
886
+ return rtn.join ""
887
+ else # :bset
888
+ return (ary - rna).to_bset
889
+ end
890
+ end
632
891
 
633
892
  end # end BitSet
634
893
 
@@ -675,7 +934,7 @@ end
675
934
  module SetFuMixinTrippleEqualsOperator
676
935
  alias_method :old_triple_equal4Set, :===
677
936
  def ===(item)
678
- return old_triple_equal4Set(item) unless (item.class==BitSet)
937
+ return old_triple_equal4Set(item) unless (item.type_of? BitSet)
679
938
  return self.to_bset === item
680
939
  end
681
940
  end
@@ -717,13 +976,13 @@ class String
717
976
  include SetFuMixinBinarySubtractOperator
718
977
 
719
978
  def <=(item)
720
- return old_string_lte4set(item) if (item.class==String)
979
+ return old_string_lte4set(item) if (item.type_of? String)
721
980
  a = BitSet.new(self)
722
981
  b = BitSet.new(item)
723
982
  return a <= b
724
983
  end
725
984
  def <(item)
726
- return old_string_lt4set(item) if (item.class==String)
985
+ return old_string_lt4set(item) if (item.type_of? String)
727
986
  a = BitSet.new(self)
728
987
  b = BitSet.new(item)
729
988
  return a < b
@@ -735,27 +994,37 @@ class Array
735
994
  alias_method :old_and_method4set, :&
736
995
  alias_method :old_or_method4set, :|
737
996
 
738
- include SetFuMixinBinaryXorOperator
997
+ # include SetFuMixinBinaryXorOperator
739
998
  include SetFuMixinBinaryIntersectionOperator
740
999
  include SetFuMixinToSetMethod
741
1000
  include SetFuMixinTrippleEqualsOperator
742
1001
  include SetFuMixinBinarySubsetOperator
743
1002
  include SetFuMixinBinaryProperOperator
1003
+
1004
+ def ^(item)
1005
+ if item.type_of? Array
1006
+ return (self | item) - (self & item)
1007
+ else
1008
+ a = BitSet.new(self)
1009
+ b = BitSet.new(item)
1010
+ return a ^ b
1011
+ end
1012
+ end
744
1013
 
745
1014
  def -(item)
746
- return old_subtract_method4set(item) if (item.class==Array)
1015
+ return old_subtract_method4set(item) if (item.type_of? Array)
747
1016
  a = BitSet.new(self)
748
1017
  b = BitSet.new(item)
749
1018
  return a - b
750
1019
  end
751
1020
  def &(item)
752
- return old_and_method4set(item) if (item.class==Array)
1021
+ return old_and_method4set(item) if (item.type_of? Array)
753
1022
  a = BitSet.new(self)
754
1023
  b = BitSet.new(item)
755
1024
  return a & b
756
1025
  end
757
1026
  def |(item)
758
- return old_or_method4set(item) if (item.class==Array)
1027
+ return old_or_method4set(item) if (item.type_of? Array)
759
1028
  a = BitSet.new(self)
760
1029
  b = BitSet.new(item)
761
1030
  return a | b
@@ -1139,6 +1408,27 @@ class BitSet
1139
1408
  @@UTF_UPPER_CASE_DUAL_SET = "".to_bset
1140
1409
  @@UTF_MIXED_CASE_DUAL_SET = "".to_bset
1141
1410
  end
1411
+
1412
+ def self.uppercase_utf_chars(return_set=true)
1413
+ return @@UTF_UPPER_CASE_SET.dup if return_set
1414
+ return @@UTF_UPPER_CASE_CHARS.dup
1415
+ end
1416
+ def self.lowercase_utf_chars(return_set=true)
1417
+ return @@UTF_LOWER_CASE_SET.dup if return_set
1418
+ return @@UTF_LOWER_CASE_CHARS.dup
1419
+ end
1420
+ def self.mixcase_utf_chars(return_set=true)
1421
+ return @@UTF_MIXED_CASE_DUAL_SET.dup if return_set
1422
+ return @@UTF_MIXED_CASE_DUAL_CHARS.dup
1423
+ end
1424
+ def self.dual_uppercase_utf_chars(return_set=true)
1425
+ return @@UTF_UPPER_CASE_DUAL_SET.dup if return_set
1426
+ return @@UTF_UPPER_CASE_DUAL_CHARS.dup
1427
+ end
1428
+ def self.dual_lowercase_utf_chars(return_set=true)
1429
+ return @@UTF_LOWER_CASE_DUAL_SET.dup if return_set
1430
+ return @@UTF_LOWER_CASE_DUAL_CHARS.dup
1431
+ end
1142
1432
 
1143
1433
  def self.modify_utf_sets(*prms)
1144
1434
  flag_add = true
@@ -1146,11 +1436,15 @@ class BitSet
1146
1436
  target_set = nil
1147
1437
  source = nil
1148
1438
  prms.each do |prm|
1149
- if prm.kind_of? String
1439
+ if prm.type_of? String
1150
1440
  source = prm
1151
- elsif prm.kind_of? BitSet
1441
+ elsif prm.type_of? BitSet
1152
1442
  source = prm
1153
- elsif prm.kind_of? Symbol
1443
+ elsif prm.type_of? Array
1444
+ source = prm
1445
+ elsif prm.type_of? Integer
1446
+ source = [prm]
1447
+ elsif prm.type_of? Symbol
1154
1448
  if prm == :rm
1155
1449
  flag_add = false
1156
1450
  elsif prm == :add
@@ -1175,32 +1469,61 @@ class BitSet
1175
1469
  end
1176
1470
  return false if target_chars.nil?
1177
1471
  return false if source.nil?
1178
- return false unless source ** target_set
1179
1472
  if flag_add
1180
1473
  eval("#{target_set} |= source")
1181
- eval("#{target_chars} |= #{target_set}.to_s")
1474
+ eval("#{target_chars} = #{target_set}.to_s")
1182
1475
  else
1183
1476
  eval("#{target_set} -= source")
1184
- eval("#{target_chars} -= #{target_set}.to_s")
1477
+ eval("#{target_chars} = #{target_set}.to_s")
1185
1478
  end
1186
1479
  return true
1187
1480
  end
1481
+
1482
+ def add_lowercase_utf
1483
+ return BitSet.lowercase_utf_chars | self
1484
+ end
1188
1485
 
1189
- def self.uppercase_utf_chars
1190
- return @@UTF_UPPER_CASE_SET.dup
1191
- end
1192
- def self.lowercase_utf_chars
1193
- return @@UTF_LOWER_CASE_SET.dup
1486
+ def add_lowercase_utf!
1487
+ add! BitSet.lowercase_utf_chars
1488
+ self
1489
+ end
1490
+
1491
+ def add_uppercase_utf
1492
+ return BitSet.uppercase_utf_chars | self
1194
1493
  end
1195
- def self.mixcase_utf_chars
1196
- return @@UTF_MIXED_CASE_DUAL_SET.dup
1494
+
1495
+ def add_uppercase_utf!
1496
+ add! BitSet.uppercase_utf_chars
1497
+ self
1197
1498
  end
1198
- def self.dual_uppercase_utf_chars
1199
- return @@UTF_UPPER_CASE_DUAL_SET.dup
1499
+
1500
+ def add_mixcase_utf
1501
+ return BitSet.mixcase_utf_chars | self
1200
1502
  end
1201
- def self.dual_lowercase_utf_chars
1202
- return @@UTF_LOWER_CASE_DUAL_SET.dup
1503
+
1504
+ def add_mixcase_utf!
1505
+ add! BitSet.mixcase_utf_chars
1506
+ self
1507
+ end
1508
+
1509
+ def add_dual_upper_utf
1510
+ return BitSet.dual_uppercase_utf_chars | self
1203
1511
  end
1512
+
1513
+ def add_dual_upper_utf!
1514
+ add! BitSet.dual_uppercase_utf_chars
1515
+ self
1516
+ end
1517
+
1518
+ def add_dual_lower_utf
1519
+ return BitSet.dual_lowercase_utf_chars | self
1520
+ end
1521
+
1522
+ def add_dual_lower_utf!
1523
+ add! BitSet.dual_lowercase_utf_chars
1524
+ self
1525
+ end
1526
+
1204
1527
  end
1205
1528
 
1206
1529
  =begin
data/setfu.gemspec CHANGED
@@ -24,6 +24,10 @@ When characters are used, the ordinate value sets the element bit of the interna
24
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
25
  spec.require_paths = ["lib"]
26
26
  spec.required_ruby_version = '>= 1.9.1'
27
+ spec.add_runtime_dependency 'pred', '~> 0.1', '>= 0.1.2'
28
+ spec.add_runtime_dependency 'betterobject', '~> 1.2', '>= 1.2.0'
29
+ spec.add_runtime_dependency 'yieldhelper', '~> 0.1', '>= 0.1.0'
30
+ spec.add_runtime_dependency 'primitive_wrapper', '~> 2', '>= 2.0.0'
27
31
 
28
32
  spec.add_development_dependency "bundler", "~> 1.3"
29
33
  spec.add_development_dependency 'rake', '~> 10.1', '>= 10.1.0'