dlinked 0.1.7 → 0.1.9

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.
data/lib/d_linked/list.rb CHANGED
@@ -1,18 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'list/node'
4
+
4
5
  module DLinked
5
- # A fast, lightweight doubly linked list implementation
6
+ # A fast, lightweight, and standards-compliant doubly linked list.
7
+ #
8
+ # This class provides a memory-efficient and performant alternative to Ruby's
9
+ # standard `Array` for scenarios requiring frequent O(1) insertions or
10
+ # deletions from the ends of the list (e.g., queues, stacks).
11
+ #
12
+ # It includes the `Enumerable` module, allowing it to work seamlessly with
13
+ # standard Ruby collection methods like `map`, `select`, and `each`.
14
+ #
15
+ # @example Basic Usage
16
+ # list = DLinked::List.new
17
+ # list.append(20).prepend(10) # => [10, 20]
18
+ # list.shift # => 10
19
+ # list.to_a # => [20]
6
20
  class List
7
21
  include Enumerable
8
22
 
9
- # PURE PERFORMANCE: We now use the dedicated Class for Node,
10
- Node = DLinked::List::Node
11
- private_constant :Node
12
-
23
+ # @!attribute [r] size
24
+ # The number of elements in the list.
25
+ # @return [Integer] The count of elements.
13
26
  attr_reader :size
14
27
  alias length size
15
28
 
29
+ # Initializes a new, empty list.
30
+ #
31
+ # @example
32
+ # list = DLinked::List.new
33
+ # list.size # => 0
16
34
  def initialize
17
35
  @head = nil
18
36
  @tail = nil
@@ -23,33 +41,37 @@ module DLinked
23
41
 
24
42
  # Adds a new value to the beginning of the list (the head).
25
43
  #
26
- # This is an **O(1)** operation, as it only involves updating a few pointers.
44
+ # This is an **O(1)** operation.
45
+ #
46
+ # @example
47
+ # list = DLinked::List.new
48
+ # list.prepend(10)
49
+ # list.prepend(5)
50
+ # list.to_a # => [5, 10]
27
51
  #
28
52
  # @param value [Object] The value to store in the new node.
29
- # @return [self] Returns the list instance, allowing for method chaining (e.g., list.prepend(1).prepend(2)).
53
+ # @return [self] The list instance, allowing for method chaining.
30
54
  def prepend(value)
31
55
  node = Node.new(value, nil, @head)
32
- @head.prev = node if @head
33
- @head = node
34
- @tail ||= node
35
- @size += 1
36
- self
56
+ list_prepend_logic(node)
37
57
  end
38
58
  alias unshift prepend
39
-
59
+
40
60
  # Adds a new value to the end of the list (the tail).
41
61
  #
42
- # This is an O(1) operation.
62
+ # This is an **O(1)** operation.
63
+ #
64
+ # @example
65
+ # list = DLinked::List.new
66
+ # list.append(10)
67
+ # list.append(20)
68
+ # list.to_a # => [10, 20]
43
69
  #
44
70
  # @param value [Object] The value to add to the list.
45
- # @return [DLinked::List] Returns the list instance for method chaining.
71
+ # @return [self] The list instance, for method chaining.
46
72
  def append(value)
47
73
  node = Node.new(value, @tail, nil)
48
- @tail.next = node if @tail
49
- @tail = node
50
- @head ||= node
51
- @size += 1
52
- self
74
+ list_append_logic(node)
53
75
  end
54
76
  alias push append
55
77
  alias << append
@@ -58,7 +80,13 @@ module DLinked
58
80
  #
59
81
  # This is an **O(1)** operation.
60
82
  #
61
- # @return [Object, nil] The value of the removed element, or nil if the list is empty.
83
+ # @example
84
+ # list = DLinked::List.new << 1 << 2
85
+ # list.shift # => 1
86
+ # list.shift # => 2
87
+ # list.shift # => nil
88
+ #
89
+ # @return [Object, nil] The value of the removed element, or `nil` if the list is empty.
62
90
  def shift
