immutable-ruby 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f0b9336a378ad7d4e2cf98b8977c0dbac13ed74b
4
- data.tar.gz: 2ec6d56a86049914a7c3e9807277a6134f5f4842
2
+ SHA256:
3
+ metadata.gz: d4dfc5d75d70e963c56837a552ac1902889f05f2fd73a3995c69959815780594
4
+ data.tar.gz: b9daefb94ad6bd5e3bf3a0bc7b74265a9d97188856ab78508f2cdf813b342636
5
5
  SHA512:
6
- metadata.gz: 02a4a9fa583aa94d5a609b8654247d486e41c51efcf0e3f3447ec47f6fd31e2f204e65dc906b6c6b82341dfdcc826b1a9ed55d9a4c8022e88508068999859d2f
7
- data.tar.gz: 4246f4d3457284cd82c13ed20b92f1b8d00e9cad02d30a0909f8f9d4194e331deac73fbaf5ebbaa1a4f395199c68f90cfdfef68403ac1c6a81edaf5b5e5878dc
6
+ metadata.gz: 306f0ef37c0ba06cf729b230f8eb6ec850cf6ff6130fea45eaf125d307078617f8688bc02b2dd6a75aae11134a45172f7ca11a12e6693409c36d5f00b7f18fc0
7
+ data.tar.gz: '0039ef718ca215f12d6826ea6803c7e404fd211536095c5a4995ca04e30b0effefa11951a640bca03b37ee9834ff94bac7acd9d1ccaf2d08ba449204850e42cb'
@@ -110,10 +110,38 @@ module Immutable
110
110
  @front.last # memoize?
111
111
  end
112
112
 
113
+ # Return a new `Deque` with elements rotated by `n` positions.
114
+ # A positive rotation moves elements to the right, negative to the left, and 0 is a no-op.
115
+ #
116
+ # @example
117
+ # Immutable::Deque["A", "B", "C"].rotate(1)
118
+ # # => Immutable::Deque["C", "A", "B"]
119
+ # Immutable::Deque["A", "B", "C"].rotate(-1)
120
+ # # => Immutable::Deque["B", "C", "A"]
121
+ #
122
+ # @param n [Integer] number of positions to move elements by
123
+ # @return [Deque]
124
+ def rotate(n)
125
+ return self.class.empty if self.empty?
126
+
127
+ n %= self.size
128
+ return self if n == 0
129
+
130
+ a, b = @front, @rear
131
+
132
+ if b.size >= n
133
+ n.times { a = a.cons(b.head); b = b.tail }
134
+ else
135
+ (self.size - n).times { b = b.cons(a.head); a = a.tail }
136
+ end
137
+
138
+ self.class.alloc(a, b)
139
+ end
140
+
113
141
  # Return a new `Deque` with `item` added at the end.
114
142
  #
115
143
  # @example
116
- # Immutable::Deque["A", "B", "C"].add("Z")
144
+ # Immutable::Deque["A", "B", "C"].push("Z")
117
145
  # # => Immutable::Deque["A", "B", "C", "Z"]
118
146
  #
119
147
  # @param item [Object] The item to add
@@ -180,6 +208,13 @@ module Immutable
180
208
  self.class.empty
181
209
  end
182
210
 
211
+ # Return a new `Deque` with the same items, but in reverse order.
212
+ #
213
+ # @return [Deque]
214
+ def reverse
215
+ self.class.alloc(@rear, @front)
216
+ end
217
+
183
218
  # Return true if `other` has the same type and contents as this `Deque`.
184
219
  #
185
220
  # @param other [Object] The collection to compare with
@@ -107,7 +107,7 @@ module Immutable
107
107
  # Return true if `other` contains the same elements, in the same order.
108
108
  # @return [Boolean]
109
109
  def ==(other)
110
- self.eql?(other) || other.respond_to?(:to_ary) && to_ary.eql?(other.to_ary)
110
+ self.eql?(other) || (other.respond_to?(:to_ary) && to_ary == other.to_ary)
111
111
  end
112
112
 
113
113
  # Convert all the elements into strings and join them together, separated by
@@ -771,7 +771,7 @@ module Immutable
771
771
  # @param other [Object] The object to compare with
772
772
  # @return [Boolean]
773
773
  def ==(other)
