Completed
Push — master ( da3ca6...f77e5d )
by Siro Díaz
01:51
created

DoublyLinkedList   B

Complexity

Total Complexity 53

Size/Duplication

Total Lines 384
Duplicated Lines 28.65 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 0 Features 1
Metric Value
wmc 53
lcom 1
cbo 3
dl 110
loc 384
rs 7.4757
c 1
b 0
f 1

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 7 7 1
A get() 8 8 2
C search() 0 30 8
A getLast() 0 6 2
A searchLast() 0 6 2
B contains() 0 21 5
A indexOf() 19 19 4
A lastIndexOf() 19 19 4
B remove() 10 29 5
A getAll() 17 17 4
A insertBeginning() 0 14 2
A insertEnd() 0 8 1
A insertAt() 0 16 2
A deleteBeginning() 13 13 2
A deleteAt() 0 18 2
A deleteEnd() 17 17 2
A rewind() 0 4 1
A current() 0 3 1
A key() 0 3 1
A next() 0 4 1
A valid() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like DoublyLinkedList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DoublyLinkedList, and based on these observations, apply Extract Interface, too.

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