63
91
  return nil if empty?
64
92
 
@@ -71,9 +99,15 @@ module DLinked
71
99
 
72
100
  # Removes the last element from the list (the tail) and returns its value.
73
101
  #
74
- # This is an **O(1)** operation, as the tail pointer gives immediate access to the node.
102
+ # This is an **O(1)** operation.
103
+ #
104
+ # @example
105
+ # list = DLinked::List.new << 1 << 2
106
+ # list.pop # => 2
107
+ # list.pop # => 1
108
+ # list.pop # => nil
75
109
  #
76
- # @return [Object, nil] The value of the removed element, or nil if the list is empty.
110
+ # @return [Object, nil] The value of the removed element, or `nil` if the list is empty.
77
111
  def pop
78
112
  return nil if empty?
79
113
 
@@ -84,20 +118,20 @@ module DLinked
84
118
  value
85
119
  end
86
120
 
87
- # Returns the value of the element at the head (start) of the list.
121
+ # Returns the value of the element at the head of the list without removing it.
88
122
  #
89
123
  # This is an **O(1)** operation.
90
124
  #
91
- # @return [Object, nil] The value of the first element, or nil if the list is empty.
125
+ # @return [Object, nil] The value of the first element, or `nil` if the list is empty.
92
126
  def first
93
127
  @head&.value
94
128
  end
95
129
 
96
- # Returns the value of the element at the tail (end) of the list.
130
+ # Returns the value of the element at the tail of the list without removing it.
97
131
  #
98
132
  # This is an **O(1)** operation.
99
133
  #
100
- # @return [Object, nil] The value of the last element, or nil if the list is empty.
134
+ # @return [Object, nil] The value of the last element, or `nil` if the list is empty.
101
135
  def last
102
136
  @tail&.value
103
137
  end
@@ -106,16 +140,16 @@ module DLinked
106
140
  #
107
141
  # This is an **O(1)** operation.
108
142
  #
109
- # @return [Boolean] True if the size of the list is zero, false otherwise.
143
+ # @return [Boolean] `true` if the list is empty, `false` otherwise.
110
144
  def empty?
111
145
  @size.zero?
112
146
  end
113
147
 
114
- # Removes all elements from the list, resetting the head, tail, and size.
148
+ # Removes all elements from the list.
115
149
  #
116
- # This is an **O(1)** operation, as it only resets instance variables.
150
+ # This is an **O(1)** operation.
117
151
  #
118
- # @return [self] Returns the list instance.
152
+ # @return [self] The cleared list instance.
119
153
  def clear
120
154
  @head = nil
121
155
  @tail = nil
@@ -125,13 +159,12 @@ module DLinked
125
159
 
126
160
  # --- O(n) ENUMERATION & LOOKUP ---
127
161
 
128
- # Iterates through the list, yielding the value of each element in order.
162
+ # Iterates through the list, yielding the value of each element in order from head to tail.
129
163
  #
130
- # This is an **O(n)** operation, as it traverses every node from head to tail.
164
+ # This is an **O(n)** operation.
131
165
  #
132
166
  # @yield [Object] The value of the current element.
133
- # @return [self, Enumerator] Returns the list instance if a block is given,
134
- # otherwise returns an Enumerator.
167
+ # @return [self, Enumerator] Returns `self` if a block is given, otherwise returns an `Enumerator`.
135
168
  def each
136
169
  return enum_for(:each) unless block_given?
137
170
 
@@ -143,14 +176,12 @@ module DLinked
143
176
  self
144
177
  end
145
178
 
146
- # Iterates through the list in reverse order, yielding the value of each element
147
- # starting from the tail and moving to the head.
179
+ # Iterates through the list in reverse order, from tail to head.
148
180
  #
149
- # This is an **O(n)** operation, as it traverses every node.
181
+ # This is an **O(n)** operation.
150
182
  #
