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

DoublyLinkedList::search()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 30
Code Lines 19

Duplication

Lines 30
Ratio 100 %

Importance

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