Completed
Push — master ( 71e8d2...303a8a )
by Siro Díaz
01:32
created

CircularLinkedList   B

Complexity

Total Complexity 54

Size/Duplication

Total Lines 388
Duplicated Lines 32.73 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 54
lcom 1
cbo 3
dl 127
loc 388
rs 7.0642
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 7 7 1
B insert() 15 15 6
A insertBeginning() 9 12 2
A insertEnd() 0 6 1
A insertAt() 0 14 2
A push() 0 3 1
A unshift() 0 3 1
A getLast() 0 5 2
A searchLast() 0 7 2
A get() 8 8 2
B search() 0 22 6
A getAll() 17 17 4
C delete() 27 27 8
A deleteBeginning() 8 8 1
A deleteAt() 18 18 2
A deleteEnd() 18 18 2
A shift() 0 3 1
A pop() 0 3 1
A clear() 0 5 2
A toArray() 0 8 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 CircularLinkedList 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 CircularLinkedList, 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\SimpleLinkedListNode as Node;
13
use DataStructures\Lists\Interfaces\ListInterface;
14
use OutOfBoundsException;
15
16
/**
17
 * CircularLinkedList
18
 *
19
 * CircularLinkedList is a single and circular linked list that has
20
 * a pointer to the next node and also and last node points to head.
21
 *
22
 * @author Siro Diaz Palazon <[email protected]>
23
 */
24
class CircularLinkedList implements ListInterface {
25
    use CountTrait, ArrayAccessTrait;
26
    private $head;
27
    private $tail;
28
    private $size;
29
    private $current;
30
    private $position;
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
     * Inserts data in the specified position.
42
     *
43
     * @param integer $index the position.
44
     * @param mixed $data the data to store.
45
     */
46 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...
47
        if($index < 0) {
48
            throw new OutOfBoundsException();
49
        }
50
51
        if($index === 0) {
52
            $this->insertBeginning($data);
53
        } else if($index >= $this->size) {
54
            $this->insertEnd($data);
55
        } else if($index > 0 && $index < $this->size) {
56
            $this->insertAt($index, $data);
57
        }
58
        
59
        $this->size++;
60
    }
61
62
    /**
63
     * Inserts at the beginning of the list.
64
     *
65
     * @param mixed $data
66
     */
67
    private function insertBeginning($data) {
68
        $newNode = new Node($data);
69 View Code Duplication
        if($this->head === null) {
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...
70
            $newNode->next = &$this->head;
71
            $this->head = &$newNode;
72
            $this->tail = &$newNode;
73
        } else {
74
            $this->tail->next = &$newNode;
75
            $newNode->next = &$this->head;
76
            $this->head = &$newNode;
77
        }
78
    }
79
80
    /**
81
     * Add a new node in the specified index.
82
     *
83
     * @param integer $index the position.
0 ignored issues
show
Bug introduced by
There is no parameter named $index. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

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