151
183
  # @yield [Object] The value of the current element.
152
- # @return [self, Enumerator] Returns the list instance if a block is given,
153
- # otherwise returns an Enumerator.
184
+ # @return [self, Enumerator] Returns `self` if a block is given, otherwise returns an `Enumerator`.
154
185
  def reverse_each
155
186
  return enum_for(:reverse_each) unless block_given?
156
187
 
@@ -164,10 +195,10 @@ module DLinked
164
195
 
165
196
  # Finds the index of the first occurrence of a given value.
166
197
  #
167
- # This is an **O(n)** operation, as it requires traversing the list from the head.
198
+ # This is an **O(n)** operation.
168
199
  #
169
200
  # @param value [Object] The value to search for.
170
- # @return [Integer, nil] The index of the first matching element, or nil if the value is not found.
201
+ # @return [Integer, nil] The index of the first matching element, or `nil` if not found.
171
202
  def index(value)
172
203
  current = @head
173
204
  idx = 0
@@ -182,19 +213,18 @@ module DLinked
182
213
 
183
214
  # Converts the linked list into a standard Ruby Array.
184
215
  #
185
- # This is an **O(n)** operation, as it requires iterating over every element
186
- # and allocating a new Array.
216
+ # This is an **O(n)** operation.
187
217
  #
188
- # @return [Array] A new Array containing all elements in order.
218
+ # @return [Array] A new `Array` containing all elements in order.
189
219
  def to_a
190
220
  map { |v| v }
191
221
  end
192
222
 
193
- # Returns a string representation of the list, resembling a standard Ruby Array.
223
+ # Returns a string representation of the list.
194
224
  #
195
- # This is an **O(n)** operation due to the call to #to_a.
225
+ # This is an **O(n)** operation.
196
226
  #
197
- # @return [String] The string representation (e.g., "[10, 20, 30]").
227
+ # @return [String] The string representation (e.g., `"[10, 20, 30]"`).
198
228
  def to_s
199
229
  "[#{to_a.join(', ')}]"
200
230
  end
@@ -202,20 +232,22 @@ module DLinked
202
232
 
203
233
  # --- O(n) ARRAY/SLICE COMPATIBILITY ---
204
234
 
205
- # Retrieves the element(s) at the specified index or within a slice.
206
- #
207
- # This method supports two primary forms of access:
208
- # 1. **Single Index (O(n)):** Returns the element at a specific positive or negative index (e.g., list[2] or list[-1]).
209
- # 2. **Slice Access (O(n)):** Delegates to the {#slice} method for start/length or range access (e.g., list[1, 2] or list[1..3]).
210
- #
211
- # Traversal is optimized: for positive indices less than size/2, traversal starts from the head;
212
- # otherwise, it starts from the tail.
213
- #
214
- # @param args [Array] Arguments representing either a single index or slice parameters:
215
- # - `(index)` for single element access.
216
- # - `(start_index, length)` for a slice.
217
- # - `(range)` for a slice using a Range object.
218
- # @return [Object, Array<Object>, nil] The value at the index, an array of values for a slice, or nil if the single index is out of bounds.
235
+ # @overload [](index)
236
+ # Retrieves the element at a specific index.
237
+ # Traversal is optimized to start from the head or tail, whichever is closer.
238
+ # @param index [Integer] The index (positive or negative).
239
+ # @return [Object, nil] The value at the index, or `nil` if out of bounds.
240
+ #
241
+ # @overload [](start, length)
242
+ # Retrieves a slice of `length` elements starting at `start`.
243
+ # @param start [Integer] The starting index.
244
+ # @param length [Integer] The number of elements to retrieve.
245
+ # @return [DLinked::List, nil] A new list containing the slice, or `nil` if `start` is out of bounds.
246
+ #
247
+ # @overload [](range)
248
+ # Retrieves a slice using a `Range`.
249
+ # @param range [Range] The range of indices to retrieve.
250
+ # @return [DLinked::List] A new list containing the slice.
219
251
  def [](*args)