774
- self.eql?(other) || (other.respond_to?(:to_hash) && to_hash.eql?(other.to_hash))
774
+ self.eql?(other) || (other.respond_to?(:to_hash) && to_hash == other.to_hash)
775
775
  end
776
776
 
777
777
  # Return true if this `Hash` is a proper superset of `other`, which means
@@ -10,8 +10,8 @@ module Immutable
10
10
  # Returns the number of key-value pairs in the trie.
11
11
  attr_reader :size
12
12
 
13
- def initialize(significant_bits, size = 0, entries = [], children = [])
14
- @significant_bits = significant_bits
13
+ def initialize(bitshift, size = 0, entries = [], children = [])
14
+ @bitshift = bitshift
15
15
  @entries = entries
16
16
  @children = children
17
17
  @size = size
@@ -19,7 +19,7 @@ module Immutable
19
19
 
20
20
  # Returns <tt>true</tt> if the trie contains no key-value pairs.
21
21
  def empty?
22
- size == 0
22
+ @size == 0
23
23
  end
24
24
 
25
25
  # Returns <tt>true</tt> if the given key is present in the trie.
@@ -29,15 +29,7 @@ module Immutable
29
29
 
30
30
  # Calls <tt>block</tt> once for each entry in the trie, passing the key-value pair as parameters.
31
31
  def each(&block)
32
- # TODO: Using block.call here is slower than using yield by 5-10%, but
33
- # the latter segfaults on ruby 2.2 and above. Once that is fixed and
34
- # broken versions are sufficiently old, we should revert back to yield
35
- # with a warning that the broken versions are unsupported.
36
- #
37
- # For more context:
38
- # * https://bugs.ruby-lang.org/issues/11451
39
- # * https://github.com/hamstergem/hamster/issues/189
40
- @entries.each { |entry| block.call(entry) if entry }
32
+ @entries.each { |entry| yield entry if entry }
41
33
  @children.each do |child|
42
34
  child.each(&block) if child
43
35
  end
@@ -65,7 +57,7 @@ module Immutable
65
57
 
66
58
  # @return [Trie] A copy of `self` with the given value associated with the
67
59
  # key (or `self` if no modification was needed because an identical
68
- # key-value pair wes already stored
60
+ # key-value pair was already stored
69
61
  def put(key, value)
70
62
  index = index_for(key)
71
63
  entry = @entries[index]
@@ -74,7 +66,7 @@ module Immutable
74
66
  entries = @entries.dup
75
67
  key = key.dup.freeze if key.is_a?(String) && !key.frozen?
76
68
  entries[index] = [key, value].freeze
77
- Trie.new(@significant_bits, @size + 1, entries, @children)
69
+ Trie.new(@bitshift, @size + 1, entries, @children)
78
70
  elsif entry[0].eql?(key)
79
71
  if entry[1].equal?(value)
80
72
  self
@@ -82,7 +74,7 @@ module Immutable
82
74
  entries = @entries.dup
83
75
  key = key.dup.freeze if key.is_a?(String) && !key.frozen?
84
76
  entries[index] = [key, value].freeze
85
- Trie.new(@significant_bits, @size, entries, @children)
77
+ Trie.new(@bitshift, @size, entries, @children)
86
78
  end
87
79
  else
88
80
  child = @children[index]
@@ -94,12 +86,12 @@ module Immutable
94
86
  children = @children.dup
95
87
  children[index] = new_child
96
88
  new_self_size = @size + (new_child.size - child.size)
97
- Trie.new(@significant_bits, new_self_size, @entries, children)
89
+ Trie.new(@bitshift, new_self_size, @entries, children)
98
90
  end
99
91
  else
100
92
  children = @children.dup
101
- children[index] = Trie.new(@significant_bits + 5).put!(key, value)
102
- Trie.new(@significant_bits, @size + 1, @entries, children)
93
+ children[index] = Trie.new(@bitshift + 5).put!(key, value)
94
+ Trie.new(@bitshift, @size + 1, @entries, children)
103
95
  end
104
96
  end
105
97
  end
@@ -141,14 +133,14 @@ module Immutable
141
133
  end
142
134
  else
143
135
  new_children ||= @children.dup
144
- new_children[index] = Trie.new(@significant_bits + 5).put!(key, value)
136
+ new_children[index] = Trie.new(@bitshift + 5).put!(key, value)
145
137
  new_size += 1
146
138
  end
147
139
  end
