Skip to content

Commit 714e942

Browse files
committed
Add different types of intervals: integer and datetime
1 parent 97c24de commit 714e942

13 files changed

+452
-97
lines changed

src/Interval/DateTimeInterval.php

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Danon\IntervalTree\Interval;
5+
6+
use DateTimeInterface;
7+
use InvalidArgumentException;
8+
use function count;
9+
10+
final class DateTimeInterval implements IntervalInterface
11+
{
12+
/**
13+
* @var DateTimeInterface
14+
*/
15+
private $low;
16+
17+
/**
18+
* @var DateTimeInterface
19+
*/
20+
private $high;
21+
22+
/**
23+
* DateTimeInterval constructor
24+
* @param DateTimeInterface $low
25+
* @param DateTimeInterface $high
26+
*/
27+
public function __construct($low, $high)
28+
{
29+
if ($low > $high) {
30+
throw new InvalidArgumentException('Low interval cannot be greater than high');
31+
}
32+
33+
$this->low = $low;
34+
$this->high = $high;
35+
}
36+
37+
/**
38+
* @param DateTimeInterface[] $interval
39+
* @return DateTimeInterval
40+
*/
41+
public static function fromArray($interval): DateTimeInterval
42+
{
43+
if (count($interval) !== 2) {
44+
throw new InvalidArgumentException('Wrong interval array');
45+
}
46+
return new self($interval[0], $interval[1]);
47+
}
48+
49+
public function getLow(): DateTimeInterface
50+
{
51+
return $this->low;
52+
}
53+
54+
public function getHigh(): DateTimeInterface
55+
{
56+
return $this->high;
57+
}
58+
59+
/**
60+
* @param DateTimeInterval $otherInterval
61+
* @return bool
62+
*/
63+
public function equalTo($otherInterval): bool
64+
{
65+
return $this->getLow()->getTimestamp() === $otherInterval->getLow()->getTimestamp() &&
66+
$this->getHigh()->getTimestamp() === $otherInterval->getHigh()->getTimestamp();
67+
}
68+
69+
/**
70+
* @param DateTimeInterval $otherInterval
71+
* @return bool
72+
*/
73+
public function lessThan($otherInterval): bool
74+
{
75+
return $this->getLow()->getTimestamp() < $otherInterval->getLow()->getTimestamp() ||
76+
(
77+
$this->getLow()->getTimestamp() === $otherInterval->getLow()->getTimestamp() &&
78+
$this->getHigh()->getTimestamp() < $otherInterval->getHigh()->getTimestamp()
79+
);
80+
}
81+
82+
/**
83+
* @param DateTimeInterval $otherInterval
84+
* @return bool
85+
*/
86+
public function intersect($otherInterval): bool
87+
{
88+
return !($this->getHigh() < $otherInterval->getLow() || $otherInterval->getHigh() < $this->getLow());
89+
}
90+
91+
/**
92+
* @param DateTimeInterval $otherInterval
93+
* @return DateTimeInterval
94+
*/
95+
public function merge($otherInterval): DateTimeInterval
96+
{
97+
return new DateTimeInterval(
98+
min($this->getLow(), $otherInterval->getLow()),
99+
max($this->getHigh(), $otherInterval->getHigh())
100+
);
101+
}
102+
}
Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<?php
22
declare(strict_types=1);
33

4-
namespace Danon\IntervalTree;
4+
namespace Danon\IntervalTree\Interval;
55

66
use InvalidArgumentException;
7+
use function count;
78