220
252
  # Case 1: Single Index Access (list[i])
221
253
  if args.size == 1 && args[0].is_a?(Integer)
@@ -231,27 +263,24 @@ module DLinked
231
263
  slice(*args) # Delegate to the robust slice method
232
264
  end
233
265
 
234
- # Sets the value of an element at a single index or replaces a slice (range or start/length)
235
- # with new element(s).
236
- #
237
- # This method handles four main scenarios:
238
- # 1. Single Element Assignment (O(n)): Overwrites the value at a valid index.
239
- # 2. Slice Replacement (O(n)): Deletes a section and inserts new elements.
240
- # 3. Out-of-Bounds Append (O(k)): If the start index is greater than the current size,
241
- # the new elements are appended to the list (k is the length of the replacement).
242
- # 4. Out-of-Bounds Non-Append (Returns nil): For a single index assignment that is out of bounds,
243
- # it returns nil (like a standard Ruby Array setter).
244
- #
245
- # The overall complexity is **O(n + k)**, where n is the traversal time to find the start point,
246
- # and k is the number of elements being inserted or deleted.
247
- #
248
- # @param args [Array] The arguments defining the assignment. The last element of this array
249
- # is always the replacement value.
250
- # - `(index, replacement)` for single assignment.
251
- # - `(start_index, length, replacement)` for slice replacement.
252
- # - `(range, replacement)` for range replacement.
253
- # @return [Object, Array, nil] The value(s) assigned, or nil if the assignment failed
254
- # due to an invalid out-of-bounds single index.
266
+ # @overload []=(index, value)
267
+ # Sets the value of an element at a single index.
268
+ # @param index [Integer] The index to modify.
269
+ # @param value [Object] The new value.
270
+ # @return [Object, nil] The assigned value, or `nil` if the index is out of bounds.
271
+ #
272
+ # @overload []=(start, length, value)
273
+ # Replaces a slice of `length` elements starting at `start` with a new value (or values).
274
+ # @param start [Integer] The starting index.
275
+ # @param length [Integer] The number of elements to replace.
276
+ # @param value [Object, Array<Object>] The new value(s).
277
+ # @return [Object, Array<Object>] The assigned value(s).
278
+ #
279
+ # @overload []=(range, value)
280
+ # Replaces a slice defined by a `Range` with a new value (or values).
281
+ # @param range [Range] The range of indices to replace.
282
+ # @param value [Object, Array<Object>] The new value(s).
283
+ # @return [Object, Array<Object>] The assigned value(s).
255
284
  def []=(*args)
256
285
  replacement = args.pop
257
286
 
@@ -259,20 +288,14 @@ module DLinked
259
288
  if args.size == 1 && args[0].is_a?(Integer)
260
289
  index = args[0]
261
290
  index += @size if index.negative?
262
- # Check bounds for simple assignment (Must be within 0 to size-1)
263
291
  return nil unless index >= 0 && index < @size
264
292
 
265
- # Simple assignment: O(n) lookup, O(1) set
266
293
  node = find_node_at_index(index)
267
294
  node.value = replacement
268
295
  return replacement
269
-
270
- # For out-of-bounds, Array compatibility is usually IndexError, but
271
- # based on your design, we return nil
272
-
273
296
  end
274
297
 
275
- # 2. Handle Slice Replacement (list[2, 3] = [a, b] or list[2..4] = [a, b])
298
+ # 2. Handle Slice Replacement
276
299
  start_index, length = *args
277
300
 
278
301
  if args.size == 1 && start_index.is_a?(Range)
@@ -293,20 +316,17 @@ module DLinked
293
316
 
294
317
  replacement = Array(replacement)
295
318
 
296
- # Find Boundaries
297
319
  predecessor = start_index.positive? ? find_node_at_index(start_index - 1) : nil
298
320
  current = predecessor ? predecessor.next : @head
299
321
 
300
322
  deleted_count = 0
