Skip to content

Commit 3fcfa36

Browse files
authoredJan 18, 2022
Merge pull request #9 from dan-on/feature/generics-types-support
feat: generics types support
2 parents c497d80 + 8e642bf commit 3fcfa36

14 files changed

+277
-130
lines changed
 

‎phpstan.neon.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ parameters:
22
level: 7
33
paths:
44
- src
5-
- tests
5+
- tests

‎src/Interval/DateTimeInterval.php

+22-16
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,26 @@
99

1010
use function count;
1111

12+
/**
13+
* @template TPoint of DateTimeInterface
14+
* @implements IntervalInterface<TPoint>
15+
*/
1216
final class DateTimeInterval implements IntervalInterface
1317
{
1418
/**
15-
* @var DateTimeInterface
19+
* @var TPoint
1620
*/
1721
private $low;
1822

1923
/**
20-
* @var DateTimeInterface
24+
* @var TPoint
2125
*/
2226
private $high;
2327

2428
/**
2529
* DateTimeInterval constructor
26-
* @param DateTimeInterface $low
27-
* @param DateTimeInterface $high
30+
* @param TPoint $low
31+
* @param TPoint $high
2832
*/
2933
public function __construct($low, $high)
3034
{
@@ -37,10 +41,12 @@ public function __construct($low, $high)
3741
}
3842

3943
/**
40-
* @param DateTimeInterface[] $interval
41-
* @return DateTimeInterval
44+
* @phpstan-ignore-next-line
45+
* @psalm-template TPoint of DateTimeInterface
46+
* @param TPoint[] $interval
47+
* @return IntervalInterface<TPoint>
4248
*/
43-
public static function fromArray($interval): DateTimeInterval
49+
public static function fromArray(array $interval): IntervalInterface
4450
{
4551
if (count($interval) !== 2) {
4652
throw new InvalidArgumentException('Wrong interval array');
@@ -59,20 +65,20 @@ public function getHigh(): DateTimeInterface
5965
}
6066

6167
/**
62-
* @param DateTimeInterval $otherInterval
68+
* @param IntervalInterface<TPoint> $otherInterval
6369
* @return bool
6470
*/
65-
public function equalTo($otherInterval): bool
71+
public function equalTo(IntervalInterface $otherInterval): bool
6672
{
6773
return $this->getLow()->getTimestamp() === $otherInterval->getLow()->getTimestamp() &&
6874
$this->getHigh()->getTimestamp() === $otherInterval->getHigh()->getTimestamp();
6975
}
7076

7177
/**
72-
* @param DateTimeInterval $otherInterval
78+
* @param IntervalInterface<TPoint> $otherInterval
7379
* @return bool
7480
*/
75-
public function lessThan($otherInterval): bool
81+
public function lessThan(IntervalInterface $otherInterval): bool
7682
{
7783
return $this->getLow()->getTimestamp() < $otherInterval->getLow()->getTimestamp() ||
7884
(
@@ -82,19 +88,19 @@ public function lessThan($otherInterval): bool
8288
}
8389

8490
/**
85-
* @param DateTimeInterval $otherInterval
91+
* @param IntervalInterface<TPoint> $otherInterval
8692
* @return bool
8793
*/
88-
public function intersect($otherInterval): bool
94+
public function intersect(IntervalInterface $otherInterval): bool
8995
{
9096
return !($this->getHigh() < $otherInterval->getLow() || $otherInterval->getHigh() < $this->getLow());
9197
}
9298

9399
/**
94-
* @param DateTimeInterval $otherInterval
95-
* @return DateTimeInterval
100+
* @param IntervalInterface<TPoint> $otherInterval
101+
* @return IntervalInterface<TPoint>
96102
*/
97-
public function merge($otherInterval): DateTimeInterval
103+
public function merge(IntervalInterface $otherInterval): IntervalInterface
98104
{
99105
return new DateTimeInterval(
100106
min($this->getLow(), $otherInterval->getLow()),

‎src/Interval/IntervalInterface.php

+44-17
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,56 @@
44

55
namespace Danon\IntervalTree\Interval;
66

7+
/**
8+
* @template TPoint
9+
*/
710
interface IntervalInterface
811
{
9-
// @phpstan-ignore-next-line
12+
/**
13+
* @param TPoint $low
14+
* @param TPoint $high
15+
*/
1016
public function __construct($low, $high);
1117

12-
// @phpstan-ignore-next-line
13-
public static function fromArray($interval);
14-
15-
// @phpstan-ignore-next-line
18+
/**
19+
* @phpstan-ignore-next-line
20+
* @psalm-template TPoint
21+
* @param TPoint[] $interval
22+
* @return IntervalInterface<TPoint>
23+
*/
24+
public static function fromArray(array $interval): IntervalInterface;
25+
26+
/**
27+
* @return TPoint
28+
*/
1629
public function getLow();
1730

18-
// @phpstan-ignore-next-line
31+
/**
32+
* @return TPoint
33+
*/
1934
public function getHigh();
2035

21-
// @phpstan-ignore-next-line
22-
public function equalTo($otherInterval): bool;
23-
24-
// @phpstan-ignore-next-line
25-
public function lessThan($otherInterval): bool;
26-
27-
// @phpstan-ignore-next-line
28-
public function intersect($otherInterval): bool;
29-
30-
// @phpstan-ignore-next-line
31-
public function merge($otherInterval);
36+
/**
37+
* @param IntervalInterface<TPoint> $otherInterval
38+
* @return bool
39+
*/
40+
public function equalTo(IntervalInterface $otherInterval): bool;
41+
42+
/**
43+
* @param IntervalInterface<TPoint> $otherInterval
44+
* @return bool
45+
*/
46+
public function lessThan(IntervalInterface $otherInterval): bool;
47+
48+
/**
49+
* @param IntervalInterface<TPoint> $otherInterval
50+
* @return bool
51+
*/
52+
public function intersect(IntervalInterface $otherInterval): bool;
53+
54+
/**
55+
* @param IntervalInterface<TPoint> $otherInterval
56+
* @return IntervalInterface<TPoint>
57+
*/
58+
public function merge(IntervalInterface $otherInterval): IntervalInterface;
3259
}

‎src/Interval/NumericInterval.php

+24-18
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,26 @@
88

99
use function count;
1010

11+
/**
12+
* @template TPoint of int|float
13+
* @implements IntervalInterface<TPoint>
14+
*/
1115
final class NumericInterval implements IntervalInterface
1216
{
1317
/**
14-
* @var int|float
18+
* @var TPoint
1519
*/
1620
private $low;
1721

1822
/**
19-
* @var int|float
23+
* @var TPoint
2024
*/
2125
private $high;
2226

2327
/**
2428
* NumericInterval constructor
25-
* @param int|float $low
26-
* @param int|float $high
29+
* @param TPoint $low
30+
* @param TPoint $high
2731
*/
2832
public function __construct($low, $high)
2933
{
@@ -36,10 +40,12 @@ public function __construct($low, $high)
3640
}
3741

3842
/**
39-
* @param int[] $interval
40-
* @return NumericInterval
43+
* @phpstan-ignore-next-line
44+
* @psalm-template TPoint of int|float
45+
* @param TPoint[] $interval
46+
* @return IntervalInterface<TPoint>
4147
*/
42-
public static function fromArray($interval): NumericInterval
48+
public static function fromArray(array $interval): IntervalInterface
4349
{
4450
if (count($interval) !== 2) {
4551
throw new InvalidArgumentException('Wrong interval array');
@@ -48,54 +54,54 @@ public static function fromArray($interval): NumericInterval
4854
}
4955

5056
/**
51-
* @return int|float
57+
* @return TPoint
5258
*/
5359
public function getLow()
5460
{
5561
return $this->low;
5662
}
5763

5864
/**
59-
* @return int|float
65+
* @return TPoint
6066
*/
6167
public function getHigh()
6268
{
6369
return $this->high;
6470
}
6571

6672
/**
67-
* @param NumericInterval $otherInterval
73+
* @param IntervalInterface<TPoint> $otherInterval
6874
* @return bool
6975
*/
70-
public function equalTo($otherInterval): bool
76+
public function equalTo(IntervalInterface $otherInterval): bool
7177
{
7278
return $this->getLow() === $otherInterval->getLow() && $this->getHigh() === $otherInterval->getHigh();
7379
}
7480

7581
/**
76-
* @param NumericInterval $otherInterval
82+
* @param IntervalInterface<TPoint> $otherInterval
7783
* @return bool
7884
*/
79-
public function lessThan($otherInterval): bool
85+
public function lessThan(IntervalInterface $otherInterval): bool
8086
{
8187
return $this->getLow() < $otherInterval->getLow() ||
8288
($this->getLow() === $otherInterval->getLow() && $this->getHigh() < $otherInterval->getHigh());
8389
}
8490

8591
/**
86-
* @param NumericInterval $otherInterval
92+
* @param IntervalInterface<TPoint> $otherInterval
8793
* @return bool
8894
*/
89-
public function intersect($otherInterval): bool
95+
public function intersect(IntervalInterface $otherInterval): bool
9096
{
9197
return !($this->getHigh() < $otherInterval->getLow() || $otherInterval->getHigh() < $this->getLow());
9298
}
9399

94100
/**
95-
* @param NumericInterval $otherInterval
96-
* @return NumericInterval
101+
* @param IntervalInterface<TPoint> $otherInterval
102+
* @return IntervalInterface<TPoint>
97103
*/
98-
public function merge($otherInterval): NumericInterval
104+
public function merge(IntervalInterface $otherInterval): IntervalInterface
99105
{
100106
return new NumericInterval(
101107
min($this->getLow(), $otherInterval->getLow()),

‎src/IntervalTree.php

+84-53
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77
use Danon\IntervalTree\Interval\IntervalInterface;
88
use Iterator;
99

10+
/**
11+
* @template TPoint
12+
* @template TValue
13+
*/
1014
final class IntervalTree
1115
{
12-
/** @var Node*/
16+
/** @var Node<TPoint, TValue> */
1317
private $root;
1418

15-
/** @var Node */
19+
/** @var Node<TPoint, TValue> */
1620
private $nilNode;
1721

1822
/**
@@ -45,9 +49,8 @@ public function isEmpty(): bool
4549

4650
/**
4751
* Find pairs which intervals intersect with given interval
48-
*
49-
* @param IntervalInterface $interval
50-
* @return Iterator<Pair>
52+
* @param IntervalInterface<TPoint> $interval
53+
* @return Iterator<Pair<TPoint, TValue>>
5154
*/
5255
public function findIntersections(IntervalInterface $interval): Iterator
5356
{
@@ -60,7 +63,7 @@ public function findIntersections(IntervalInterface $interval): Iterator
6063
/**
6164
* Returns true if interval has at least one intersection in tree
6265
*
63-
* @param IntervalInterface $interval
66+
* @param IntervalInterface<TPoint> $interval
6467
* @return bool
6568
*/
6669
public function hasIntersection(IntervalInterface $interval): bool
@@ -72,7 +75,7 @@ public function hasIntersection(IntervalInterface $interval): bool
7275
/**
7376
* Count intervals that has intersections
7477
*
75-
* @param IntervalInterface $interval
78+
* @param IntervalInterface<TPoint> $interval
7679
* @return int
7780
*/
7881
public function countIntersections(IntervalInterface $interval): int
@@ -84,8 +87,8 @@ public function countIntersections(IntervalInterface $interval): int
8487
/**
8588
* Insert new pair (interval + value) into interval tree
8689
*
87-
* @param IntervalInterface $interval
88-
* @param mixed $value
90+
* @param IntervalInterface<TPoint> $interval
91+
* @param TValue $value
8992
*/
9093
public function insert(IntervalInterface $interval, $value = null): void
9194
{
@@ -103,8 +106,8 @@ public function insert(IntervalInterface $interval, $value = null): void
103106
/**
104107
* Returns true if interval and value exist in the tree
105108
*
106-
* @param IntervalInterface $interval
107-
* @param mixed $value
109+
* @param IntervalInterface<TPoint> $interval
110+
* @param TValue $value
108111
* @return bool
109112
*/
110113
public function exist(IntervalInterface $interval, $value): bool
@@ -116,8 +119,8 @@ public function exist(IntervalInterface $interval, $value): bool
116119
/**
117120
* Remove node from tree by interval and value
118121
*
119-
* @param IntervalInterface $interval
120-
* @param mixed $value
122+
* @param IntervalInterface<TPoint> $interval
123+
* @param TValue $value
121124
* @return bool
122125
*/
123126
public function remove(IntervalInterface $interval, $value): bool
@@ -131,6 +134,10 @@ public function remove(IntervalInterface $interval, $value): bool
131134
return false;
132135
}
133136

137+
/**
138+
* @param Node<TPoint, TValue> $node
139+
* @return void
140+
*/
134141
private function recalculateMax(Node $node): void
135142
{
136143
$nodeCurrent = $node;
@@ -140,6 +147,10 @@ private function recalculateMax(Node $node): void
140147
}
141148
}
142149

150+
/**
151+
* @param Node<TPoint, TValue> $insertNode
152+
* @return void
153+
*/
143154
private function treeInsert(Node $insertNode): void
144155
{
145156
$currentNode = $this->root;
@@ -170,11 +181,7 @@ private function treeInsert(Node $insertNode): void
170181
}
171182

172183
/**
173-
* After insertion insert_node may have red-colored parent
174-
* And this is a single possible violation
175-
* Go upwards to the root and re-color until violation will be resolved
176-
*
177-
* @param Node $insertNode
184+
* @param Node<TPoint, TValue> $insertNode
178185
*/
179186
private function insertFixup(Node $insertNode): void
180187
{
@@ -218,6 +225,10 @@ private function insertFixup(Node $insertNode): void
218225
$this->root->setColor(NodeColor::black());
219226
}
220227

228+
/**
229+
* @param Node<TPoint, TValue> $deleteNode
230+
* @return void
231+
*/
221232
private function treeDelete(Node $deleteNode): void
222233
{
223234
if ($deleteNode->getLeft() === $this->nilNode || $deleteNode->getRight() === $this->nilNode) {
@@ -226,7 +237,6 @@ private function treeDelete(Node $deleteNode): void
226237
$cutNode = $this->treeSuccessor($deleteNode);
227238
}
228239

229-
// fix_node if single child of cut_node
230240
if ($cutNode->getLeft() !== $this->nilNode) {
231241
$fixNode = $cutNode->getLeft();
232242
} else {
@@ -243,24 +253,26 @@ private function treeDelete(Node $deleteNode): void
243253
} else {
244254
$cutNode->getParent()->setRight($fixNode);
245255
}
246-
$cutNode->getParent()->updateMax(); // update max property of the parent
256+
$cutNode->getParent()->updateMax();
247257
}
248258

249-
$this->recalculateMax($fixNode); // update max property upward from fix_node to root
259+
$this->recalculateMax($fixNode);
250260

251-
// deleteNode becomes cutNode, it means that we cannot hold reference
252-
// to node in outer structure and we will have to delete by key, additional search need
253261
if ($cutNode !== $deleteNode) {
254262
$deleteNode->copyPairFrom($cutNode);
255-
$deleteNode->updateMax(); // update max property of the cut node at the new place
256-
$this->recalculateMax($deleteNode); // update max property upward from deleteNode to root
263+
$deleteNode->updateMax();
264+
$this->recalculateMax($deleteNode);
257265
}
258266

259267
if ($cutNode->getColor()->isBlack()) {
260268
$this->deleteFixup($fixNode);
261269
}
262270
}
263271

272+
/**
273+
* @param Node<TPoint, TValue> $fixNode
274+
* @return void
275+
*/
264276
private function deleteFixup(Node $fixNode): void
265277
{
266278
$currentNode = $fixNode;
@@ -286,7 +298,6 @@ private function deleteFixup(Node $fixNode): void
286298
$brotherNode->setColor(NodeColor::red());
287299
$brotherNode->getLeft()->setColor(NodeColor::black());
288300
$this->rotateRight($brotherNode);
289-
$brotherNode = $currentNode->getParent()->getRight();
290301
}
291302
$brotherNode->setColor($currentNode->getParent()->getColor());
292303
$currentNode->getParent()->setColor(NodeColor::black());
@@ -310,7 +321,6 @@ private function deleteFixup(Node $fixNode): void
310321
$brotherNode->setColor(NodeColor::red());
311322
$brotherNode->getRight()->setColor(NodeColor::black());
312323
$this->rotateLeft($brotherNode);
313-
$brotherNode = $currentNode->getParent()->getLeft();
314324
}
315325
$brotherNode->setColor($currentNode->getParent()->getColor());
316326
$currentNode->getParent()->setColor(NodeColor::black());
@@ -324,27 +334,32 @@ private function deleteFixup(Node $fixNode): void
324334
$currentNode->setColor(NodeColor::black());
325335
}
326336

327-
private function treeSearch(Node $node, Node $searchNode): ?Node
337+
/**
338+
* @param Node<TPoint, TValue> $startingNode
339+
* @param Node<TPoint, TValue> $node
340+
* @return Node<TPoint, TValue>|null
341+
*/
342+
private function treeSearch(Node $startingNode, Node $node): ?Node
328343
{
329-
if ($node === $this->nilNode) {
344+
if ($startingNode === $this->nilNode) {
330345
return null;
331346
}
332347

333-
if ($searchNode->equalTo($node)) {
334-
return $node;
335-
}
336-
337-
if ($searchNode->lessThan($node)) {
338-
return $this->treeSearch($node->getLeft(), $searchNode);
348+
if ($node->equalTo($startingNode)) {
349+
$searchedNode = $startingNode;
350+
} elseif ($node->lessThan($startingNode)) {
351+
$searchedNode = $this->treeSearch($startingNode->getLeft(), $node);
352+
} else {
353+
$searchedNode = $this->treeSearch($startingNode->getRight(), $node);
339354
}
340355

341-
return $this->treeSearch($node->getRight(), $searchNode);
356+
return $searchedNode;
342357
}
343358

344359
/**
345-
* @param Node $searchNode
346-
* @param Node|null $fromNode
347-
* @return Iterator<Node>
360+
* @param Node<TPoint, TValue> $searchNode
361+
* @param Node<TPoint, TValue>|null $fromNode
362+
* @return Iterator<Node<TPoint, TValue>>
348363
*/
349364
private function treeSearchInterval(Node $searchNode, Node $fromNode = null): Iterator
350365
{
@@ -360,6 +375,10 @@ private function treeSearchInterval(Node $searchNode, Node $fromNode = null): It
360375
}
361376
}
362377

378+
/**
379+
* @param Node<TPoint, TValue> $node
380+
* @return Node<TPoint, TValue>
381+
*/
363382
private function localMinimum(Node $node): Node
364383
{
365384
$nodeMin = $node;
@@ -369,6 +388,10 @@ private function localMinimum(Node $node): Node
369388
return $nodeMin;
370389
}
371390

391+
/**
392+
* @param Node<TPoint, TValue> $node
393+
* @return Node<TPoint, TValue>|null
394+
*/
372395
private function treeSuccessor(Node $node): ?Node
373396
{
374397
if ($node->getRight() !== $this->nilNode) {
@@ -385,25 +408,29 @@ private function treeSuccessor(Node $node): ?Node
385408
return $nodeSuccessor;
386409
}
387410

411+
/**
412+
* @param Node<TPoint, TValue> $x
413+
* @return void
414+
*/
388415
private function rotateLeft(Node $x): void
389416
{
390417
$y = $x->getRight();
391-
$x->setRight($y->getLeft()); // b goes to x.right
418+
$x->setRight($y->getLeft());
392419

393420
if ($y->getLeft() !== $this->nilNode) {
394-
$y->getLeft()->setParent($x); // x becomes parent of b
421+
$y->getLeft()->setParent($x);
395422
}
396-
$y->setParent($x->getParent()); // move parent
423+
$y->setParent($x->getParent());
397424

398425
if ($x->getParent() === null) {
399-
$this->root = $y; // y becomes root
426+
$this->root = $y;
400427
} elseif ($x === $x->getParent()->getLeft()) {
401428
$x->getParent()->setLeft($y);
402429
} else {
403430
$x->getParent()->setRight($y);
404431
}
405-
$y->setLeft($x); // x becomes left child of y
406-
$x->setParent($y); // and y becomes parent of x
432+
$y->setLeft($x);
433+
$x->setParent($y);
407434

408435
if ($x !== $this->nilNode) {
409436
$x->updateMax();
@@ -415,26 +442,30 @@ private function rotateLeft(Node $x): void
415442
}
416443
}
417444

445+
/**
446+
* @param Node<TPoint, TValue> $y
447+
* @return void
448+
*/
418449
private function rotateRight(Node $y): void
419450
{
420451
$x = $y->getLeft();
421452

422-
$y->setLeft($x->getRight()); // b goes to y.left
453+
$y->setLeft($x->getRight());
423454

424455
if ($x->getRight() !== $this->nilNode) {
425-
$x->getRight()->setParent($y); // y becomes parent of b
456+
$x->getRight()->setParent($y);
426457
}
427-
$x->setParent($y->getParent()); // move parent
458+
$x->setParent($y->getParent());
428459

429-
if ($y->getParent() === null) { // x becomes root
460+
if ($y->getParent() === null) {
430461
$this->root = $x;
431462
} elseif ($y === $y->getParent()->getLeft()) {
432463
$y->getParent()->setLeft($x);
433464
} else {
434465
$y->getParent()->setRight($x);
435466
}
436-
$x->setRight($y); // y becomes right child of x
437-
$y->setParent($x); // and x becomes parent of y
467+
$x->setRight($y);
468+
$y->setParent($x);
438469

439470
if ($y !== $this->nilNode) {
440471
$y->updateMax();
@@ -447,9 +478,9 @@ private function rotateRight(Node $y): void
447478
}
448479

449480
/**
450-
* @return Iterator<Node>
481+
* @return Iterator<Node<TPoint, TValue>>
451482
*/
452-
public function treeWalk(): Iterator
483+
private function treeWalk(): Iterator
453484
{
454485
if ($this->root !== null) {
455486
$stack = [$this->root];

‎src/Node.php

+70-5
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@
44

55
use Danon\IntervalTree\Interval\IntervalInterface;
66

7+
/**
8+
* @template TPoint
9+
* @template TValue
10+
*/
711
final class Node
812
{
913
/**
10-
* @var Node
14+
* @var Node<TPoint, TValue>
1115
*/
1216
private $left;
1317

1418
/**
15-
* @var Node
19+
* @var Node<TPoint, TValue>
1620
*/
1721
private $right;
1822

1923
/**
20-
* @var Node
24+
* @var Node<TPoint, TValue>
2125
*/
2226
private $parent;
2327

@@ -27,19 +31,26 @@ final class Node
2731
private $color;
2832

2933
/**
30-
* @var Pair
34+
* @var Pair<TPoint, TValue>
3135
*/
3236
private $pair;
3337

3438
/**
35-
* @var null|IntervalInterface
39+
* @var null|IntervalInterface<TPoint>
3640
*/
3741
private $max;
3842

3943
private function __construct()
4044
{
4145
}
4246

47+
/**
48+
* @phpstan-ignore-next-line
49+
* @psalm-template TPoint
50+
* @psalm-template TValue
51+
* @param Pair<TPoint, TValue> $pair
52+
* @return static
53+
*/
4354
public static function withPair(Pair $pair): self
4455
{
4556
$self = new self();
@@ -49,6 +60,9 @@ public static function withPair(Pair $pair): self
4960
return $self;
5061
}
5162

63+
/**
64+
* @return Node<TPoint, TValue>
65+
*/
5266
public static function nil(): self
5367
{
5468
$self = new self();
@@ -66,46 +80,78 @@ public function getColor(): NodeColor
6680
return $this->color;
6781
}
6882

83+
/**
84+
* @return Node<TPoint, TValue>
85+
*/
6986
public function getLeft(): Node
7087
{
7188
return $this->left;
7289
}
7390

91+
/**
92+
* @param Node<TPoint, TValue> $node
93+
* @return void
94+
*/
7495
public function setLeft(Node $node): void
7596
{
7697
$this->left = $node;
7798
}
7899

100+
/**
101+
* @return Node<TPoint, TValue>
102+
*/
79103
public function getRight(): Node
80104
{
81105
return $this->right;
82106
}
83107

108+
/**
109+
* @param Node<TPoint, TValue> $node
110+
* @return void
111+
*/
84112
public function setRight(Node $node): void
85113
{
86114
$this->right = $node;
87115
}
88116

117+
/**
118+
* @return Node<TPoint, TValue>|null
119+
*/
89120
public function getParent(): ?Node
90121
{
91122
return $this->parent;
92123
}
93124

125+
/**
126+
* @param Node<TPoint, TValue>|null $node
127+
* @return void
128+
*/
94129
public function setParent(?Node $node): void
95130
{
96131
$this->parent = $node;
97132
}
98133

134+
/**
135+
* @return Pair<TPoint, TValue>
136+
*/
99137
public function getPair(): Pair
100138
{
101139
return $this->pair;
102140
}
103141

142+
/**
143+
* @param Node<TPoint, TValue> $otherNode
144+
* @return bool
145+
*/
104146
public function lessThan(Node $otherNode): bool
105147
{
106148
return $this->getPair()->getInterval()->lessThan($otherNode->getPair()->getInterval());
107149
}
108150

151+
/**
152+
* @param Node<TPoint, TValue> $otherNode
153+
* @return bool
154+
*/
109155
public function equalTo(Node $otherNode): bool
110156
{
111157
$valueEqual = true;
@@ -115,16 +161,27 @@ public function equalTo(Node $otherNode): bool
115161
return $this->getPair()->getInterval()->equalTo($otherNode->getPair()->getInterval()) && $valueEqual;
116162
}
117163

164+
/**
165+
* @param Node<TPoint, TValue> $otherNode
166+
* @return bool
167+
*/
118168
public function intersect(Node $otherNode): bool
119169
{
120170
return $this->getPair()->getInterval()->intersect($otherNode->getPair()->getInterval());
121171
}
122172

173+
/**
174+
* @param Node<TPoint, TValue> $otherNode
175+
* @return void
176+
*/
123177
public function copyPairFrom(Node $otherNode): void
124178
{
125179
$this->pair = $otherNode->getPair();
126180
}
127181

182+
/**
183+
* @return void
184+
*/
128185
public function updateMax(): void
129186
{
130187
$this->max = $this->getPair()->getInterval();
@@ -136,12 +193,20 @@ public function updateMax(): void
136193
}
137194
}
138195

196+
/**
197+
* @param Node<TPoint, TValue> $searchNode
198+
* @return bool
199+
*/
139200
public function notIntersectLeftSubtree(Node $searchNode): bool
140201
{
141202
$high = $this->getLeft()->max->getHigh() ?? $this->getLeft()->getPair()->getInterval()->getHigh();
142203
return $high < $searchNode->getPair()->getInterval()->getLow();
143204
}
144205

206+
/**
207+
* @param Node<TPoint, TValue> $searchNode
208+
* @return bool
209+
*/
145210
public function notIntersectRightSubtree(Node $searchNode): bool
146211
{
147212
$low = $this->getRight()->max->getLow() ?? $this->getRight()->getPair()->getInterval()->getLow();

‎src/Pair.php

+12-5
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,38 @@
66

77
use Danon\IntervalTree\Interval\IntervalInterface;
88

9+
/**
10+
* @template TPoint
11+
* @template TValue
12+
*/
913
final class Pair
1014
{
11-
/** @var IntervalInterface */
15+
/** @var IntervalInterface<TPoint> */
1216
private $interval;
1317

14-
/** @var mixed */
18+
/** @var TValue */
1519
private $value;
1620

1721
/**
18-
* @param IntervalInterface $interval
19-
* @param mixed $value
22+
* @param IntervalInterface<TPoint> $interval
23+
* @param TValue $value
2024
*/
2125
public function __construct(IntervalInterface $interval, $value = null)
2226
{
2327
$this->interval = $interval;
2428
$this->value = $value;
2529
}
2630

31+
/**
32+
* @return IntervalInterface<TPoint>
33+
*/
2734
public function getInterval(): IntervalInterface
2835
{
2936
return $this->interval;
3037
}
3138

3239
/**
33-
* @return mixed
40+
* @return TValue
3441
*/
3542
public function getValue()
3643
{

‎tests/Benchmark/CountIntersectionsBench.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Danon\IntervalTree\Tests\Benchmark;
66

7-
use Danon\IntervalTree\Interval\NumericInterval;
87
use Danon\IntervalTree\IntervalTree;
98
use PhpBench\Benchmark\Metadata\Annotations\Revs;
9+
use Danon\IntervalTree\Interval\IntervalInterface;
1010

1111
/**
1212
* @BeforeMethods({"init"})
@@ -20,12 +20,12 @@ class CountIntersectionsBench
2020
private const MAX_INTERVAL_OFFSET = 100;
2121

2222
/**
23-
* @var IntervalTree
23+
* @var IntervalTree<int|float, null>
2424
*/
2525
private $tree;
2626

2727
/**
28-
* @var NumericInterval[]
28+
* @var IntervalInterface<int|float>[]
2929
*/
3030
private $bruteForceList;
3131

@@ -42,7 +42,7 @@ public function init(): void
4242
}
4343

4444
/**
45-
* @Revs(1000)
45+
* @Revs(10)
4646
*/
4747
public function benchTree(): void
4848
{
@@ -51,7 +51,7 @@ public function benchTree(): void
5151
}
5252

5353
/**
54-
* @Revs(1000)
54+
* @Revs(10)
5555
*/
5656
public function benchBruteForce(): void
5757
{

‎tests/Benchmark/GenerateIntervalTrait.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Danon\IntervalTree\Tests\Benchmark;
66

7+
use Danon\IntervalTree\Interval\IntervalInterface;
78
use Danon\IntervalTree\Interval\NumericInterval;
89
use Exception;
910
use InvalidArgumentException;
@@ -13,16 +14,17 @@ trait GenerateIntervalTrait
1314
/**
1415
* @param int $maxHigh
1516
* @param int $maxOffset
16-
* @return NumericInterval
17+
* @return IntervalInterface<int|float>
1718
*/
18-
private function generateInterval(int $maxHigh, int $maxOffset): NumericInterval
19+
private function generateInterval(int $maxHigh, int $maxOffset): IntervalInterface
1920
{
2021
try {
2122
$low = random_int(0, $maxHigh);
2223
$high = random_int($low, min($low + $maxOffset, $maxHigh));
2324
} catch (Exception $exception) {
2425
throw new InvalidArgumentException('Wrong interval arguments', $exception->getCode(), $exception);
2526
}
26-
return new NumericInterval($low, $high);
27+
28+
return NumericInterval::fromArray([$low, $high]);
2729
}
2830
}

‎tests/Benchmark/HasIntersectionBench.php

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Danon\IntervalTree\Tests\Benchmark;
66

7-
use Danon\IntervalTree\Interval\NumericInterval;
87
use Danon\IntervalTree\IntervalTree;
98
use PhpBench\Benchmark\Metadata\Annotations\Revs;
9+
use Danon\IntervalTree\Interval\IntervalInterface;
1010

1111
/**
1212
* @BeforeMethods({"init"})
@@ -20,12 +20,12 @@ class HasIntersectionBench
2020
private const MAX_INTERVAL_OFFSET = 100;
2121

2222
/**
23-
* @var IntervalTree
23+
* @var IntervalTree<int|float, null>
2424
*/
2525
private $tree;
2626

2727
/**
28-
* @var NumericInterval[]
28+
* @var IntervalInterface<int|float>[]
2929
*/
3030
private $bruteForceList;
3131

@@ -42,7 +42,7 @@ public function init(): void
4242
}
4343

4444
/**
45-
* @Revs(1000)
45+
* @Revs(10)
4646
*/
4747
public function benchTree(): void
4848
{
@@ -51,7 +51,7 @@ public function benchTree(): void
5151
}
5252

5353
/**
54-
* @Revs(1000)
54+
* @Revs(10)
5555
*/
5656
public function benchBruteForce(): void
5757
{

‎tests/Interval/DateTimeIntervalTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace Danon\IntervalTree\Tests\Interval;

‎tests/Interval/NumericIntervalTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
declare(strict_types=1);
34

45
namespace Danon\IntervalTree\Tests\Interval;

‎tests/IntervalTreeTest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ final class IntervalTreeTest extends TestCase
1717
[7, 8], [1, 4], [2, 3], [7, 12], [1, 1], [3, 4], [7, 7], [0, 2], [0, 2], [0, 3], [9, 12]
1818
];
1919

20-
/** @var IntervalTree */
20+
/** @var IntervalTree<int|float, string> */
2121
private $tree;
2222

2323
public function setUp(): void
@@ -42,6 +42,7 @@ public function setUp(): void
4242
public function testFindIntersections(): void
4343
{
4444
$checkInterval = [2, 3];
45+
/** @var array<int[]> $overlappingIntervals */
4546
$overlappingIntervals = [[0, 2], [0, 2], [0, 3], [1, 4], [2, 3], [3, 4]];
4647
$intersections = $this->tree->findIntersections(NumericInterval::fromArray($checkInterval));
4748
foreach ($intersections as $index => $pair) {

‎tests/PairTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ final class PairTest extends TestCase
1515
private const EXAMPLE_VALUE = '1_5';
1616

1717
/**
18-
* @var Pair
18+
* @var Pair<int|float, string>
1919
*/
2020
protected $pair;
2121

0 commit comments

Comments
 (0)
Please sign in to comment.