8-
final class Interval
9+
final class IntegerInterval implements IntervalInterface
910
{
1011
/**
1112
* @var int
@@ -17,7 +18,12 @@ final class Interval
1718
*/
1819
private $high;
1920

20-
public function __construct(int $low, int $high)
21+
/**
22+
* IntegerInterval constructor
23+
* @param int $low
24+
* @param int $high
25+
*/
26+
public function __construct($low, $high)
2127
{
2228
if ($low > $high) {
2329
throw new InvalidArgumentException('Low interval cannot be greater than high');
@@ -29,9 +35,9 @@ public function __construct(int $low, int $high)
2935

3036
/**
3137
* @param int[] $interval
32-
* @return Interval
38+
* @return IntegerInterval
3339
*/
34-
public static function fromArray(array $interval): self
40+
public static function fromArray($interval): IntegerInterval
3541
{
3642
if (count($interval) !== 2) {
3743
throw new InvalidArgumentException('Wrong interval array');
@@ -49,30 +55,41 @@ public function getHigh(): int
4955
return $this->high;
5056
}
5157

52-
public function lessThan(Interval $otherInterval): bool
53-
{
54-
return $this->getLow() < $otherInterval->getLow() ||
55-
($this->getLow() === $otherInterval->getLow() && $this->getHigh() < $otherInterval->getHigh());
56-
}
57-
58-
public function equalTo(Interval $otherInterval): bool
58+
/**
59+
* @param IntegerInterval $otherInterval
60+
* @return bool
61+
*/
62+
public function equalTo($otherInterval): bool
5963
{
6064
return $this->getLow() === $otherInterval->getLow() && $this->getHigh() === $otherInterval->getHigh();
6165
}
6266

63-
public function intersect(Interval $otherInterval): bool
67+
/**
68+
* @param IntegerInterval $otherInterval
69+
* @return bool
70+
*/
71+
public function lessThan($otherInterval): bool
6472
{
65-
return !$this->notIntersect($otherInterval);
73+
return $this->getLow() < $otherInterval->getLow() ||
74+
($this->getLow() === $otherInterval->getLow() && $this->getHigh() < $otherInterval->getHigh());
6675
}
6776

68-
public function notIntersect(Interval $otherInterval): bool
77+
/**
78+
* @param IntegerInterval $otherInterval
79+
* @return bool
80+
*/
81+
public function intersect($otherInterval): bool
6982
{
70-
return ($this->getHigh() < $otherInterval->getLow() || $otherInterval->getHigh() < $this->getLow());
83+
return !($this->getHigh() < $otherInterval->getLow() || $otherInterval->getHigh() < $this->getLow());
7184
}
7285

73-
public function merge(Interval $otherInterval): Interval
86+
/**
87+
* @param IntegerInterval $otherInterval
88+
* @return IntegerInterval
89+
*/
90+
public function merge($otherInterval): IntegerInterval
7491
{
75-
return new Interval(
92+
return new IntegerInterval(
7693
min($this->getLow(), $otherInterval->getLow()),
7794
max($this->getHigh(), $otherInterval->getHigh())
7895
);

src/Interval/IntervalInterface.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Danon\IntervalTree\Interval;
5+
6+
interface IntervalInterface
7+
{
8+
// @phpstan-ignore-next-line
9+
public function __construct($low, $high);
10+
11+
// @phpstan-ignore-next-line
12+
public static function fromArray($interval);
13+
14+
// @phpstan-ignore-next-line
15+
public function getLow();
16+
17+
// @phpstan-ignore-next-line
18+
public function getHigh();
19+
20+
// @phpstan-ignore-next-line
21+
public function equalTo($otherInterval): bool;
22+
23+
// @phpstan-ignore-next-line
24+
public function lessThan($otherInterval): bool;
25+
26+
// @phpstan-ignore-next-line
27+
public function intersect($otherInterval): bool;
28+
29+
// @phpstan-ignore-next-line
30+
public function merge($otherInterval);
31+
}

src/IntervalTree.php

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace Danon\IntervalTree;
55

6+
use Danon\IntervalTree\Interval\IntervalInterface;
67
use Iterator;
78

89
final class IntervalTree
@@ -43,10 +44,10 @@ public function isEmpty(): bool
4344

4445
/**
4546
* Find nodes which intervals intersect with given interval
46-
* @param Interval $interval
47+
* @param IntervalInterface $interval
4748
* @return Iterator<Node>
4849
*/
49-
public function findIntersections(Interval $interval): Iterator
50+
public function findIntersections(IntervalInterface $interval): Iterator
5051
{
5152
$searchNode = Node::withPair(new Pair($interval));
5253
foreach ($this->treeSearchInterval($searchNode) as $node) {
@@ -57,10 +58,10 @@ public function findIntersections(Interval $interval): Iterator
5758
/**
5859
* Check that interval has intersections
5960
*
60-
* @param Interval $interval
61+
* @param IntervalInterface $interval
6162
* @return bool
6263
*/
63-
public function hasIntersection(Interval $interval): bool
64+
public function hasIntersection(IntervalInterface $interval): bool
6465
{
6566
$nodes = $this->findIntersections($interval);
6667
return $nodes->current() !== null;
@@ -69,10 +70,10 @@ public function hasIntersection(Interval $interval): bool
6970
/**
7071
* Count intervals that has intersections
7172
*
72-
* @param Interval $interval
73+
* @param IntervalInterface $interval
7374
* @return int
7475
*/
75-
public function countIntersections(Interval $interval): int
76+
public function countIntersections(IntervalInterface $interval): int
7677
{
7778
$nodes = $this->findIntersections($interval);
7879
return iterator_count($nodes);
@@ -81,11 +82,11 @@ public function countIntersections(Interval $interval): int
8182
/**
8283
* Insert new pair (interval + value) into interval tree
8384
*
84-
* @param Interval $interval
85+
* @param IntervalInterface $interval
8586
* @param mixed $value
8687
* @return Node
8788
*/
88-
public function insert(Interval $interval, $value = null): Node
89+
public function insert(IntervalInterface $interval, $value = null): Node
8990
{
9091
$insertNode = Node::withPair(new Pair($interval, $value));
9192
$insertNode->setLeft($this->nilNode);
@@ -101,22 +102,22 @@ public function insert(Interval $interval, $value = null): Node
101102

102103
/**
103104
* Returns true if interval and value exist in the tree
104-
* @param Interval $interval
105+
* @param IntervalInterface $interval
105106
* @param mixed $value
106107
* @return bool
107108
*/
108-
public function exist(Interval $interval, $value): bool
109+
public function exist(IntervalInterface $interval, $value): bool
109110
{
110111
$searchNode = Node::withPair(new Pair($interval, $value));
111112
return $this->treeSearch($this->root, $searchNode) !== null;
112113
}
113114

114115
/**
115-
* @param Interval $interval
116+
* @param IntervalInterface $interval
116117
* @param mixed $value
117118
* @return bool
118119
*/
119-
public function remove(Interval $interval, $value): bool
120+
public function remove(IntervalInterface $interval, $value): bool
120121
{
121122
$searchNode = Node::withPair(new Pair($interval, $value));
122123
$deleteNode = $this->treeSearch($this->root, $searchNode);

src/Node.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Danon\IntervalTree;
44

5+
use Danon\IntervalTree\Interval\IntervalInterface;
6+
57
class Node
68
{
79
/**
@@ -30,7 +32,7 @@ class Node
3032
private $pair;
3133

3234
/**
33-
* @var null|Interval
35+
* @var null|IntervalInterface
3436
*/
3537
private $max;
3638

src/Pair.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,27 @@
33

44
namespace Danon\IntervalTree;
55

6+
use Danon\IntervalTree\Interval\IntervalInterface;
7+
68
final class Pair
79
{
8-
/** @var Interval */
10+
/** @var IntervalInterface */
911
private $interval;
1012

1113
/** @var mixed */
1214
private $value;
1315

1416
/**
15-
* @param Interval $interval
17+
* @param IntervalInterface $interval
1618
* @param mixed $value
1719
*/
18-
public function __construct(Interval $interval, $value = null)
20+
public function __construct(IntervalInterface $interval, $value = null)
1921
{
2022
$this->interval = $interval;
2123
$this->value = $value;
2224
}
2325

24-
public function getInterval(): Interval
26+
public function getInterval(): IntervalInterface
2527
{
2628
return $this->interval;
2729
}

tests/Benchmark/CountIntersectionsBench.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
namespace Danon\IntervalTree\Tests\Benchmark;
55

6-
use Danon\IntervalTree\Interval;
6+
use Danon\IntervalTree\Interval\IntegerInterval;
77
use Danon\IntervalTree\IntervalTree;
88
use PhpBench\Benchmark\Metadata\Annotations\Revs;
99

@@ -24,7 +24,7 @@ class CountIntersectionsBench
2424
private $tree;
2525

2626
/**
27-
* @var Interval[]
27+
* @var IntegerInterval[]
2828
*/
2929
private $bruteForceList;
3030

0 commit comments

Comments
 (0)