1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* GpsLab component. |
5
|
|
|
* |
6
|
|
|
* @author Peter Gribanov <[email protected]> |
7
|
|
|
* @copyright Copyright (c) 2016, Peter Gribanov |
8
|
|
|
* @license http://opensource.org/licenses/MIT |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace GpsLab\Component\Interval; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* The comparator must be used only in intervals for safe use the data types. |
15
|
|
|
*/ |
16
|
|
|
class IntervalComparator |
17
|
|
|
{ |
18
|
|
|
/** |
19
|
|
|
* @var ComparableIntervalInterface |
20
|
|
|
*/ |
21
|
|
|
private $interval; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* @param ComparableIntervalInterface $interval |
25
|
|
|
*/ |
26
|
63 |
|
public function __construct(ComparableIntervalInterface $interval) |
27
|
|
|
{ |
28
|
63 |
|
$this->interval = $interval; |
29
|
63 |
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Checks if this Interval is equal to the specified interval. |
33
|
|
|
* |
34
|
|
|
* @param ComparableIntervalInterface $interval |
35
|
|
|
* |
36
|
|
|
* @return bool |
37
|
|
|
*/ |
38
|
|
|
public function equal(ComparableIntervalInterface $interval) |
39
|
|
|
{ |
40
|
|
|
return |
41
|
|
|
$this->interval->startPoint()->eq($interval->startPoint()) && |
42
|
|
|
$this->interval->endPoint()->eq($interval->endPoint()) && |
43
|
|
|
$this->interval->type()->equal($interval->type()) |
44
|
|
|
; |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Does this interval contain the specified point. |
49
|
|
|
* |
50
|
|
|
* @param IntervalPointInterface $point |
51
|
|
|
* |
52
|
|
|
* @return bool |
53
|
|
|
*/ |
54
|
7 |
|
public function contains(IntervalPointInterface $point) |
55
|
|
|
{ |
56
|
7 |
|
if ($this->interval->startPoint()->eq($point)) { |
57
|
2 |
|
return !$this->interval->type()->startExcluded(); |
58
|
|
|
} |
59
|
|
|
|
60
|
5 |
|
if ($this->interval->endPoint()->eq($point)) { |
61
|
2 |
|
return !$this->interval->type()->endExcluded(); |
62
|
|
|
} |
63
|
|
|
|
64
|
3 |
|
return $this->interval->startPoint()->lt($point) && $this->interval->endPoint()->gt($point); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Does this interval intersect the specified interval. |
69
|
|
|
* |
70
|
|
|
* @param ComparableIntervalInterface $interval |
71
|
|
|
* |
72
|
|
|
* @return bool |
73
|
|
|
*/ |
74
|
8 |
|
public function intersects(ComparableIntervalInterface $interval) |
75
|
|
|
{ |
76
|
|
|
if ( |
77
|
8 |
|
$this->interval->startPoint()->gt($interval->endPoint()) || |
78
|
8 |
|
$this->interval->endPoint()->lt($interval->startPoint()) |
79
|
|
|
) { |
80
|
2 |
|
return false; |
81
|
|
|
} |
82
|
|
|
|
83
|
6 |
View Code Duplication |
if ($this->interval->startPoint()->eq($interval->endPoint())) { |
|
|
|
|
84
|
3 |
|
return !$this->interval->type()->startExcluded() && !$interval->type()->endExcluded(); |
85
|
|
|
} |
86
|
|
|
|
87
|
3 |
View Code Duplication |
if ($this->interval->endPoint()->eq($interval->startPoint())) { |
|
|
|
|
88
|
3 |
|
return !$this->interval->type()->endExcluded() && !$interval->type()->startExcluded(); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
return true; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* Gets the intersection between this interval and another interval. |
96
|
|
|
* |
97
|
|
|
* @param ComparableIntervalInterface $interval |
98
|
|
|
* |
99
|
|
|
* @return ComparableIntervalInterface|null |
100
|
|
|
*/ |
101
|
12 |
|
public function intersection(ComparableIntervalInterface $interval) |
102
|
|
|
{ |
103
|
|
|
// intervals is not intersect or impossible create interval from one point |
104
|
|
|
if ( |
105
|
12 |
|
$this->interval->startPoint()->gte($interval->endPoint()) || |
106
|
12 |
|
$this->interval->endPoint()->lte($interval->startPoint()) |
107
|
|
|
) { |
108
|
|
|
// ignore closed intervals: |
109
|
|
|
// [a, b] | [b, c] = [b, b] |
110
|
4 |
|
return null; |
111
|
|
|
} |
112
|
|
|
|
113
|
8 |
|
$type = IntervalType::TYPE_CLOSED; |
114
|
|
|
|
115
|
8 |
View Code Duplication |
if ($this->interval->startPoint()->lt($interval->startPoint())) { |
|
|
|
|
116
|
4 |
|
$start = $interval->startPoint(); |
117
|
4 |
|
if ($interval->type()->startExcluded()) { |
118
|
4 |
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
119
|
|
|
} |
120
|
|
|
} else { |
121
|
4 |
|
$start = $this->interval->startPoint(); |
122
|
4 |
|
if ($this->interval->type()->startExcluded()) { |
123
|
2 |
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
124
|
|
|
} |
125
|
|
|
} |
126
|
|
|
|
127
|
8 |
View Code Duplication |
if ($this->interval->endPoint()->gt($interval->endPoint())) { |
|
|
|
|
128
|
4 |
|
$end = $interval->endPoint(); |
129
|
4 |
|
if ($interval->type()->endExcluded()) { |
130
|
4 |
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
131
|
|
|
} |
132
|
|
|
} else { |
133
|
4 |
|
$end = $this->interval->endPoint(); |
134
|
4 |
|
if ($this->interval->type()->endExcluded()) { |
135
|
2 |
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
8 |
|
return $this->interval |
140
|
8 |
|
->withStart($start) |
141
|
8 |
|
->withEnd($end) |
142
|
8 |
|
->withType(IntervalType::create($type)); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
/** |
146
|
|
|
* Gets the covered interval between this Interval and another interval. |
147
|
|
|
* |
148
|
|
|
* @param ComparableIntervalInterface $interval |
149
|
|
|
* |
150
|
|
|
* @return ComparableIntervalInterface |
151
|
|
|
*/ |
152
|
|
|
public function cover(ComparableIntervalInterface $interval) |
153
|
|
|
{ |
154
|
|
|
$type = IntervalType::TYPE_CLOSED; |
155
|
|
|
|
156
|
|
View Code Duplication |
if ($this->interval->startPoint()->lt($interval->startPoint())) { |
|
|
|
|
157
|
|
|
$start = $this->interval->startPoint(); |
158
|
|
|
if ($this->interval->type()->startExcluded()) { |
159
|
|
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
160
|
|
|
} |
161
|
|
|
} else { |
162
|
|
|
$start = $interval->startPoint(); |
163
|
|
|
if ($interval->type()->startExcluded()) { |
164
|
|
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
View Code Duplication |
if ($this->interval->endPoint()->gt($interval->endPoint())) { |
|
|
|
|
169
|
|
|
$end = $this->interval->endPoint(); |
170
|
|
|
if ($this->interval->type()->endExcluded()) { |
171
|
|
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
172
|
|
|
} |
173
|
|
|
} else { |
174
|
|
|
$end = $interval->endPoint(); |
175
|
|
|
if ($interval->type()->endExcluded()) { |
176
|
|
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
return $this->interval |
181
|
|
|
->withStart($start) |
182
|
|
|
->withEnd($end) |
183
|
|
|
->withType(IntervalType::create($type)); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Gets the gap between this interval and another interval. |
188
|
|
|
* |
189
|
|
|
* @param ComparableIntervalInterface $interval |
190
|
|
|
* |
191
|
|
|
* @return ComparableIntervalInterface|null |
192
|
|
|
*/ |
193
|
|
|
public function gap(ComparableIntervalInterface $interval) |
194
|
|
|
{ |
195
|
|
View Code Duplication |
if ($this->interval->startPoint()->gt($interval->endPoint())) { |
|
|
|
|
196
|
|
|
$type = IntervalType::TYPE_CLOSED; |
197
|
|
|
|
198
|
|
|
if (!$interval->type()->endExcluded()) { // invert exclude |
199
|
|
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
if (!$this->interval->type()->startExcluded()) { // invert exclude |
203
|
|
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
return $this->interval |
207
|
|
|
->withStart($interval->endPoint()) |
208
|
|
|
->withEnd($this->interval->startPoint()) |
209
|
|
|
->withType(IntervalType::create($type)); |
210
|
|
|
} |
211
|
|
|
|
212
|
|
View Code Duplication |
if ($interval->startPoint()->gt($this->interval->endPoint())) { |
|
|
|
|
213
|
|
|
$type = IntervalType::TYPE_CLOSED; |
214
|
|
|
|
215
|
|
|
if (!$this->interval->type()->endExcluded()) { // invert exclude |
216
|
|
|
$type |= IntervalType::TYPE_START_EXCLUDED; |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
if (!$interval->type()->startExcluded()) { // invert exclude |
220
|
|
|
$type |= IntervalType::TYPE_END_EXCLUDED; |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
return $this->interval |
224
|
|
|
->withStart($this->interval->endPoint()) |
225
|
|
|
->withEnd($interval->startPoint()) |
226
|
|
|
->withType(IntervalType::create($type)); |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
return null; // no gap |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Does this interval abuts with the interval specified. |
234
|
|
|
* |
235
|
|
|
* @param ComparableIntervalInterface $interval |
236
|
|
|
* |
237
|
|
|
* @return bool |
238
|
|
|
*/ |
239
|
|
|
public function abuts(ComparableIntervalInterface $interval) |
240
|
|
|
{ |
241
|
|
|
return |
242
|
|
|
$interval->endPoint()->eq($this->interval->startPoint()) || |
243
|
|
|
$this->interval->endPoint()->eq($interval->startPoint()) |
244
|
|
|
; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Joins the interval between the adjacent. |
249
|
|
|
* |
250
|
|
|
* @param ComparableIntervalInterface $interval |
251
|
|
|
* |
252
|
|
|
* @return ComparableIntervalInterface|null |
253
|
|
|
*/ |
254
|
|
|
public function join(ComparableIntervalInterface $interval) |
255
|
|
|
{ |
256
|
|
|
if (!$this->abuts($interval)) { |
257
|
|
|
return null; |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
return $this->cover($interval); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Gets the union between this interval and another interval. |
265
|
|
|
* |
266
|
|
|
* @param ComparableIntervalInterface $interval |
267
|
|
|
* |
268
|
|
|
* @return ComparableIntervalInterface|null |
269
|
|
|
*/ |
270
|
|
|
public function union(ComparableIntervalInterface $interval) |
271
|
|
|
{ |
272
|
|
|
if (!$this->intersects($interval)) { |
273
|
|
|
return null; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
return $this->cover($interval); |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* The point is before the interval. |
281
|
|
|
* |
282
|
|
|
* @param IntervalPointInterface $point |
283
|
|
|
* |
284
|
|
|
* @return bool |
285
|
|
|
*/ |
286
|
|
|
public function before(IntervalPointInterface $point) |
287
|
|
|
{ |
288
|
|
|
if ($this->interval->startPoint()->eq($point)) { |
289
|
|
|
return $this->interval->type()->startExcluded(); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
return $this->interval->startPoint()->gt($point); |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* The point is after the interval. |
297
|
|
|
* |
298
|
|
|
* @param IntervalPointInterface $point |
299
|
|
|
* |
300
|
|
|
* @return bool |
301
|
|
|
*/ |
302
|
|
|
public function after(IntervalPointInterface $point) |
303
|
|
|
{ |
304
|
|
|
if ($this->interval->endPoint()->eq($point)) { |
305
|
|
|
return $this->interval->type()->endExcluded(); |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
return $this->interval->endPoint()->lt($point); |
309
|
|
|
} |
310
|
|
|
} |
311
|
|
|
|
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.