linked 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 44eaaab09ac6111dd607d3cd52e35d2d072315bbc4614b7805254c8a7bab311a
4
- data.tar.gz: 4672f6d56f0dde64a6fe12ecaefdf82d39795a957d97ccb55eb0558ffe23285d
2
+ SHA1:
3
+ metadata.gz: d990f5f26ec08e5038257496d465b4435ad8a113
4
+ data.tar.gz: 7cb0f4acb27d81e950d99ef5dd9494f015d471c4
5
5
  SHA512:
6
- metadata.gz: c94e42bee0a6717053ff3c33de9f36c03ab4ba24d35ddb3b387ed78a3e4a54898269eb62237117a05db4cf4c96b5555c376cc507cb2d8cc02aed1a80059f69a3
7
- data.tar.gz: 479b9a24c6dc2a785993072e620695bebe35e9599cfb9f8842fd1a767d6783c5b0838f6ccd0abce247a155e416930fa76cc6c164cb6ae85e2e9aac23c7948c9f
6
+ metadata.gz: ef56ee302097e4b1a6240d38026cfbbd3d1a179777e7b5dbea23d490d10ed423c4fa3a19fbd159d5a43f2df63d732855283b7bfb7fb55ace4fb6fa5a6abe7244
7
+ data.tar.gz: 3c7236c0a480a14fa94ba4156c3606f4defdb400b4340ed173a3254a75dadba034f78d6cc0cc4ecf5cad7e5847f239318cd43a9b016330aff2a914023d0dd121
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
@@ -43,6 +43,7 @@ Metrics/BlockLength:
43
43
  Metrics/ModuleLength:
44
44
  Exclude:
45
45
  - 'test/**/*.rb'
46
+ Max: 120
46
47
 
47
48
  Metrics/ParameterLists:
48
49
  CountKeywordArgs: false
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ linked (0.5.2)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ docile (1.3.1)
11
+ json (2.1.0)
12
+ minitest (5.11.3)
13
+ parallel (1.12.1)
14
+ parser (2.5.1.0)
15
+ ast (~> 2.4.0)
16
+ powerpack (0.1.1)
17
+ rainbow (3.0.0)
18
+ rake (10.5.0)
19
+ redcarpet (3.4.0)
20
+ rubocop (0.55.0)
21
+ parallel (~> 1.10)
22
+ parser (>= 2.5)
23
+ powerpack (~> 0.1)
24
+ rainbow (>= 2.2.2, < 4.0)
25
+ ruby-progressbar (~> 1.7)
26
+ unicode-display_width (~> 1.0, >= 1.0.1)
27
+ ruby-progressbar (1.9.0)
28
+ simplecov (0.16.1)
29
+ docile (~> 1.1)
30
+ json (>= 1.8, < 3)
31
+ simplecov-html (~> 0.10.0)
32
+ simplecov-html (0.10.2)
33
+ unicode-display_width (1.3.2)
34
+ yard (0.9.12)
35
+
36
+ PLATFORMS
37
+ ruby
38
+
39
+ DEPENDENCIES
40
+ bundler (~> 1.16)
41
+ linked!
42
+ minitest (~> 5.0)
43
+ rake (~> 10.0)
44
+ redcarpet (~> 3.4)
45
+ rubocop (~> 0.52)
46
+ simplecov (~> 0.16)
47
+ yard (~> 0.9)
48
+
49
+ BUNDLED WITH
50
+ 1.16.3
data/README.md CHANGED
@@ -1,17 +1,11 @@
1
- # Linked
1
+ # 🔗 Linked
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/linked.svg)](https://badge.fury.io/rb/linked)
4
4
  [![Build Status](https://travis-ci.org/seblindberg/ruby-linked.svg?branch=master)](https://travis-ci.org/seblindberg/ruby-linked)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/seblindberg/ruby-linked/badge.svg?branch=master)](https://coveralls.io/github/seblindberg/ruby-linked?branch=master)
6
6
  [![Inline docs](http://inch-ci.org/github/seblindberg/ruby-linked.svg?branch=master)](http://inch-ci.org/github/seblindberg/ruby-linked)
7
7
 
8
- Yet another Linked List implementation for Ruby (hence the somewhat awkward name). The library is still under development. The intention is to
9
-
10
- 1. nail down the functionality,
11
- 2. start porting the methods over to c and finally
12
- 3. try to optimize for speed, as much as possible.
13
-
14
- The project is still in phase 1.
8
+ Yet another Linked List implementation for Ruby (hence the somewhat awkward name).
15
9
 
16
10
  ## Installation
17
11
 
@@ -63,7 +57,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
63
57
 
64
58
  ## Contributing
65
59
 
66
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/linked.
60
+ Bug reports and pull requests are welcome on GitHub at https://github.com/seblindberg/ruby-linked.
67
61
 
68
62
 
69
63
  ## License
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'forwardable'
4
+
3
5
  require 'linked/version'
4
6
  require 'linked/util'
7
+ require 'linked/mutable'
5
8
  require 'linked/listable'
6
9
  require 'linked/item'
7
10
  require 'linked/list_enumerable'
@@ -9,5 +12,5 @@ require 'linked/list'
9
12
 
10
13
  # Linked List implementation.
11
14
  module Linked
12
- private_constant :Util, :ListEnumerable
15
+ private_constant :Util
13
16
  end
@@ -10,6 +10,7 @@ module Linked
10
10
 
11
11
  # The Item can hold an arbitrary object as its value and it will stay with
12
12
  # the item.
13
+ #
13
14
  # @return [Object] any object that is stored in the item.
14
15
  attr_accessor :value
15
16
 
@@ -89,13 +89,7 @@ module Linked
89
89
  def pop
90
90
  return nil if empty?
91
91
 
92
- if list_tail.first?
93
- item = last
94
- @_chain = nil
95
- item
96
- else
97
- list_tail.delete
98
- end
92
+ list_tail.first? ? last.tap { @_chain = nil } : list_tail.delete
99
93
  end
100
94
 
101
95
  # Insert an item at the beginning of the list. If the given object is not an
@@ -121,9 +115,7 @@ module Linked
121
115
  return nil if empty?
122
116
 
123
117
  if list_head.last?
124
- item = @_chain
125
- @_chain = nil
126
- item
118
+ @_chain.tap { @_chain = nil }
127
119
  else
128
120
  old_head = list_head
129
121
  @_chain = list_head.next
@@ -173,6 +165,8 @@ module Linked
173
165
 
174
166
  alias inspect inspect_list
175
167
 
168
+ protected
169
+
176
170
  # Protected factory method for creating items compatible with the list. This
177
171
  # method is called whenever an arbitrary object is pushed or unshifted onto
178
172
  # the list and need to be wraped inside an Item.
@@ -182,37 +176,35 @@ module Linked
182
176
  # @param args [Array<Object>] the arguments that are to be passed on to
183
177
  # `Item.new`.
184
178
  # @return [Item] a new `Listable` item.
185
- protected def create_item(*args)
179
+ def create_item(*args)
186
180
  Item.new(*args)
187
181
  end
188
182
 
183
+ private
184
+
189
185
  # Takes an arbitrary object and coerces it into an item compliant with the
190
186
  # list. If the object is already an item it will be used as is. Otherwise
191
187
  # #create_item will be called with the object as an argument.
192
188
  #
193
- # @param [#item, Object] the object to coerce.
189
+ # @param object [#item, Object] the object to coerce.
194
190
  # @return [Listable] see `#create_item`.
195
- private def coerce_item(object)
196
- if object.respond_to? :item
197
- object.item
198
- else
199
- create_item object
200
- end
191
+ def coerce_item(object)
192
+ object.respond_to?(:item) ? object.item : create_item(object)
201
193
  end
202
194
 
203
195
  # Private method for clearing the list and bringing it to a pristine
204
196
  # state.
205
- private def reset_list
197
+ def reset_list
206
198
  @_chain = nil
207
199
  end
208
200
 
209
201
  # Returns the first item item in the list, or nil if empty.
210
- private def list_head
202
+ def list_head
211
203
  @_chain
212
204
  end
213
205
 
214
206
  # Returns an the last item in the list, or nil if empty.
215
- private def list_tail
207
+ def list_tail
216
208
  @_chain.last_in_chain
217
209
  end
218
210
  end
@@ -22,6 +22,9 @@ module Linked
22
22
 
23
23
  # Iterates over each item in the list in reverse order. If a block is not
24
24
  # given an enumerator is returned.
25
+ #
26
+ # @yield [Listable] each item in the list.
27
+ # @return [Enumerable] if no block is given.
25
28
  def reverse_each_item
26
29
  return to_enum(__method__) { count } unless block_given?
27
30
  return if empty?
@@ -37,12 +40,9 @@ module Linked
37
40
 
38
41
  # Access the first n item(s) in the list.
39
42
  #
40
- # n - the number of items to return.
41
- #
42
- # Returns, for different values of n:
43
- # n = nil) an item if the list contains one, or nil.
44
- # n >= 0) an array of between 0 and n items, depending on how many are in.
45
- # the list.
43
+ # @param n [Integer] the number of items to return.
44
+ # @return [Listable] if n = nil.
45
+ # @return [Array<Listable>] if n >= 0.
46
46
  def first(n = nil)
47
47
  return list_head unless n
48
48
  raise ArgumentError, 'n cannot be negative' if n.negative?
@@ -54,12 +54,9 @@ module Linked
54
54
 
55
55
  # Access the first n item(s) in the list.
56
56
  #
57
- # n - the number of items to return.
58
- #
59
- # Returns, for different values of n:
60
- # n = nil) an item if the list contains one, or nil.
61
- # n >= 0) an array of between 0 and n items, depending on how many are in.
62
- # the list. The order is preserved.
57
+ # @param n [Integer] the number of items to return.
58
+ # @return [Listable] if n = nil.
59
+ # @return [Array<Listable>] if n >= 0. The order is preserved.
63
60
  def last(n = nil)
64
61
  return empty? ? nil : list_tail unless n
65
62
  raise ArgumentError, 'n cannot be negative' if n.negative?
@@ -37,7 +37,9 @@ module Linked
37
37
  # Single items are denoted with capital letters (i), while chains are written
38
38
  # as multiple connected items (ii).
39
39
  module Listable
40
+ extend Forwardable
40
41
  include Util
42
+ include Mutable
41
43
 
42
44
  # Creates a new item. Always make a call to super whenever overriding this
43
45
  # method in an including class.
@@ -64,33 +66,29 @@ module Linked
64
66
  self
65
67
  end
66
68
 
69
+ # @!method chain_head?
70
+ #
67
71
  # Returns true if no item come before this one. Note that the implementation
68
72
  # of this method is protected and publicly accessible through its alias
69
73
  # #first?.
70
74
  #
71
75
  # @return [true] if the item is the head of the chain.
72
76
  # @return [false] otherwise.
73
- def chain_head?
74
- # p @_chain_head.is_a? Numeric
75
- # @_chain_head.is_a? Numeric
76
- @_prev.chain_tail?
77
- end
77
+ def_delegator :@_prev, :chain_tail?, :chain_head?
78
78
 
79
79
  alias first? chain_head?
80
- protected :chain_head?
81
80
 
81
+ # @!method chain_tail?
82
+ #
82
83
  # Returns true if no item come after this one. Note that the implementation
83
84
  # of this method is protected and publicly accessible through its alias
84
85
  # #last?.
85
86
  #
86
87
  # @return [true] if the item is the tail of the chain.
87
88
  # @return [false] otherwise.
88
- def chain_tail?
89
- @_next.nil?
90
- end
89
+ def_delegator :@_next, :nil?, :chain_tail?
91
90
 
92
91
  alias last? chain_tail?
93
- protected :chain_tail?
94
92
 
95
93
  # Returns the first item in the chain. Note that the implementation of this
96
94
  # method is protected and publicly accessible through its aliases
@@ -103,7 +101,6 @@ module Linked
103
101
 
104
102
  alias first_in_chain chain_head
105
103
  alias chain chain_head
106
- protected :chain_head
107
104
 
108
105
  # Returns the last item in the chain. Note that the implementation of this
109
106
  # method is protected and publicly accessible through its aliases
@@ -115,14 +112,11 @@ module Linked
115
112
  end
116
113
 
117
114
  alias last_in_chain chain_tail
118
- protected :chain_tail
119
115
 
120
116
  # Returns the number of items in the current chain.
121
117
  #
122
118
  # @return [Integer] the number of items in the chain.
123
- def chain_length
124
- chain_head.chain_head!
125
- end
119
+ def_delegator :chain_head, :chain_head!, :chain_length
126
120
 
127
121
  # Check if this object is in a chain.
128
122
  #
@@ -171,123 +165,6 @@ module Linked
171
165
 
172
166
  alias previous prev
173
167
 
174
- # rubocop:disable Metrics/MethodLength
175
-
176
- def append(object)
177
- # Assume the given object to be the head of its chain
178
- # B. If it is not, chain B will be split before the
179
- # object, and the sub chain in which the object now is
180
- # the head will be appended.
181
- sub_chain_b_head = coerce object
182
-
183
- # Grab the first item in this chain. We will need it
184
- # later.
185
- target_chain = chain_head
186
-
187
- # Split chain B before the given object and grab the
188
- # tail of that new sub chain.
189
- sub_chain_b_tail = sub_chain_b_head.split_before_and_insert target_chain
190
-
191
- # If we are the last item in our chain we need to
192
- # notify the head that there is a new tail.
193
- # Otherwise the next item in the list need to be
194
- # linked up correctly.
195
- if chain_tail?
196
- target_chain.prev = sub_chain_b_tail
197
- else
198
- sub_chain_b_tail.next = next!
199
- next!.prev = sub_chain_b_tail
200
- end
201
-
202
- # Connect sub chain B to this item
203
- sub_chain_b_head.prev = self
204
- self.next = sub_chain_b_head
205
-
206
- sub_chain_b_tail
207
- end
208
-
209
- # rubocop:enable Metrics/MethodLength
210
-
211
- # rubocop:disable Metrics/MethodLength
212
-
213
- def prepend(object)
214
- sub_chain_a_tail = coerce object
215
-
216
- if chain_head?
217
- sub_chain_a_tail.split_after_and_insert
218
- sub_chain_a_tail.append self
219
-
220
- return chain_head
221
- end
222
-
223
- target_chain = chain_head
224
-
225
- sub_chain_a_head = sub_chain_a_tail.split_after_and_insert target_chain
226
-
227
- prev!.next = sub_chain_a_head
228
- sub_chain_a_head.prev = prev!
229
-
230
- sub_chain_a_tail.next = self
231
- self.prev = sub_chain_a_tail
232
-
233
- sub_chain_a_head
234
- end
235
-
236
- # rubocop:enable Metrics/MethodLength
237
-
238
- # rubocop:disable Metrics/MethodLength
239
-
240
- # Remove an item from the chain. Note that calling #delete on the first item
241
- # in a chain causes all subsequent items to be moved to a new chain.
242
- #
243
- # === Usage
244
- # Example using the chain A <> B <> C
245
- #
246
- # A.delete # => A | B <> C
247
- # B.delete # => B | A <> C
248
- # C.delete # => C | A <> B
249
- #
250
- # @return [self]
251
- def delete
252
- if chain_head?
253
- split_after_and_insert
254
- else
255
- shrink
256
-
257
- if chain_tail?
258
- chain_head.prev = @_prev
259
- else
260
- @_next.prev = @_prev
261
- end
262
-
263
- @_prev.next = @_next
264
- end
265
-
266
- reset_item
267
- end
268
-
269
- # rubocop:enable Metrics/MethodLength
270
-
271
- # Remove all items before this one in the chain.
272
- #
273
- # @return [nil] if this is the first item.
274
- # @return [Listable] the first item in the chain that was just deleted.
275
- def delete_before
276
- prev!.split_after_and_insert unless chain_head?
277
- end
278
-
279
- # Remove all items after this one in the chain.
280
- #
281
- # @return [nil] if this is the lst item.
282
- # @return [Listable] the first item in the chain that was just deleted.
283
- def delete_after
284
- return nil if chain_tail?
285
-
286
- item = next!
287
- item.split_before_and_insert
288
- item
289
- end
290
-
291
168
  # Iterates over each item before this, in reverse order. If a block is not
292
169
  # given an enumerator is returned.
293
170
  #
@@ -322,7 +199,10 @@ module Linked
322
199
  end
323
200
  end
324
201
 
202
+ # rubocop:disable Metrics/AbcSize
203
+ # rubocop:disable Metrics/CyclomaticComplexity
325
204
  # rubocop:disable Metrics/MethodLength
205
+ # rubocop:disable Metrics/PerceivedComplexity
326
206
 
327
207
  # Take n items and put them into a sorted array. If n is positive the array
328
208
  # will include this item, as well the n - 1 items following it in the chain.
@@ -363,7 +243,10 @@ module Linked
363
243
  res
364
244
  end
365
245
 
246
+ # rubocop:enable Metrics/AbcSize
247
+ # rubocop:enable Metrics/CyclomaticComplexity
366
248
  # rubocop:enable Metrics/MethodLength
249
+ # rubocop:enable Metrics/PerceivedComplexity
367
250
 
368
251
  # Due to the nature of listable objects the default #inspect method is
369
252
  # problematic. This basic replacement includes only the class name and the
@@ -397,13 +280,6 @@ module Linked
397
280
  @_next
398
281
  end
399
282
 
400
- # Never call this method directly since it may corrupt the chain.
401
- #
402
- # Sets the value of the `next` field.
403
- def next=(other)
404
- @_next = other
405
- end
406
-
407
283
  # Protected, unsafe accessor of the previous item in the chain. It is
408
284
  # preferable to use #prev, possibly in conjunction with #first?.
409
285
  #
@@ -413,13 +289,6 @@ module Linked
413
289
  @_prev
414
290
  end
415
291
 
416
- # Never call this method directly since it may corrupt the chain.
417
- #
418
- # Sets the value of the `prev` field.
419
- def prev=(other)
420
- @_prev = other
421
- end
422
-
423
292
  # Protected, unsafe accessor of the first item in the chain. It is
424
293
  # preferable to use #first.
425
294
  #
@@ -429,148 +298,6 @@ module Linked
429
298
  @_chain_head
430
299
  end
431
300
 
432
- # Never call this method directly since it may corrupt the chain.
433
- #
434
- # Sets the value of the `first` field.
435
-
436
- def chain_head=(other)
437
- @_chain_head = other
438
- end
439
-
440
- # Never call this method directly since it may corrupt the chain. Grow the
441
- # chain with n items.
442
- #
443
- # n - the number of items to increase the chain count with.
444
- #
445
- # Returns the updated chain count.
446
-
447
- def grow(n = 1)
448
- head = chain_head
449
- head.chain_head = head.chain_head! + n
450
- end
451
-
452
- # Never call this method directly since it may corrupt the chain. Shrink the
453
- # chain with n items.
454
- #
455
- # n - the number of items to decrease the chain count with.
456
- #
457
- # Returns the updated chain count.
458
-
459
- def shrink(n = 1)
460
- head = chain_head
461
- head.chain_head = head.chain_head! - n
462
- end
463
-
464
- # Split the chain on this item and insert the latter part into the chain
465
- # with head as its first item.
466
- #
467
- # Calling C.split_before_and_insert(.) yields the two chains (ii) and (iii)
468
- #
469
- # A <> B <> C <> D A <> B C <> D
470
- # (i) (ii) (iii)
471
- #
472
- # Chain (ii) is guaranteed to be complete. Chain (iii) will however be left
473
- # in an inclomplete state unless head_b == self (default). The first item in
474
- # (iii) must then be connected to the one preceeding it.
475
- #
476
- # head_b - the head of a new chain that (iii) will be added to.
477
- #
478
- # Returns the last element of (iii).
479
-
480
- def split_before_and_insert(head_b = self)
481
- # Get the current chain head. It will remain the head
482
- # of sub chain a (ii). If this item is the first then
483
- # chain a will be empty.
484
- chain_a_head = chain_head? ? nil : chain_head
485
-
486
- # The head of sub chain b (iii) is self.
487
- chain_b_head = self
488
-
489
- # Find the tail of sub chain b by iterating over each
490
- # item, starting with this one. Set the the new head
491
- # of these while counting them.
492
- chain_b_tail = self
493
- chain_b_length = 1
494
-
495
- loop do
496
- chain_b_tail.chain_head = head_b
497
- chain_b_tail = chain_b_tail.next
498
- chain_b_length += 1
499
- end
500
-
501
- # If sub chain a is not empty it needs to be updated.
502
- # Shrink its count by the number of items in sub
503
- # chain b and complete it by connecting the head to
504
- # the tail.
505
- if chain_a_head
506
- chain_a_head.shrink chain_b_length
507
-
508
- chain_a_tail = chain_b_head.prev
509
- chain_a_head.prev = chain_a_tail
510
- chain_a_tail.next = nil
511
- end
512
-
513
- # Tell the new chain to grow. If sub chain b is to be
514
- # the new head we can insert the count directly. We
515
- # also complete the chain by connecting the head to
516
- # the tail. The next field of the tail should already
517
- # be nil.
518
- if chain_b_head.equal? head_b
519
- chain_b_head.chain_head = chain_b_length
520
- chain_b_head.prev = chain_b_tail
521
- else
522
- head_b.grow chain_b_length
523
- end
524
-
525
- # Chain a is now either empty (nil) or completed.
526
- # Chain b however is only complete if the given head
527
- # is equal to self (default). If it is not chain b
528
- # will need a) the next field of the tail set to the
529
- # item after, unless nil, and b) the prev field of
530
- # head set to the item before.
531
-
532
- chain_b_tail
533
- end
534
-
535
- # TODO
536
-
537
- def split_after_and_insert(head_a = chain_head)
538
- # If this is not the last item in the chain, sub chain
539
- # b will contain items. Use #split_before_and_insert
540
- # to cut the chain after this one. This will complete
541
- # chain b and update the item count of chain a.
542
- next!.split_before_and_insert unless chain_tail?
543
-
544
- chain_a_head = chain_head
545
-
546
- # If the head of sub chain a is same as the target
547
- # chain head
548
- return chain_a_head if chain_a_head.equal? head_a
549
-
550
- chain_a_length = chain_length
551
-
552
- # Set the head field of all items, starting with the
553
- # tail (self), moving backwards.
554
- item = self
555
-
556
- # Loop until we hit the first item.
557
- loop do
558
- item.chain_head = head_a
559
- item = item.prev
560
- end
561
-
562
- # Tell the target chain to grow with the number of
563
- # items in sub chain a.
564
- head_a.grow chain_a_length
565
-
566
- # Sub chain b is now either empty or complete. Sub
567
- # chain a however is only complete if the target
568
- # head is the same as the head of chain a. Otherwise
569
- # the prev field of head and the next field of tail
570
- # both need to be set correctly.
571
- chain_a_head
572
- end
573
-
574
301
  private
575
302
 
576
303
  # Convert the given object to a listable item. If the object responds to
@@ -600,5 +327,7 @@ module Linked
600
327
  @_next = nil
601
328
  @_prev = self
602
329
  end
330
+
331
+ protected :chain_head?, :chain_tail?, :chain_head, :chain_tail
603
332
  end
604
333
  end
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Linked
4
+ # This module collects all the methods that mutate a listable item.
5
+ module Mutable
6
+ # rubocop:disable Metrics/MethodLength
7
+
8
+ def append(object)
9
+ # Assume the given object to be the head of its chain
10
+ # B. If it is not, chain B will be split before the
11
+ # object, and the sub chain in which the object now is
12
+ # the head will be appended.
13
+ sub_chain_b_head = coerce object
14
+
15
+ # Grab the first item in this chain. We will need it
16
+ # later.
17
+ target_chain = chain_head
18
+
19
+ # Split chain B before the given object and grab the
20
+ # tail of that new sub chain.
21
+ sub_chain_b_tail = sub_chain_b_head.split_before_and_insert target_chain
22
+
23
+ # If we are the last item in our chain we need to
24
+ # notify the head that there is a new tail.
25
+ # Otherwise the next item in the list need to be
26
+ # linked up correctly.
27
+ if chain_tail?
28
+ target_chain.prev = sub_chain_b_tail
29
+ else
30
+ sub_chain_b_tail.next = next!
31
+ next!.prev = sub_chain_b_tail
32
+ end
33
+
34
+ # Connect sub chain B to this item
35
+ sub_chain_b_head.prev = self
36
+ self.next = sub_chain_b_head
37
+
38
+ sub_chain_b_tail
39
+ end
40
+
41
+ # rubocop:enable Metrics/MethodLength
42
+
43
+ # rubocop:disable Metrics/MethodLength
44
+
45
+ def prepend(object)
46
+ sub_chain_a_tail = coerce object
47
+
48
+ if chain_head?
49
+ sub_chain_a_tail.split_after_and_insert
50
+ sub_chain_a_tail.append self
51
+
52
+ return chain_head
53
+ end
54
+
55
+ target_chain = chain_head
56
+
57
+ sub_chain_a_head = sub_chain_a_tail.split_after_and_insert target_chain
58
+
59
+ prev!.next = sub_chain_a_head
60
+ sub_chain_a_head.prev = prev!
61
+
62
+ sub_chain_a_tail.next = self
63
+ self.prev = sub_chain_a_tail
64
+
65
+ sub_chain_a_head
66
+ end
67
+
68
+ # rubocop:enable Metrics/MethodLength
69
+
70
+ # rubocop:disable Metrics/MethodLength
71
+
72
+ # Remove an item from the chain. Note that calling #delete on the first item
73
+ # in a chain causes all subsequent items to be moved to a new chain.
74
+ #
75
+ # === Usage
76
+ # Example using the chain A <> B <> C
77
+ #
78
+ # A.delete # => A | B <> C
79
+ # B.delete # => B | A <> C
80
+ # C.delete # => C | A <> B
81
+ #
82
+ # @return [self]
83
+ def delete
84
+ if chain_head?
85
+ split_after_and_insert
86
+ else
87
+ shrink
88
+
89
+ if chain_tail?
90
+ chain_head.prev = @_prev
91
+ else
92
+ @_next.prev = @_prev
93
+ end
94
+
95
+ @_prev.next = @_next
96
+ end
97
+
98
+ reset_item
99
+ end
100
+
101
+ # rubocop:enable Metrics/MethodLength
102
+
103
+ # Remove all items before this one in the chain.
104
+ #
105
+ # @return [nil] if this is the first item.
106
+ # @return [Listable] the first item in the chain that was just deleted.
107
+ def delete_before
108
+ prev!.split_after_and_insert unless chain_head?
109
+ end
110
+
111
+ # Remove all items after this one in the chain.
112
+ #
113
+ # @return [nil] if this is the lst item.
114
+ # @return [Listable] the first item in the chain that was just deleted.
115
+ def delete_after
116
+ return nil if chain_tail?
117
+
118
+ item = next!
119
+ item.split_before_and_insert
120
+ item
121
+ end
122
+
123
+ protected
124
+
125
+ # Never call this method directly since it may corrupt the chain.
126
+ #
127
+ # Sets the value of the `next` field.
128
+ def next=(other)
129
+ @_next = other
130
+ end
131
+
132
+ # Never call this method directly since it may corrupt the chain.
133
+ #
134
+ # Sets the value of the `prev` field.
135
+ def prev=(other)
136
+ @_prev = other
137
+ end
138
+
139
+ # Never call this method directly since it may corrupt the chain.
140
+ #
141
+ # Sets the value of the `first` field.
142
+ def chain_head=(other)
143
+ @_chain_head = other
144
+ end
145
+
146
+ # Never call this method directly since it may corrupt the chain. Grow the
147
+ # chain with n items.
148
+ #
149
+ # n - the number of items to increase the chain count with.
150
+ #
151
+ # Returns the updated chain count.
152
+ def grow(n = 1)
153
+ head = chain_head
154
+ head.chain_head = head.chain_head! + n
155
+ end
156
+
157
+ # Never call this method directly since it may corrupt the chain. Shrink the
158
+ # chain with n items.
159
+ #
160
+ # n - the number of items to decrease the chain count with.
161
+ #
162
+ # Returns the updated chain count.
163
+ def shrink(n = 1)
164
+ head = chain_head
165
+ head.chain_head = head.chain_head! - n
166
+ end
167
+
168
+ # rubocop:disable Metrics/AbcSize
169
+ # rubocop:disable Metrics/MethodLength
170
+
171
+ # Split the chain on this item and insert the latter part into the chain
172
+ # with head as its first item.
173
+ #
174
+ # Calling C.split_before_and_insert(.) yields the two chains (ii) and (iii)
175
+ #
176
+ # A <> B <> C <> D A <> B C <> D
177
+ # (i) (ii) (iii)
178
+ #
179
+ # Chain (ii) is guaranteed to be complete. Chain (iii) will however be left
180
+ # in an inclomplete state unless head_b == self (default). The first item in
181
+ # (iii) must then be connected to the one preceeding it.
182
+ #
183
+ # head_b - the head of a new chain that (iii) will be added to.
184
+ #
185
+ # Returns the last element of (iii).
186
+ def split_before_and_insert(head_b = self)
187
+ # Get the current chain head. It will remain the head
188
+ # of sub chain a (ii). If this item is the first then
189
+ # chain a will be empty.
190
+ chain_a_head = chain_head? ? nil : chain_head
191
+
192
+ # The head of sub chain b (iii) is self.
193
+ chain_b_head = self
194
+
195
+ # Find the tail of sub chain b by iterating over each
196
+ # item, starting with this one. Set the the new head
197
+ # of these while counting them.
198
+ chain_b_tail = self
199
+ chain_b_length = 1
200
+
201
+ loop do
202
+ chain_b_tail.chain_head = head_b
203
+ chain_b_tail = chain_b_tail.next
204
+ chain_b_length += 1
205
+ end
206
+
207
+ # If sub chain a is not empty it needs to be updated.
208
+ # Shrink its count by the number of items in sub
209
+ # chain b and complete it by connecting the head to
210
+ # the tail.
211
+ if chain_a_head
212
+ chain_a_head.shrink chain_b_length
213
+
214
+ chain_a_tail = chain_b_head.prev
215
+ chain_a_head.prev = chain_a_tail
216
+ chain_a_tail.next = nil
217
+ end
218
+
219
+ # Tell the new chain to grow. If sub chain b is to be
220
+ # the new head we can insert the count directly. We
221
+ # also complete the chain by connecting the head to
222
+ # the tail. The next field of the tail should already
223
+ # be nil.
224
+ if chain_b_head.equal? head_b
225
+ chain_b_head.chain_head = chain_b_length
226
+ chain_b_head.prev = chain_b_tail
227
+ else
228
+ head_b.grow chain_b_length
229
+ end
230
+
231
+ # Chain a is now either empty (nil) or completed.
232
+ # Chain b however is only complete if the given head
233
+ # is equal to self (default). If it is not chain b
234
+ # will need a) the next field of the tail set to the
235
+ # item after, unless nil, and b) the prev field of
236
+ # head set to the item before.
237
+
238
+ chain_b_tail
239
+ end
240
+
241
+ # rubocop:enable Metrics/AbcSize
242
+ # rubocop:enable Metrics/MethodLength
243
+
244
+ # rubocop:disable Metrics/MethodLength
245
+
246
+ # TODO
247
+
248
+ def split_after_and_insert(head_a = chain_head)
249
+ # If this is not the last item in the chain, sub chain
250
+ # b will contain items. Use #split_before_and_insert
251
+ # to cut the chain after this one. This will complete
252
+ # chain b and update the item count of chain a.
253
+ next!.split_before_and_insert unless chain_tail?
254
+
255
+ chain_a_head = chain_head
256
+
257
+ # If the head of sub chain a is same as the target
258
+ # chain head
259
+ return chain_a_head if chain_a_head.equal? head_a
260
+
261
+ chain_a_length = chain_length
262
+
263
+ # Set the head field of all items, starting with the
264
+ # tail (self), moving backwards.
265
+ item = self
266
+
267
+ # Loop until we hit the first item.
268
+ loop do
269
+ item.chain_head = head_a
270
+ item = item.prev
271
+ end
272
+
273
+ # Tell the target chain to grow with the number of
274
+ # items in sub chain a.
275
+ head_a.grow chain_a_length
276
+
277
+ # Sub chain b is now either empty or complete. Sub
278
+ # chain a however is only complete if the target
279
+ # head is the same as the head of chain a. Otherwise
280
+ # the prev field of head and the next field of tail
281
+ # both need to be set correctly.
282
+ chain_a_head
283
+ end
284
+
285
+ # rubocop:enable Metrics/MethodLength
286
+ end
287
+ end
@@ -3,7 +3,9 @@
3
3
  module Linked
4
4
  # Collection of common utility methods.
5
5
  module Util
6
- protected def object_identifier
6
+ protected
7
+
8
+ def object_identifier
7
9
  format '%s:0x%0x', self.class.name, object_id
8
10
  end
9
11
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Linked
4
4
  # The current version number of the Linked library.
5
- VERSION = '0.5.1'
5
+ VERSION = '0.5.2'
6
6
  end
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  lib = File.expand_path('lib', __dir__)
@@ -25,14 +24,14 @@ Gem::Specification.new do |spec|
25
24
  spec.bindir = 'exe'
26
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
26
  spec.require_paths = ['lib']
28
-
27
+
29
28
  spec.metadata['yard.run'] = 'yri'
30
29
 
31
- spec.add_development_dependency 'bundler', '~> 1.12'
32
- spec.add_development_dependency 'coveralls', '~> 0.8'
30
+ spec.add_development_dependency 'bundler', '~> 1.16'
33
31
  spec.add_development_dependency 'minitest', '~> 5.0'
34
32
  spec.add_development_dependency 'rake', '~> 10.0'
33
+ spec.add_development_dependency 'redcarpet', '~> 3.4'
35
34
  spec.add_development_dependency 'rubocop', '~> 0.52'
36
- spec.add_development_dependency 'simplecov', '~> 0.9'
35
+ spec.add_development_dependency 'simplecov', '~> 0.16'
37
36
  spec.add_development_dependency 'yard', '~> 0.9'
38
37
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: linked
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Lindberg
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-09 00:00:00.000000000 Z
11
+ date: 2018-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.12'
19
+ version: '1.16'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.12'
26
+ version: '1.16'
27
27
  - !ruby/object:Gem::Dependency
28
- name: coveralls
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.8'
33
+ version: '5.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.8'
40
+ version: '5.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '10.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '5.0'
54
+ version: '10.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rake
56
+ name: redcarpet
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '10.0'
61
+ version: '3.4'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '10.0'
68
+ version: '3.4'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rubocop
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.9'
89
+ version: '0.16'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.9'
96
+ version: '0.16'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: yard
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +121,7 @@ files:
121
121
  - ".rubocop.yml"
122
122
  - ".travis.yml"
123
123
  - Gemfile
124
+ - Gemfile.lock
124
125
  - LICENSE.txt
125
126
  - README.md
126
127
  - Rakefile
@@ -131,6 +132,7 @@ files:
131
132
  - lib/linked/list.rb
132
133
  - lib/linked/list_enumerable.rb
133
134
  - lib/linked/listable.rb
135
+ - lib/linked/mutable.rb
134
136
  - lib/linked/util.rb
135
137
  - lib/linked/version.rb
136
138
  - linked.gemspec
@@ -155,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
157
  version: '0'
156
158
  requirements: []
157
159
  rubyforge_project:
158
- rubygems_version: 2.7.3
160
+ rubygems_version: 2.6.11
159
161
  signing_key:
160
162
  specification_version: 4
161
163
  summary: Yet another linked list implementation in Ruby.