148
140
  end
149
141
 
150
142
  if new_entries || new_children
151
- Trie.new(@significant_bits, new_size, new_entries || @entries, new_children || @children)
143
+ Trie.new(@bitshift, new_size, new_entries || @entries, new_children || @children)
152
144
  else
153
145
  self
154
146
  end
@@ -172,7 +164,7 @@ module Immutable
172
164
  @children[index] = child.put!(key, value)
173
165
  @size += child.size - old_child_size
174
166
  else
175
- @children[index] = Trie.new(@significant_bits + 5).put!(key, value)
167
+ @children[index] = Trie.new(@bitshift + 5).put!(key, value)
176
168
  @size += 1
177
169
  end
178
170
  end
@@ -193,7 +185,7 @@ module Immutable
193
185
 
194
186
  # Returns a copy of <tt>self</tt> with the given key (and associated value) deleted. If not found, returns <tt>self</tt>.
195
187
  def delete(key)
196
- find_and_delete(key) || Trie.new(@significant_bits)
188
+ find_and_delete(key) || Trie.new(@bitshift)
197
189
  end
198
190
 
199
191
  # Delete multiple elements from a Trie. This is more efficient than
@@ -238,7 +230,7 @@ module Immutable
238
230
  end
239
231
 
240
232
  if new_entries || new_children
241
- Trie.new(@significant_bits, new_size, new_entries || @entries, new_children || @children)
233
+ Trie.new(@bitshift, new_size, new_entries || @entries, new_children || @children)
242
234
  else
243
235
  self
244
236
  end
@@ -297,7 +289,7 @@ module Immutable
297
289
  children = @children.dup
298
290
  children[index] = copy
299
291
  new_size = @size - (child.size - copy_size(copy))
300
- return Trie.new(@significant_bits, new_size, @entries, children)
292
+ return Trie.new(@bitshift, new_size, @entries, children)
301
293
  end
302
294
  end
303
295
  end
@@ -318,14 +310,14 @@ module Immutable
318
310
  else
319
311
  entries[index] = nil
320
312
  end
321
- Trie.new(@significant_bits, @size - 1, entries, children || @children)
313
+ Trie.new(@bitshift, @size - 1, entries, children || @children)
322
314
  end
323
315
  end
324
316
 
325
317
  private
326
318
 
327
319
  def index_for(key)
328
- (key.hash.abs >> @significant_bits) & 31
320
+ (key.hash.abs >> @bitshift) & 31
329
321
  end
330
322
 
331
323
  def copy_size(copy)
@@ -334,5 +326,5 @@ module Immutable
334
326
  end
335
327
 
336
328
  # @private
337
- EmptyTrie = Trie.new(0)
329
+ EmptyTrie = Trie.new(0).freeze
338
330
  end
@@ -1,5 +1,5 @@
1
1
  module Immutable
2
2
  # Current released gem version. Note that master will often have the same
3
3
  # value as a release gem but with different code.
4
- VERSION = "0.0.3"
4
+ VERSION = "0.0.4"
5
5
  end
@@ -26,7 +26,7 @@ describe Immutable::Deque do
26
26
  context "on empty subclass" do
27
27
  let(:subclass) { Class.new(Immutable::Deque) }
28
28
  let(:empty_instance) { subclass.new }
29
- it "returns emtpy object of same class" do
29
+ it "returns empty object of same class" do
30
30
  empty_instance.send(method).class.should be subclass
31
31
  end
32
32
  end