301
323
  length.times do
302
324
  break unless current
303
-
304
325
  current = current.next
305
326
  deleted_count += 1
306
327
  end
307
328
  successor = current
308
329
 
309
- # Stage 1: DELETION (Relink the neighbors)
310
330
  if predecessor
311
331
  predecessor.next = successor
312
332
  else
@@ -320,43 +340,37 @@ module DLinked
320
340
  end
321
341
  @size -= deleted_count
322
342
 
323
- # Stage 2: INSERTION (Insert new nodes at the boundary)
324
343
  insertion_point = predecessor
325
344
  replacement.each do |value|
326
345
  new_node = Node.new(value, insertion_point, successor)
327
-
328
346
  if insertion_point
329
347
  insertion_point.next = new_node
330
348
  else
331
349
  @head = new_node
332
350
  end
333
-
334
351
  insertion_point = new_node
335
352
  @size += 1
336
353
  end
337
354
 
338
- # Stage 3: FINAL RELINKING (The last inserted node links back to the successor)
339
355
  if successor
340
356
  successor.prev = insertion_point
341
357
  else
342
358
  @tail = insertion_point
343
359
  end
344
360
 
345
- replacement # Return the set values
361
+ replacement
346
362
  end
347
363
 
348
364
  # Inserts a new element at the specified index.
349
365
  #
350
- # If the index is 0, this is equivalent to {#prepend} (O(1)).
351
- # If the index is equal to or greater than the size, this is equivalent to {#append} (O(1)).
352
- # For all other valid indices, this is an **O(n)** operation as it requires traversal
353
- # to find the insertion point.
354
- #
355
- # Supports negative indices, where list.insert(-1, value) inserts before the last element.
366
+ # This is an **O(n)** operation, unless inserting at the head (`index` = 0)
367
+ # or tail (`index` >= `size`), in which case it is **O(1)**.
356
368
  #
357
- # @param index [Integer] The index before which the new element should be inserted.
358
- # @param value [Object] The value to be inserted.
359
- # @return [DLinked::List] Returns the list instance for method chaining.
369
+ # @param index [Integer] The index before which to insert the new element.
370
+ # @param value [Object] The value to insert.
371
+ # @return [self] The list instance for method chaining.
372
+ # @see #prepend
373
+ # @see #append
360
374
  def insert(index, value)
361
375
  if index.negative?
362
376
  index += @size
@@ -367,8 +381,6 @@ module DLinked
367
381
  return append(value) if index >= @size
368
382
 
369
383
  current = find_node_at_index(index)
370
-
371
- # Insert before current node (O(1) linking)
372
384
  new_node = Node.new(value, current.prev, current)
373
385
  current.prev.next = new_node
374
386
  current.prev = new_node
@@ -377,13 +389,12 @@ module DLinked
377
389
  self
378
390
  end
379
391
 
380
- # Deletes the *first* node that matches the given value and returns the value of the deleted element.
392
+ # Deletes the *first* node that matches the given value.
381
393
  #
382
- # This is an **O(n)** operation because it requires traversal to find the node.
383
- # However, once the node is found, the relinking operation is O(1).
394
+ # This is an **O(n)** operation.
384
395
  #
385
396
  # @param value [Object] The value to search for and delete.
386
- # @return [Object, nil] The value of the deleted element, or nil if the value was not found in the list.
397
+ # @return [Object, nil] The value of the deleted element, or `nil` if not found.
387
398
  def delete(value)
388
399
  current = @head
389
400
  while current
@@ -396,13 +407,13 @@ module DLinked
396
407
  nil
397
408
  end
398
409
 
399
- # Concatenates the elements of another DLinked::List to the end of this list, modifying the current list.
410
+ # Appends the elements of another list to this one (destructive).
400
411
  #
401
- # This is an **O(n)** operation, where n is the size of the *other* list, as it must traverse and link
402
- # every element of the other list into the current list structure.
412
+ # This is an **O(k)** operation, where `k` is the size of the `other` list.
403
413
  #
