Completed
Push — master ( bca99a...ce603c )
by Siro Díaz
01:43
created

DoublyLinkedList::contains()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 17
Ratio 100 %

Importance

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