Completed
Push — master ( 2cb83b...930afd )
by Siro Díaz
01:41
created

DoublyLinkedList::remove()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.439
c 0
b 0
f 0
cc 5
eloc 20
nc 5
nop 1
1
<?php
2
/**
3
 * DataStructures for PHP
4
 *
5
 * @link      https://github.com/SiroDiaz/DataStructures
6
 * @copyright Copyright (c) 2017 Siro Díaz Palazón
7
 * @license   https://github.com/SiroDiaz/DataStructures/blob/master/README.md (MIT License)
8
 */
9
namespace DataStructures\Lists;
10
11
use DataStructures\Lists\Traits\{CountTrait, ArrayAccessTrait};
12
use DataStructures\Lists\Nodes\DoublyLinkedListNode as Node;
13
use DataStructures\Lists\Interfaces\ListInterface;
14
use OutOfBoundsException;
15
16
/**
17
 * DoublyLinkedList
18
 *
19
 * DoublyLinkedList is a double linked list that has
20
 * a pointer to the next and the previous node.
21
 *
22
 * @author Siro Diaz Palazon <[email protected]>
23
 */
24
class DoublyLinkedList implements ListInterface {
25
    use ArrayAccessTrait;
26
27
    private $head;
28
    private $tail;
29
    private $size;
30
    private $position;
31
    private $current;
32
33 View Code Duplication
    public function __construct() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
34
        $this->head = null;
35
        $this->tail = &$this->head;
36
        $this->size = 0;
37
        $this->position = 0;
38
        $this->current = &$this->head;
39
    }
40
41
    /**
42
     * Removes all nodes of the list. It removes from the beginning.
43
     */
44
    public function clear() {
45
        while($this->head !== null) {
46
            $this->shift();
47
        }
48
    }
49
    
50
    /**
51
     * Gets the data stored in the position especified.
52
     *
53
     * @param integer $index Index that must be greater than 0
54
     *  and lower than the list size.
55
     * @return mixed The data stored in the given index
56
     * @throws OutOfBoundsException if index is out bounds.
57
     */
58 View Code Duplication
    public function get($index) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
59
        $node = $this->search($index);
60
        if($node === null) {
61
            return null;
62
        }
63
64
        return $node->data;
65
    }
66
67
    /**
68
     * Gets the node stored in the position especified.
69
     * If index is 0 or (size - 1) the method is O(1) else O(n).
70
     *
71
     * @param integer $index the position.
72
     * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1)
73
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index.
74
     */
75
    protected function search($index) {
76
        if($index < 0 || $index > $this->size - 1) {
77
            throw new OutOfBoundsException();
78
        }
79
80
        if($index === 0) {
81
            return $this->head;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->head; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 81 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
82
        }
83
84
        if($index === $this->size - 1) {
85
            return $this->tail;
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->tail; of type null|DataStructures\List...es\DoublyLinkedListNode adds the type DataStructures\Lists\Nodes\DoublyLinkedListNode to the return on line 85 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
86
        }
87
88
        $current = &$this->head;
89
        if($index < (int) ceil($this->size / 2)) {
90
            $i = 0;
91
            while($i < $index) {
92
                $current = &$current->next;
93
                $i++;
94
            }    
95
        } else {
96
            $i = $this->size;
97
            while($i > $index) {
98
                $current = &$current->prev;
99
                $i--;
100
            }
101
        }
102
103
        return $current;
104
    }
105
106
    /**
107
     * Returns the data in the last node with O(1).
108
     *
109
     * @return mixed null if the list is empty.
110
     */
111
    public function getLast() {
112
        if($this->head === null) {
113
            return null;
114
        }
115
        return $this->tail->data;
116
    }
117
118
    /**
119
     * Returns the last node with O(1).
120
     *
121
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty.
122
     */
123
    public function searchLast() {
124
        if($this->head === null) {
125
            return null;
126
        }
127
        return $this->tail;
128
    }
129
130
    /**
131
     *
132
     */
133
    public function contains($data) : bool {
134
        if($this->empty()) {
135
            return false;
136
        }
137
138
        $current = $this->head->next;
139
        $prev = $this->head;
140
        while($current !== $this->head) {
141
            if($prev->data === $data) {
142
                return true;
143
            }
144
            $prev = $current;
145
            $current = $current->next;
146
        }
147
148
        return false;
149
    }
150
151
    /**
152
     *
153
     */
154 View Code Duplication
    public function indexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
155
        if($this->head === null) {
156
            return false;
157
        }
158
        
159
        $current = $this->head;
160
        $i = 0;
161
        
162
        while($i < $this->size) {
163
            if($current->data === $data) {
164
                return $i;
165
            }
166
167
            $current = $current->next;
168
            $i++;
169
        }
170
171
        return false;
172
    }
173
174
    /**
175
     *
176
     */