404
- # @param other [DLinked::List] The list whose elements will be appended.
405
- # @return [self] Returns the modified list instance.
414
+ # @param other [#each] An enumerable object to append.
415
+ # @return [self] The modified list instance.
416
+ # @raise [TypeError] if `other` is not enumerable.
406
417
  def concat(other)
407
418
  raise TypeError, "can't convert #{other.class} into DLinked::List" unless other.respond_to?(:each)
408
419
  return self if other.empty?
@@ -411,14 +422,13 @@ module DLinked
411
422
  self
412
423
  end
413
424
 
414
- # Returns a new DLinked::List that is the concatenation of this list and another list.
425
+ # Returns a new list by concatenating this list with another (non-destructive).
415
426
  #
416
- # This is a non-destructive operation, meaning neither the current list nor the other list is modified.
417
- # The complexity is **O(n + k)**, where n is the size of the current list and k is the size of the other list,
418
- # as both must be traversed and copied into the new list.
427
+ # The complexity is **O(n + k)**, where `n` is the size of this list and `k`
428
+ # is the size of the `other` list.
419
429
  #
420
- # @param other [DLinked::List] The list to append to this one.
421
- # @return [DLinked::List] A new list containing all elements from both lists.
430
+ # @param other [#each] The enumerable object to concatenate.
431
+ # @return [DLinked::List] A new list containing all elements from both.
422
432
  def +(other)
423
433
  new_list = self.class.new
424
434
  each { |value| new_list.append(value) }
@@ -426,41 +436,37 @@ module DLinked
426
436
  new_list
427
437
  end
428
438
 
429
- # Extracts a slice of elements from the list, returning a new DLinked::List instance.
439
+ # @overload slice(index)
440
+ # Extracts a single element at `index` and returns it in a new list.
441
+ # @param index [Integer] The index to retrieve.
442
+ # @return [DLinked::List, nil] A new list with one element, or `nil` if out of bounds.
430
443
  #
431
- # Supports slicing via:
432
- # 1. Start index and length (e.g., list.slice(1, 2))
433
- # 2. Range (e.g., list.slice(1..3))
444
+ # @overload slice(start, length)
445
+ # @param start [Integer] The starting index.
446
+ # @param length [Integer] The number of elements in the slice.
447
+ # @return [DLinked::List, nil] A new list, or `nil` if `start` is out of bounds.
434
448
  #
435
- # This is an **O(n)** operation, where n is the traversal time to find the start point,
436
- # plus the time to copy the slice elements into a new list.
437
- #
438
- # @param start [Integer, Range] The starting index or a Range object defining the slice.
439
- # @param length [Integer, nil] The number of elements to include in the slice.
440
- # @return [DLinked::List, nil] A new list containing the sliced elements, or nil if the slice is out of bounds.
449
+ # @overload slice(range)
450
+ # @param range [Range] A range of indices.
451
+ # @return [DLinked::List] A new list.
441
452
  def slice(start, length = nil)
442
- # Handle Range Argument
443
453
  if start.is_a?(Range) && length.nil?
444
454
  range = start
445
455
  start = range.begin
446
456
  length = range.end - range.begin + (range.exclude_end? ? 0 : 1)
447
457
  end
448
458
 
449
- # 1. Resolve start index (including negative indices)
450
459
  start += @size if start.negative?
451
-
452
460
  return nil if start.negative? || start >= @size
453
461
 
454
462
  if length.nil?
455
463
  node = find_node_at_index(start)
456
464
  new_list = self.class.new
457
465
  new_list.append(node.value)
458
- return new_list # Returns DLinked::List: [value]
466
+ return new_list
459
467
  end
460
468
 
461
- # Handle negative length returning nil
462
- return nil if length.negative?
463
- return List.new if length.zero?
469
+ return List.new if length.negative?
464
470
 
465
471
  new_list = List.new
