Completed
Push — master ( 547e17...d8178d )
by Siro Díaz
01:37
created

DoublyLinkedList::offsetExists()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
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 CountTrait;
26
    use ArrayAccessTrait;
27
28
    private $head;
29
    private $tail;
30
    private $size;
31
    private $position;
32
    private $current;
33
34 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...
35
        $this->head = null;
36
        $this->tail = &$this->head;
37
        $this->size = 0;
38
        $this->position = 0;
39
        $this->current = &$this->head;
40
    }
41
42
    /**
43
     * Removes all nodes of the list. It removes from the beginning.
44
     */
45
    public function clear() {
46
        while($this->head !== null) {
47
            $this->shift();
48
        }
49
    }
50
    
51
    /**
52
     * Gets the data stored in the position especified.
53
     *
54
     * @param integer $index Index that must be greater than 0
55
     *  and lower than the list size.
56
     * @return mixed The data stored in the given index
57
     * @throws OutOfBoundsException if index is out bounds.
58
     */
59 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...
60
        if($index < 0 || $index > $this->size - 1) {
61
            throw new OutOfBoundsException();
62
        }
63
64
        if($index === 0) {
65
            return $this->head->data;
66
        }
67
68
        if($index === $this->size - 1) {
69
            return $this->tail->data;
70
        }
71
72
        $current = &$this->head;
73
        if($index < (int) ceil($this->size / 2)) {
74
            $i = 0;
75
            while($i < $index) {
76
                $current = &$current->next;
77
                $i++;
78
            }    
79
        } else {
80
            $i = $this->size;
81
            while($i > $index) {
82
                $current = &$current->prev;
83
                $i--;
84
            }
85
        }
86
87
        return $current->data;
88
    }
89
90
    /**
91
     * Gets the node stored in the position especified.
92
     * If index is 0 or (size - 1) the method is O(1) else O(n).
93
     *
94
     * @param integer $index the position.
95
     * @throws OutOfBoundsException if it is out of limits (< 0 or > size - 1)
96
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null the node stored in $index.
97
     */
98 View Code Duplication
    protected function search($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...
99
        if($index < 0 || $index > $this->size - 1) {
100
            throw new OutOfBoundsException();
101
        }
102
103
        if($index === 0) {
104
            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 104 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
105
        }
106
107
        if($index === $this->size - 1) {
108
            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 108 which is incompatible with the return type documented by DataStructures\Lists\DoublyLinkedList::search of type DataStructures\Lists\Dat...ublyLinkedListNode|null.
Loading history...
109
        }
110
111
        $current = &$this->head;
112
        if($index < (int) ceil($this->size / 2)) {
113
            $i = 0;
114
            while($i < $index) {
115
                $current = &$current->next;
116
                $i++;
117
            }    
118
        } else {
119
            $i = $this->size;
120
            while($i > $index) {
121
                $current = &$current->prev;
122
                $i--;
123
            }
124
        }
125
126
        return $current;
127
    }
128
129
    /**
130
     * Returns the data in the last node with O(1).
131
     *
132
     * @return mixed null if the list is empty.
133
     */
134
    public function getLast() {
135
        if($this->head === null) {
136
            return null;
137
        }
138
        return $this->tail->data;
139
    }
140
141
    /**
142
     * Returns the last node with O(1).
143
     *
144
     * @return DataStructures\Lists\Nodes\DoublyLinkedListNode|null if the list is empty.
145
     */
146
    public function searchLast() {
147
        if($this->head === null) {
148
            return null;
149
        }
150
        return $this->tail;
151
    }
152
    
153
    /**
154
     * Generator for retrieve all nodes stored.
155
     * 
156
     * @return null if the head is null (or list is empty)
157
     */
158 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...
159
        if($this->head === null) {
160
            return;
161
        }
162
163
        if($this->head === $this->tail) {
164
            yield $this->head->data;
165
        } else {
166
            $current = $this->head;
167
            $i = 0;
168
            while($i < $this->size) {
169
                yield $current->data;
170
                $current = $current->next;
171
                $i++;
172
            }
173
        }
174
    }
175
176
    /**
177
     * Inserts data in the specified position.
178
     *
179
     * @param integer $index the position.
180
     * @param mixed $data the data to store.
181
     */
182 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...
183
        if($index < 0) {
184
            throw new OutOfBoundsException();
185
        }
186
187
        if($index === 0) {
188
            $this->insertBeginning($data);
189
        } else if($index >= $this->size) {
190
            $this->insertEnd($data);
191
        } else if($index > 0 && $index < $this->size) {
192
            $this->insertAt($index, $data);
193
        }
194
        
195
        $this->size++;
196
    }
197
198
    /**
199
     * Inserts at the beginning of the list.
200
     *
201
     * @param mixed $data
202
     */
203
    private function insertBeginning($data) {
204
        $newNode = new Node($data);
205
        if($this->head === null) {
206
            $newNode->next = &$this->head;
207
            $newNode->prev = &$this->head;
208
            $this->head = &$newNode;
209
            $this->tail = &$newNode;
210
        } else {
211
            $this->tail->next = &$newNode;
212
            $newNode->next = &$this->head;
213
            $newNode->prev = &$this->tail;
214
            $this->head = &$newNode;
215
        }
216
    }
217
218
    /**
219
     * Add a new node in the specified index.
220
     *
221
     * @param mixed $data the data to be stored.
222
     */