177 View Code Duplication
    public function lastIndexOf($data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
178
        if($this->head === null) {
179
            return false;
180
        }
181
        
182
        $current = $this->head;
183
        $i = 0;
184
        $pos = false;
185
        while($i < $this->size) {
186
            if($current->data == $data) {
187
                $pos = $i;
188
            }
189
190
            $current = $current->next;
191
            $i++;
192
        }
193
194
        return $pos;
195
    }
196
197
    /**
198
     *
199
     */
200
    public function remove($data) {
201
        $current = &$this->head;
202
        $i = 0;
203
        
204
        if($this->head === null) {
205
            return null;
206
        }
207
208
        if($this->head->data === $data) {
209
            $aux = $this->head->prev;
0 ignored issues
show
Unused Code introduced by
$aux is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
210
            $this->head = &$this->head->next;
211
            $this->head->prev = &$this->tail;
212
            // $this->tail->next = &$this->head;
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
213
            $this->size--;
214
            
215
            return $data;
216
        }
217
218
        while($i < $this->size) {
219
            if($current->data === $data) {
220
                echo $this->head->prev->data;
221
                $current->prev = &$current->next;
222
                $current = null;
223
                $this->size--;
224
                return $data;
225
            }
226
227
            $current = $current->next;
228
        }
229
230
        return null;
231
    }
232
    
233
    /**
234
     * Generator for retrieve all nodes stored.
235
     * 
236
     * @return null if the head is null (or list is empty)
237
     */
238 View Code Duplication
    public function getAll() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
239
        if($this->head === null) {
240
            return;
241
        }
242
243
        if($this->head === $this->tail) {
244
            yield $this->head->data;
245
        } else {
246
            $current = $this->head;
247
            $i = 0;
248
            while($i < $this->size) {
249
                yield $current->data;
250
                $current = $current->next;
251
                $i++;
252
            }
253
        }
254
    }
255
256
    /**
257
     * Inserts data in the specified position.
258
     *
259
     * @param integer $index the position.
260
     * @param mixed $data the data to store.
261
     */
262 View Code Duplication
    public function insert($index, $data) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
        if($index < 0) {
264
            throw new OutOfBoundsException();
265
        }
266
267
        if($index === 0) {
268
            $this->insertBeginning($data);
269
        } else if($index >= $this->size) {
270
            $this->insertEnd($data);
271
        } else if($index > 0 && $index < $this->size) {
272
            $this->insertAt($index, $data);
273
        }
274
        
275
        $this->size++;
276
    }
277
278
    /**
279
     * Inserts at the beginning of the list.
280
     *
281
     * @param mixed $data
282
     */
283
    private function insertBeginning($data) {
284
        $newNode = new Node($data);
285
        if($this->head === null) {
286
            $newNode->next = &$this->head;
287
            $newNode->prev = &$this->head;
288
            $this->head = &$newNode;
289
            $this->tail = &$newNode;
290
        } else {
291
            $this->tail->next = &$newNode;
292
            $newNode->next = &$this->head;
293
            $newNode->prev = &$this->tail;
294
            $this->head = &$newNode;
295
        }
296
    }
297
298
    /**
299
     * Add a new node in the specified index.
300
     *
301
     * @param mixed $data the data to be stored.
302
     */
303
    private function insertEnd($data) {
304
        $newNode = new Node($data);
305
        $this->tail->next = &$newNode;
306
        $newNode->next = &$this->head;
307
        $newNode->prev = &$this->tail;
308
        $this->tail = &$newNode;
309
        $this->head->prev = &$newNode;
310
    }
311
312
    /**
313
     * Add a new node in the specified index.
314
     *
315
     * @param integer $index the position.
316
     * @param mixed $data the data to be stored.
317
     */
318
    private function insertAt($index, $data) {
319
        $newNode = new Node($data);
320
        $current = $this->head;
321
        $prev = null;
322
        $i = 0;
323
        while($i < $index) {
324
            $prev = $current;
325
            $current = $current->next;
326
            $i++;
327
        }
328
        
329
        $prev->next = &$newNode;
330
        $newNode->prev = &$prev;
331
        $newNode->next = &$current;
332
        $current->prev = &$newNode;
333
    }
334
335
    /**
336
     * Adds at the end of the list new node containing
337
     * the data to be stored.
338
     *
339
     * @param mixed $data The data
340
     */
341
    public function push($data) {
342
        $this->insert($this->size, $data);
343
    }
344
345
    /**
346
     * Adds at the beginning a node in the list.
347
     *
348
     * @param mixed $data
349
     * @return mixed the data stored.
350
     */
351
    public function unshift($data) {
352
        $this->insert(0, $data);
353
    }
354
    
355
    /**
356
     * Delete a node in the given position and returns it back.
357
     *
358
     * @param integer $index the position.
359
     * @throws OutOfBoundsException if index is negative
360
     *  or is greater than the size of the list.
361
     */
