linked 0.5.1 → 0.5.2

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
- 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.