223
    private function insertEnd($data) {
224
        $newNode = new Node($data);
225
        $this->tail->next = &$newNode;
226
        $newNode->next = &$this->head;
227
        $newNode->prev = &$this->tail;
228
        $this->tail = &$newNode;
229
        $this->head->prev = &$newNode;
230
    }
231
232
    /**
233
     * Add a new node in the specified index.
234
     *
235
     * @param integer $index the position.
236
     * @param mixed $data the data to be stored.
237
     */
238
    private function insertAt($index, $data) {
239
        $newNode = new Node($data);
240
        $current = $this->head;
241
        $prev = null;
242
        $i = 0;
243
        while($i < $index) {
244
            $prev = $current;
245
            $current = $current->next;
246
            $i++;
247
        }
248
        
249
        $prev->next = &$newNode;
250
        $newNode->prev = &$prev;
251
        $newNode->next = &$current;
252
        $current->prev = &$newNode;
253
    }
254
255
    /**
256
     * Adds at the end of the list new node containing
257
     * the data to be stored.
258
     *
259
     * @param mixed $data The data
260
     */
261
    public function push($data) {
262
        $this->insert($this->size, $data);
263
    }
264
265
    /**
266
     * Adds at the beginning a node in the list.
267
     *
268
     * @param mixed $data
269
     * @return mixed the data stored.
270
     */
271
    public function unshift($data) {
272
        $this->insert(0, $data);
273
    }
274
    
275
    /**
276
     * Delete a node in the given position and returns it back.
277
     *
278
     * @param integer $index the position.
279
     * @throws OutOfBoundsException if index is negative
280
     *  or is greater than the size of the list.
281
     */
282 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...
283
        if($index < 0 || ($index > 0 && $index > $this->size - 1)) {
284
            throw new OutOfBoundsException();
285
        }
286
287
        // if the list is empty
288
        if($this->head === null) {
289
            return null;
290
        }
291
        
292
        // if only there is an element
293
        if($this->head->next === $this->head) {
294
            $temp = $this->head;
295
            $this->head = null;
296
            $this->size--;
297
298
            return $temp->data;
299
        }
300
301
        if($index === 0) {
302
            return $this->deleteBeginning();
303
        } else if($index === $this->size - 1) {
304
            return $this->deleteEnd();
305
        } else {
306
            return $this->deleteAt($index);
307
        }
308
    }
309
310
    /**
311
     * Deletes at the beginnig of the list and returns the data stored.
312
     *
313
     * @return mixed the data stored in the node.
314
     */
315 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...
316
        $temp = $this->head;
317
        $this->head = &$this->head->next;
318
        $this->tail->next = &$this->head;
319
        $this->size--;
320
321
        return $temp->data;
322
    }
323
324
    /**
325
     * Deletes at the specified position and returns the data stored.
326
     *
327
     * @param integer $index the position.
328
     * @return mixed the data stored in the node.
329
     */
330 View Code Duplication
    private function deleteAt($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...
331
        $i = 0;
332
        $prev = $this->head;
333
        $current = $this->head;
334
        
335
        while($i < $index) {
336
            $prev = $current;
337
            $current = $current->next;
338
            $i++;
339
        }
340
341
        $temp = $current;
342
        $prev->next = &$current->next;
343
        $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...
344
        $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...
345
        $this->size--;
346
347
        return $temp->data;
348
    }
349
350
    /**
351
     * Deletes at the end of the list and returns the data stored.
352
     *
353
     * @return mixed the data stored in the node.
354
     */
355 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...
356
        $prev = $this->head;
357
        $current = $this->head;
358
        
359
        while($current !== $this->tail) {
360
            $prev = $current;
361
            $current = $current->next;
362
        }
363
        
364
        $temp = $current;
365
        $prev->next = &$this->head;
366
        $this->head->prev = &$prev;
367
        $this->tail = &$prev;
368
        $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...
369
370
        $this->size--;
371
372
        return $temp->data;
373
    }
374
375
    /**
376
     * Deletes the first node of the list and returns it.
377
     *
378
     * @return mixed the data.
379
     */
380
    public function shift() {
381
        return $this->delete(0);
382
    }
383
384
    /**
385
     * Removes and returns the last node in the list.
386
     *
387
     * @return mixed data in node.
388
     */
389
    public function pop() {
390
        return $this->delete($this->size - 1);
391
    }
392
    
393
    /**
394
     * Converts/exports the list content into array type.
395
     *
396
     * @return array data stored in all nodes.
397
     */
398
    public function toArray() : array {
399
        $arr = [];
400
        foreach($this->getAll() as $node) {
401
            $arr[] = $node;
402
        }
403
404
        return $arr;
405
    }
406
407
    /**
408
     * Reset the cursor position.
409
     */
410
    public function rewind() {
411
        $this->position = 0;
412
        $this->current = &$this->head;
413
    }
414
415
    /**
416
     * Returns the current node data.
417
     *
418
     * @return mixed
419
     */
420
    public function current() {
421
        return $this->current->data;
422
    }
423
424
    /**
425
     * Key or index that indicates the cursor position.
426
     *
427
     * @return integer The current position.
428
     */
429
    public function key() {
430
        return $this->position;
431
    }
432
433
    /**
434
     * Move the cursor to the next node and increments the
435
     * position counter.
436
     */
437
    public function next() {
438
        ++$this->position;
439
        $this->current = $this->current->next;
440
    }
441
442
    /**
443
     * Returns if the current pointer position is valid.
444
     *
445
     * @return boolean true if pointer is not last, else false.
446
     */
447
    public function valid() {
448
        return $this->position < $this->size;
449
    }
450
}