362 View Code Duplication
    public function delete($index) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
363
        if($index < 0 || ($index > 0 && $index > $this->size - 1)) {
364
            throw new OutOfBoundsException();
365
        }
366
367
        // if the list is empty
368
        if($this->head === null) {
369
            return null;
370
        }
371
        
372
        // if only there is an element
373
        if($this->head->next === $this->head) {
374
            $temp = $this->head;
375
            $this->head = null;
376
            $this->size--;
377
378
            return $temp->data;
379
        }
380
381
        if($index === 0) {
382
            return $this->deleteBeginning();
383
        } else if($index === $this->size - 1) {
384
            return $this->deleteEnd();
385
        } else {
386
            return $this->deleteAt($index);
387
        }
388
    }
389
390
    /**
391
     * Deletes at the beginnig of the list and returns the data stored.
392
     *
393
     * @return mixed the data stored in the node.
394
     */
395 View Code Duplication
    private function deleteBeginning() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
396
        $temp = $this->head;
397
        $this->head = &$this->head->next;
398
        $this->tail->next = &$this->head;
399
        $this->size--;
400
401
        return $temp->data;
402
    }
403
404
    /**
405
     * Deletes at the specified position and returns the data stored.
406
     *
407
     * @param integer $index the position.
408
     * @return mixed the data stored in the node.
409
     */
410
    private function deleteAt($index) {
411
        $i = 0;
412
        $prev = $this->head;
413
        $current = $this->head;
414
        
415
        while($i < $index) {
416
            $prev = $current;
417
            $current = $current->next;
418
            $i++;
419
        }
420
421
        $temp = $current;
422
        $prev->next = &$current->next;
423
        $current->next->prev = &$pre;
0 ignored issues
show
Bug introduced by
The variable $pre does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
424
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
425
        $this->size--;
426
427
        return $temp->data;
428
    }
429
430
    /**
431
     * Deletes at the end of the list and returns the data stored.
432
     *
433
     * @return mixed the data stored in the node.
434
     */
435 View Code Duplication
    private function deleteEnd() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
436
        $prev = $this->head;
437
        $current = $this->head;
438
        
439
        while($current !== $this->tail) {
440
            $prev = $current;
441
            $current = $current->next;
442
        }
443
        
444
        $temp = $current;
445
        $prev->next = &$this->head;
446
        $this->head->prev = &$prev;
447
        $this->tail = &$prev;
448
        $current = null;
0 ignored issues
show
Unused Code introduced by
$current is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
449
450
        $this->size--;
451
452
        return $temp->data;
453
    }
454
455
    /**
456
     * Deletes the first node of the list and returns it.
457
     *
458
     * @return mixed the data.
459
     */
460
    public function shift() {
461
        return $this->delete(0);
462
    }
463
464
    /**
465
     * Removes and returns the last node in the list.
466
     *
467
     * @return mixed data in node.
468
     */
469
    public function pop() {
470
        return $this->delete($this->size - 1);
471
    }
472
    
473
    /**
474
     * Converts/exports the list content into array type.
475
     *
476
     * @return array data stored in all nodes.
477
     */
478
    public function toArray() : array {
479
        $arr = [];
480
        foreach($this->getAll() as $node) {
481
            $arr[] = $node;
482
        }
483
484
        return $arr;
485
    }
486
487
    /**
488
     * Reset the cursor position.
489
     */
490
    public function rewind() {
491
        $this->position = 0;
492
        $this->current = &$this->head;
493
    }
494
495
    /**
496
     * Returns the current node data.
497
     *
498
     * @return mixed
499
     */
500
    public function current() {
501
        return $this->current->data;
502
    }
503
504
    /**
505
     * Key or index that indicates the cursor position.
506
     *
507
     * @return integer The current position.
508
     */
509
    public function key() {
510
        return $this->position;
511
    }
512
513
    /**
514
     * Move the cursor to the next node and increments the
515
     * position counter.
516
     */
517
    public function next() {
518
        ++$this->position;
519
        $this->current = $this->current->next;
520
    }
521
522
    /**
523
     * Returns if the current pointer position is valid.
524
     *
525
     * @return boolean true if pointer is not last, else false.
526
     */
527
    public function valid() {
528
        return $this->position < $this->size;
529
    }
530
531
    /**
532
     * Binds to count() method. This is equal to make $this->tree->size().
533
     *
534
     * @return integer the tree size. 0 if it is empty.
535
     */
536
    public function count() {
537
        return $this->size;
538
    }
539
540
    /**
541
     * Returns the array size.
542
     *
543
     * @return int the length
544
     */
545
    public function size() : int {
546
        return $this->size;
547
    }
548
549
    /**
550
     * Checks if the list is empty.
551
     *
552
     * @return boolean true if is empty, else false.
553
     */
554
    public function empty() : bool {
555
        return $this->size === 0;
556
    }
557
}