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

CircularLinkedList::searchLast()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

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