@@ -0,0 +1,68 @@
1
+ require "spec_helper"
2
+
3
+ describe Immutable::Deque do
4
+
5
+ # Deques can have items distributed differently between the 'front' and 'rear' lists
6
+ # and still be equivalent
7
+ # Since the implementation of #rotate depends on how items are distributed between the
8
+ # two lists, we need to test both the case where most items are on the 'front' and
9
+ # where most are on the 'rear'
10
+ big_front = D.alloc(L.from_enum([1, 2, 3]), L.from_enum([5, 4]))
11
+ big_rear = D.alloc(L.from_enum([1, 2]), L.from_enum([5, 4, 3]))
12
+
13
+ describe "#rotate" do
14
+ [
15
+ [[], 9999, []],
16
+ [['A'], -1, ['A']],
17
+ [['A', 'B', 'C'], -1, ['B', 'C', 'A']],
18
+ [['A', 'B', 'C', 'D'], 0, ['A', 'B', 'C', 'D']],
19
+ [%w[A B C D], 2, %w[C D A B]],
20
+ ].each do |values, rotation, expected|
21
+ context "on #{values.inspect}" do
22
+ let(:deque) { D[*values] }
23
+
24
+ it "preserves the original" do
25
+ deque.rotate(rotation)
26
+ deque.should eql(D[*values])
27
+ end
28
+
29
+ it "returns #{expected.inspect}" do
30
+ deque.rotate(rotation).should eql(D[*expected])
31
+ end
32
+
33
+ it "returns a frozen instance" do
34
+ deque.rotate(rotation).should be_frozen
35
+ end
36
+ end
37
+ end
38
+
39
+ context "on a Deque with most items on 'front' list" do
40
+ it "works with a small rotation" do
41
+ big_front.rotate(2).should eql(D[4, 5, 1, 2, 3])
42
+ end
43
+
44
+ it "works with a larger rotation" do
45
+ big_front.rotate(4).should eql(D[2, 3, 4, 5, 1])
46
+ end
47
+ end
48
+
49
+ context "on a Deque with most items on 'rear' list" do
50
+ it "works with a small rotation" do
51
+ big_rear.rotate(2).should eql(D[4, 5, 1, 2, 3])
52
+ end
53
+
54
+ it "works with a larger rotation" do
55
+ big_rear.rotate(4).should eql(D[2, 3, 4, 5, 1])
56
+ end
57
+ end
58
+
59
+ context "on empty subclass" do
60
+ let(:subclass) { Class.new(Immutable::Deque) }
61
+ let(:empty_instance) { subclass.new }
62
+ it "returns an empty object of the same class" do
63
+ empty_instance.rotate(1).class.should be subclass
64
+ empty_instance.rotate(-1).class.should be subclass
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,4 +1,5 @@
1
1
  require "spec_helper"
2
+ require "bigdecimal"
2
3
 
3
4
  describe Immutable::Hash do
4
5
  let(:hash) { H["A" => "aye", "B" => "bee", "C" => "see"] }
@@ -33,6 +34,11 @@ describe Immutable::Hash do
33
34
  instance = subclass.new("A" => "aye", "B" => "bee", "C" => "see")
34
35
  (hash == instance).should == true
35
36
  end
37
+
38
+ it "performs numeric conversions between floats and BigDecimals" do
39
+ expect(H[a: 0.0] == H[a: BigDecimal('0.0')]).to be true
40
+ expect(H[a: BigDecimal('0.0')] == H[a: 0.0]).to be true
41
+ end
36
42
  end
37
43
 
38
44
  [:eql?, :==].each do |method|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: immutable-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dowad
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2018-05-12 00:00:00.000000000 Z
14
+ date: 2019-05-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: concurrent-ruby
@@ -167,6 +167,7 @@ files:
167
167
  - spec/lib/immutable/deque/pretty_print_spec.rb
168
168
  - spec/lib/immutable/deque/push_spec.rb
169
169
  - spec/lib/immutable/deque/random_modification_spec.rb
170
+ - spec/lib/immutable/deque/rotate_spec.rb
170
171
  - spec/lib/immutable/deque/shift_spec.rb
171
172
  - spec/lib/immutable/deque/size_spec.rb
172
173
  - spec/lib/immutable/deque/to_a_spec.rb
@@ -504,8 +505,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
504
505
  - !ruby/object:Gem::Version
505
506
  version: '0'
506
507
  requirements: []
507
- rubyforge_project:
508
- rubygems_version: 2.6.13
508
+ rubygems_version: 3.0.1
509
509
  signing_key:
510
510
  specification_version: 4
511
511
  summary: Efficient, immutable, thread-safe collection classes for Ruby
@@ -606,6 +606,7 @@ test_files:
606
606
  - spec/lib/immutable/deque/copying_spec.rb
607
607
  - spec/lib/immutable/deque/pop_spec.rb
608
608
  - spec/lib/immutable/deque/to_a_spec.rb
609
+ - spec/lib/immutable/deque/rotate_spec.rb
609
610
  - spec/lib/immutable/deque/marshal_spec.rb
610
611
  - spec/lib/immutable/set/difference_spec.rb
611
612
  - spec/lib/immutable/set/disjoint_spec.rb