466
472
  current = find_node_at_index(start)
@@ -475,19 +481,18 @@ module DLinked
475
481
  new_list
476
482
  end
477
483
 
478
- # Extracts and removes a slice of elements from the list, returning a new list
479
- # containing the removed elements.
484
+ # Extracts and removes a slice of elements, returning the removed slice as a new list.
480
485
  #
481
- # Supports destructive slicing via:
482
- # 1. Start index and length (e.g., list.slice!(1, 2))
483
- # 2. Range (e.g., list.slice!(1..3))
486
+ # This is an **O(n)** operation.
484
487
  #
485
- # The complexity is **O(n + k)**, where n is the traversal time to find the start point,
486
- # and k is the number of elements removed/copied.
488
+ # @overload slice!(start, length)
489
+ # @param start [Integer] The starting index.
490
+ # @param length [Integer] The number of elements to remove.
491
+ # @return [DLinked::List, nil] A new list with the removed elements, or `nil` if the slice is empty.
487
492
  #
488
- # @param args [Array] Arguments representing the slice: (start_index, length) or (range).
489
- # @return [DLinked::List, nil] A new list containing the extracted and removed elements,
490
- # or nil if the slice is empty or invalid.
493
+ # @overload slice!(range)
494
+ # @param range [Range] The range of indices to remove.
495
+ # @return [DLinked::List, nil] A new list with the removed elements, or `nil` if the slice is empty.
491
496
  def slice!(*args)
492
497
  start_index, length = *args
493
498
 
@@ -510,7 +515,6 @@ module DLinked
510
515
 
511
516
  length.times do
512
517
  break unless current
513
-
514
518
  current = current.next
515
519
  end
516
520
  successor = current
@@ -544,9 +548,36 @@ module DLinked
544
548
  result.empty? ? nil : result
545
549
  end
546
550
 
551
+ protected
552
+
553
+ # Handles the O(1) pointer logic for prepending a node.
554
+ # @param node [DLinked::List::Node] The node to prepend.
555
+ # @return [self]
556
+ def list_prepend_logic(node)
557
+ @head.prev = node if @head
558
+ @head = node
559
+ @tail ||= node
560
+ @size += 1
561
+ self
562
+ end
563
+
564
+ # Handles the O(1) pointer logic for appending a node.
565
+ # @param node [DLinked::List::Node] The node to append.
566
+ # @return [self]
567
+ def list_append_logic(node)
568
+ @tail.next = node if @tail
569
+ @tail = node
570
+ @head ||= node
571
+ @size += 1
572
+ self
573
+ end
574
+
547
575
  private
548
576
 
549
- # O(n/2) - Internal helper method to find the node at a valid index.
577
+ # Finds the node at a valid index using an optimized O(n/2) search.
578
+ # @param index [Integer] The index to find.
579
+ # @return [DLinked::List::Node] The node at the specified index.
580
+ # @api private
550
581
  def find_node_at_index(index)
551
582
  # Optimization: Start from head or tail, whichever is closer
552
583
  if index <= @size / 2
@@ -559,7 +590,9 @@ module DLinked
559
590
  current
560
591
  end
561
592
 
562
- # O(1) - Internal method to delete a specific node
593
+ # Deletes a node from the list in O(1) time.
594
+ # @param node [DLinked::List::Node] The node to delete.
595
+ # @api private
563
596
  def delete_node(node)
564
597
  if node.prev
565
598
  node.prev.next = node.next
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DLinked
4
- VERSION = '0.1.7'
4
+ VERSION = '0.1.9'
5
5
  end
data/lib/dlinked.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'd_linked/version'
4
- require_relative 'd_linked/list'
4
+
5
+ require_relative 'd_linked/list'
6
+
7
+ require_relative 'd_linked/cache_list'
5
8
 
6
9
  module DLinked
7
- # The DLinked module serves as the namespace for the gem.
8
- end
10
+ # Namespace definition is fine here, it just re-opens the module.
11
+ end