View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.geometry.euclidean.oned;
18  
19  import org.apache.commons.geometry.core.RegionLocation;
20  import org.apache.commons.geometry.core.partitioning.Split;
21  import org.apache.commons.geometry.core.partitioning.SplitLocation;
22  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
23  import org.apache.commons.numbers.core.Precision;
24  import org.junit.jupiter.api.Assertions;
25  import org.junit.jupiter.api.Test;
26  
27  class IntervalTest {
28  
29      private static final double TEST_EPS = 1e-15;
30  
31      private static final Precision.DoubleEquivalence TEST_PRECISION =
32              Precision.doubleEquivalenceOfEpsilon(TEST_EPS);
33  
34      @Test
35      void testOf_doubles() {
36          // act/assert
37          checkInterval(Interval.of(0, 0, TEST_PRECISION), 0, 0);
38  
39          checkInterval(Interval.of(1, 2, TEST_PRECISION), 1, 2);
40          checkInterval(Interval.of(2, 1, TEST_PRECISION), 1, 2);
41  
42          checkInterval(Interval.of(-2, -1, TEST_PRECISION), -2, -1);
43          checkInterval(Interval.of(-1, -2, TEST_PRECISION), -2, -1);
44  
45          checkInterval(Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION),
46                  1, Double.POSITIVE_INFINITY);
47          checkInterval(Interval.of(Double.POSITIVE_INFINITY, 1, TEST_PRECISION),
48                  1, Double.POSITIVE_INFINITY);
49  
50          checkInterval(Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION),
51                  Double.NEGATIVE_INFINITY, 1);
52          checkInterval(Interval.of(1, Double.NEGATIVE_INFINITY, TEST_PRECISION),
53                  Double.NEGATIVE_INFINITY, 1);
54  
55          checkInterval(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION),
56                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
57  
58          checkInterval(Interval.of(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, TEST_PRECISION),
59                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
60      }
61  
62      @Test
63      void testOf_doubles_invalidIntervals() {
64  
65          // act/assert
66          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(1, Double.NaN, TEST_PRECISION));
67          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Double.NaN, 1, TEST_PRECISION));
68          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Double.NaN, Double.NaN, TEST_PRECISION));
69          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION));
70          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, TEST_PRECISION));
71      }
72  
73      @Test
74      void testOf_points() {
75          // act/assert
76          checkInterval(Interval.of(Vector1D.of(1), Vector1D.of(2), TEST_PRECISION), 1, 2);
77          checkInterval(Interval.of(Vector1D.of(Double.POSITIVE_INFINITY), Vector1D.of(Double.NEGATIVE_INFINITY), TEST_PRECISION),
78                  Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
79      }
80  
81      @Test
82      void testOf_points_invalidIntervals() {
83  
84          // act/assert
85          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Vector1D.of(1), Vector1D.of(Double.NaN), TEST_PRECISION));
86          Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(Vector1D.of(Double.POSITIVE_INFINITY), Vector1D.of(Double.POSITIVE_INFINITY), TEST_PRECISION));
87      }
88  
89      @Test
90      void testOf_hyperplanes() {
91          // act/assert
92          Assertions.assertSame(Interval.full(), Interval.of(null, null));
93  
94          checkInterval(Interval.of(
95                  OrientedPoints.fromLocationAndDirection(1, true, TEST_PRECISION),
96                  OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION)), 1, 1);
97          checkInterval(Interval.of(
98                  OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION),
99                  OrientedPoints.fromLocationAndDirection(1, true, TEST_PRECISION)), 1, 1);
100 
101         checkInterval(Interval.of(
102                 OrientedPoints.fromLocationAndDirection(-2, false, TEST_PRECISION),
103                 OrientedPoints.fromLocationAndDirection(5, true, TEST_PRECISION)), -2, 5);
104         checkInterval(Interval.of(
105                 OrientedPoints.fromLocationAndDirection(5, true, TEST_PRECISION),
106                 OrientedPoints.fromLocationAndDirection(-2, false, TEST_PRECISION)), -2, 5);
107 
108         checkInterval(Interval.of(
109                 null,
110                 OrientedPoints.fromLocationAndDirection(5, true, TEST_PRECISION)), Double.NEGATIVE_INFINITY, 5);
111         checkInterval(Interval.of(
112                 OrientedPoints.fromLocationAndDirection(5, true, TEST_PRECISION),
113                 null), Double.NEGATIVE_INFINITY, 5);
114         checkInterval(Interval.of(
115                 OrientedPoints.fromLocationAndDirection(Double.NEGATIVE_INFINITY, false, TEST_PRECISION),
116                 OrientedPoints.fromLocationAndDirection(5, true, TEST_PRECISION)), Double.NEGATIVE_INFINITY, 5);
117 
118         checkInterval(Interval.of(
119                 null,
120                 OrientedPoints.fromLocationAndDirection(5, false, TEST_PRECISION)), 5, Double.POSITIVE_INFINITY);
121         checkInterval(Interval.of(
122                 OrientedPoints.fromLocationAndDirection(5, false, TEST_PRECISION),
123                 null), 5, Double.POSITIVE_INFINITY);
124         checkInterval(Interval.of(
125                 OrientedPoints.fromLocationAndDirection(Double.POSITIVE_INFINITY, true, TEST_PRECISION),
126                 OrientedPoints.fromLocationAndDirection(5, false, TEST_PRECISION)), 5, Double.POSITIVE_INFINITY);
127     }
128 
129     @Test
130     void testOf_hyperplanes_invalidArgs() {
131         // act/assert
132         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
133                 OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION),
134                 OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION)));
135         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
136                 OrientedPoints.fromLocationAndDirection(2, false, TEST_PRECISION),
137                 OrientedPoints.fromLocationAndDirection(1, true, TEST_PRECISION)));
138         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
139                 OrientedPoints.fromLocationAndDirection(Double.POSITIVE_INFINITY, false, TEST_PRECISION),
140                 OrientedPoints.fromLocationAndDirection(Double.POSITIVE_INFINITY, true, TEST_PRECISION)));
141         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
142                 OrientedPoints.fromLocationAndDirection(Double.NaN, false, TEST_PRECISION),
143                 OrientedPoints.fromLocationAndDirection(1, true, TEST_PRECISION)));
144         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
145                 OrientedPoints.fromLocationAndDirection(1, false, TEST_PRECISION),
146                 OrientedPoints.fromLocationAndDirection(Double.NaN, true, TEST_PRECISION)));
147         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
148                 OrientedPoints.fromLocationAndDirection(Double.NaN, false, TEST_PRECISION),
149                 OrientedPoints.fromLocationAndDirection(Double.NaN, true, TEST_PRECISION)));
150         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.of(
151                 null,
152                 OrientedPoints.fromLocationAndDirection(Double.NaN, true, TEST_PRECISION)));
153     }
154 
155     @Test
156     void testPoint() {
157         // act/assert
158         checkInterval(Interval.point(0, TEST_PRECISION), 0, 0);
159         checkInterval(Interval.point(1, TEST_PRECISION), 1, 1);
160         checkInterval(Interval.point(-1, TEST_PRECISION), -1, -1);
161     }
162 
163     @Test
164     void testPoint_invalidArgs() {
165         // act/assert
166         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.point(Double.NEGATIVE_INFINITY, TEST_PRECISION));
167         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.point(Double.POSITIVE_INFINITY, TEST_PRECISION));
168         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.point(Double.NaN, TEST_PRECISION));
169     }
170 
171     @Test
172     void testMin() {
173         // act/assert
174         checkInterval(Interval.min(Double.NEGATIVE_INFINITY, TEST_PRECISION),
175                 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
176 
177         checkInterval(Interval.min(0, TEST_PRECISION), 0, Double.POSITIVE_INFINITY);
178         checkInterval(Interval.min(1, TEST_PRECISION), 1, Double.POSITIVE_INFINITY);
179         checkInterval(Interval.min(-1, TEST_PRECISION), -1, Double.POSITIVE_INFINITY);
180     }
181 
182     @Test
183     void testMin_invalidArgs() {
184         // act/assert
185         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.min(Double.POSITIVE_INFINITY, TEST_PRECISION));
186         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.min(Double.NaN, TEST_PRECISION));
187     }
188 
189     @Test
190     void testMax() {
191         // act/assert
192         checkInterval(Interval.max(Double.POSITIVE_INFINITY, TEST_PRECISION),
193                 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
194 
195         checkInterval(Interval.max(0, TEST_PRECISION), Double.NEGATIVE_INFINITY, 0);
196         checkInterval(Interval.max(1, TEST_PRECISION), Double.NEGATIVE_INFINITY, 1);
197         checkInterval(Interval.max(-1, TEST_PRECISION), Double.NEGATIVE_INFINITY, -1);
198     }
199 
200     @Test
201     void testMax_invalidArgs() {
202         // act/assert
203         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.max(Double.NEGATIVE_INFINITY, TEST_PRECISION));
204         Assertions.assertThrows(IllegalArgumentException.class, () -> Interval.max(Double.NaN, TEST_PRECISION));
205     }
206 
207     @Test
208     void testIsInfinite() {
209         // act/assert
210         Assertions.assertFalse(Interval.of(1, 2, TEST_PRECISION).isInfinite());
211 
212         Assertions.assertTrue(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION).isInfinite());
213         Assertions.assertTrue(Interval.of(2, Double.POSITIVE_INFINITY, TEST_PRECISION).isInfinite());
214         Assertions.assertTrue(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).isInfinite());
215     }
216 
217     @Test
218     void testIsFinite() {
219         // act/assert
220         Assertions.assertTrue(Interval.of(1, 2, TEST_PRECISION).isFinite());
221 
222         Assertions.assertFalse(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION).isFinite());
223         Assertions.assertFalse(Interval.of(2, Double.POSITIVE_INFINITY, TEST_PRECISION).isFinite());
224         Assertions.assertFalse(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).isFinite());
225     }
226 
227     @Test
228     void testClassify_finite() {
229         // arrange
230         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
231         final Interval interval = Interval.of(-1, 1, precision);
232 
233         // act/assert
234         checkClassify(interval, RegionLocation.OUTSIDE,
235                 Double.NEGATIVE_INFINITY, -2, -1.1,
236                 1.1, 2, Double.POSITIVE_INFINITY);
237 
238         checkClassify(interval, RegionLocation.BOUNDARY,
239                 -1.001, -1, -0.999,
240                 0.999, 1, 1.001);
241 
242         checkClassify(interval, RegionLocation.INSIDE, -0.9, 0, 0.9);
243 
244         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
245     }
246 
247     @Test
248     void testClassify_singlePoint() {
249         // arrange
250         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
251         final Interval interval = Interval.of(1, 1, precision);
252 
253         // act/assert
254         checkClassify(interval, RegionLocation.OUTSIDE,
255                 Double.NEGATIVE_INFINITY, 0, 0.9, 1.1, 2, Double.POSITIVE_INFINITY);
256 
257         checkClassify(interval, RegionLocation.BOUNDARY,
258                 0.999, 1, 1.0001);
259 
260         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
261     }
262 
263     @Test
264     void testClassify_maxInfinite() {
265         // arrange
266         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
267         final Interval interval = Interval.of(-1, Double.POSITIVE_INFINITY, precision);
268 
269         // act/assert
270         checkClassify(interval, RegionLocation.OUTSIDE,
271                 Double.NEGATIVE_INFINITY, -2, -1.1);
272 
273         checkClassify(interval, RegionLocation.BOUNDARY,
274                 -1.001, -1, -0.999);
275 
276         checkClassify(interval, RegionLocation.INSIDE,
277                 -0.9, 0, 1.0, Double.POSITIVE_INFINITY);
278 
279         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
280     }
281 
282     @Test
283     void testClassify_minInfinite() {
284         // arrange
285         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
286         final Interval interval = Interval.of(Double.NEGATIVE_INFINITY, 1, precision);
287 
288         // act/assert
289         checkClassify(interval, RegionLocation.INSIDE,
290                 Double.NEGATIVE_INFINITY, 0, 0.9);
291 
292         checkClassify(interval, RegionLocation.BOUNDARY,
293                 0.999, 1, 1.001);
294 
295         checkClassify(interval, RegionLocation.OUTSIDE,
296                 1.1, 2, Double.POSITIVE_INFINITY);
297 
298         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
299     }
300 
301     @Test
302     void testClassify_minMaxInfinite() {
303         // arrange
304         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
305         final Interval interval = Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, precision);
306 
307         // act/assert
308         checkClassify(interval, RegionLocation.INSIDE,
309                 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
310 
311         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
312     }
313 
314     @Test
315     void testContains_finite() {
316         // arrange
317         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
318         final Interval interval = Interval.of(-1, 1, precision);
319 
320         // act/assert
321         checkContains(interval, true,
322                 -1.001, -1, -0.999,
323                 0.999, 1, 1.001,
324 
325                 -0.9, 0, 0.9);
326 
327         checkContains(interval, false,
328                 Double.NEGATIVE_INFINITY, -2, -1.1,
329                 1.1, 2, Double.POSITIVE_INFINITY);
330 
331         checkContains(interval, false, Double.NaN);
332     }
333 
334     @Test
335     void testIsFull() {
336         // act/assert
337         Assertions.assertFalse(Interval.of(1, 1, TEST_PRECISION).isFull());
338         Assertions.assertFalse(Interval.of(-2, 2, TEST_PRECISION).isFull());
339 
340         Assertions.assertFalse(Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION).isFull());
341         Assertions.assertFalse(Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION).isFull());
342 
343         Assertions.assertTrue(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).isFull());
344     }
345 
346     @Test
347     void testGetSize() {
348         // act/assert
349         Assertions.assertEquals(0, Interval.of(1, 1, TEST_PRECISION).getSize(), TEST_EPS);
350 
351         Assertions.assertEquals(4, Interval.of(-2, 2, TEST_PRECISION).getSize(), TEST_EPS);
352         Assertions.assertEquals(5, Interval.of(2, -3, TEST_PRECISION).getSize(), TEST_EPS);
353 
354         Assertions.assertEquals(Double.POSITIVE_INFINITY,
355                 Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION).getSize(), TEST_EPS);
356         Assertions.assertEquals(Double.POSITIVE_INFINITY,
357                 Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION).getSize(), TEST_EPS);
358 
359         Assertions.assertEquals(Double.POSITIVE_INFINITY,
360                 Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).getSize(), TEST_EPS);
361     }
362 
363     @Test
364     void testGetBoundarySize() {
365         // act/assert
366         Assertions.assertEquals(0, Interval.of(1, 1, TEST_PRECISION).getBoundarySize(), TEST_EPS);
367         Assertions.assertEquals(0, Interval.of(-2, 5, TEST_PRECISION).getBoundarySize(), TEST_EPS);
368         Assertions.assertEquals(0, Interval.full().getBoundarySize(), TEST_EPS);
369     }
370 
371     @Test
372     void testGetCentroid() {
373         // act/assert
374         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.ZERO,
375                 Interval.of(-1, 1, TEST_PRECISION).getCentroid(), TEST_EPS);
376         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(10),
377                 Interval.of(10, 10, TEST_PRECISION).getCentroid(), TEST_EPS);
378 
379         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(2),
380                 Interval.of(1, 3, TEST_PRECISION).getCentroid(), TEST_EPS);
381         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(-1),
382                 Interval.of(-2, 0, TEST_PRECISION).getCentroid(), TEST_EPS);
383 
384         Assertions.assertNull(Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION).getCentroid());
385         Assertions.assertNull(Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION).getCentroid());
386         Assertions.assertNull(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).getCentroid());
387     }
388 
389     @Test
390     void checkToTree_finite() {
391         // arrange
392         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
393         final Interval interval = Interval.of(-1, 1, precision);
394 
395         // act
396         final RegionBSPTree1D tree = interval.toTree();
397 
398         // assert
399         Assertions.assertEquals(5, tree.count());
400 
401         checkClassify(tree, RegionLocation.OUTSIDE,
402                 Double.NEGATIVE_INFINITY, -2, -1.1,
403                 1.1, 2, Double.POSITIVE_INFINITY);
404 
405         checkClassify(tree, RegionLocation.BOUNDARY,
406                 -1.001, -1, -0.999,
407                 0.999, 1, 1.001);
408 
409         checkClassify(tree, RegionLocation.INSIDE, -0.9, 0, 0.9);
410 
411         checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
412     }
413 
414     @Test
415     void checkToTree_singlePoint() {
416         // arrange
417         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
418         final Interval interval = Interval.of(1, 1, precision);
419 
420         // act
421         final RegionBSPTree1D tree = interval.toTree();
422 
423         // assert
424         Assertions.assertEquals(5, tree.count());
425 
426         checkClassify(tree, RegionLocation.OUTSIDE,
427                 Double.NEGATIVE_INFINITY, 0, 0.9, 1.1, 2, Double.POSITIVE_INFINITY);
428 
429         checkClassify(tree, RegionLocation.BOUNDARY,
430                 0.999, 1, 1.0001);
431 
432         checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
433     }
434 
435     @Test
436     void checkToTree_maxInfinite() {
437         // arrange
438         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
439         final Interval interval = Interval.of(-1, Double.POSITIVE_INFINITY, precision);
440 
441         // act
442         final RegionBSPTree1D tree = interval.toTree();
443 
444         // assert
445         Assertions.assertEquals(3, tree.count());
446 
447         checkClassify(tree, RegionLocation.OUTSIDE,
448                 Double.NEGATIVE_INFINITY, -2, -1.1);
449 
450         checkClassify(tree, RegionLocation.BOUNDARY,
451                 -1.001, -1, -0.999);
452 
453         checkClassify(tree, RegionLocation.INSIDE,
454                 -0.9, 0, 1.0, Double.POSITIVE_INFINITY);
455 
456         checkClassify(interval, RegionLocation.OUTSIDE, Double.NaN);
457     }
458 
459     @Test
460     void checkToTree_minInfinite() {
461         // arrange
462         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
463         final Interval interval = Interval.of(Double.NEGATIVE_INFINITY, 1, precision);
464 
465         // act
466         final RegionBSPTree1D tree = interval.toTree();
467 
468         // assert
469         Assertions.assertEquals(3, tree.count());
470 
471         checkClassify(tree, RegionLocation.INSIDE,
472                 Double.NEGATIVE_INFINITY, 0, 0.9);
473 
474         checkClassify(tree, RegionLocation.BOUNDARY,
475                 0.999, 1, 1.001);
476 
477         checkClassify(tree, RegionLocation.OUTSIDE,
478                 1.1, 2, Double.POSITIVE_INFINITY);
479 
480         checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
481     }
482 
483     @Test
484     void checkToTree_minMaxInfinite() {
485         // arrange
486         final Precision.DoubleEquivalence precision = Precision.doubleEquivalenceOfEpsilon(1e-2);
487         final Interval interval = Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, precision);
488 
489         // act
490         final RegionBSPTree1D tree = interval.toTree();
491 
492         // assert
493         Assertions.assertEquals(1, tree.count());
494 
495         checkClassify(tree, RegionLocation.INSIDE,
496                 Double.NEGATIVE_INFINITY, -1, 0, 1, Double.POSITIVE_INFINITY);
497 
498         checkClassify(tree, RegionLocation.OUTSIDE, Double.NaN);
499     }
500 
501     @Test
502     void testProjectToBoundary_full() {
503         // arrange
504         final Interval full = Interval.full();
505 
506 
507         // act/assert
508         Assertions.assertNull(full.project(Vector1D.of(Double.NEGATIVE_INFINITY)));
509         Assertions.assertNull(full.project(Vector1D.of(0)));
510         Assertions.assertNull(full.project(Vector1D.of(Double.POSITIVE_INFINITY)));
511     }
512 
513     @Test
514     void testProjectToBoundary_singlePoint() {
515         // arrange
516         final Interval interval = Interval.point(1, TEST_PRECISION);
517 
518         // act/assert
519         checkBoundaryProjection(interval, -1, 1);
520         checkBoundaryProjection(interval, 0, 1);
521 
522         checkBoundaryProjection(interval, 1, 1);
523 
524         checkBoundaryProjection(interval, 2, 1);
525         checkBoundaryProjection(interval, 3, 1);
526 
527         checkBoundaryProjection(interval, Double.NEGATIVE_INFINITY, 1);
528         checkBoundaryProjection(interval, Double.POSITIVE_INFINITY, 1);
529     }
530 
531     @Test
532     void testProjectToBoundary_closedInterval() {
533         // arrange
534         final Interval interval = Interval.of(1, 3, TEST_PRECISION);
535 
536         // act/assert
537         checkBoundaryProjection(interval, -1, 1);
538         checkBoundaryProjection(interval, 0, 1);
539         checkBoundaryProjection(interval, 1, 1);
540 
541         checkBoundaryProjection(interval, 1.9, 1);
542         checkBoundaryProjection(interval, 2, 1);
543         checkBoundaryProjection(interval, 2.1, 3);
544 
545         checkBoundaryProjection(interval, 3, 3);
546         checkBoundaryProjection(interval, 4, 3);
547         checkBoundaryProjection(interval, 5, 3);
548 
549         checkBoundaryProjection(interval, Double.NEGATIVE_INFINITY, 1);
550         checkBoundaryProjection(interval, Double.POSITIVE_INFINITY, 3);
551     }
552 
553     @Test
554     void testProjectToBoundary_noMinBoundary() {
555         // arrange
556         final Interval interval = Interval.of(Double.NEGATIVE_INFINITY, 1, TEST_PRECISION);
557 
558         // act/assert
559         checkBoundaryProjection(interval, -1, 1);
560         checkBoundaryProjection(interval, 0, 1);
561         checkBoundaryProjection(interval, 1, 1);
562         checkBoundaryProjection(interval, 2, 1);
563         checkBoundaryProjection(interval, 3, 1);
564 
565         checkBoundaryProjection(interval, Double.NEGATIVE_INFINITY, 1);
566         checkBoundaryProjection(interval, Double.POSITIVE_INFINITY, 1);
567     }
568 
569     @Test
570     void testProjectToBoundary_noMaxBoundary() {
571         // arrange
572         final Interval interval = Interval.of(1, Double.POSITIVE_INFINITY, TEST_PRECISION);
573 
574         // act/assert
575         checkBoundaryProjection(interval, -1, 1);
576         checkBoundaryProjection(interval, 0, 1);
577         checkBoundaryProjection(interval, 1, 1);
578         checkBoundaryProjection(interval, 2, 1);
579         checkBoundaryProjection(interval, 3, 1);
580 
581         checkBoundaryProjection(interval, Double.NEGATIVE_INFINITY, 1);
582         checkBoundaryProjection(interval, Double.POSITIVE_INFINITY, 1);
583     }
584 
585     @Test
586     void testTransform() {
587         // arrange
588         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(2);
589 
590         // act/assert
591         checkInterval(Interval.of(-1, 2, TEST_PRECISION).transform(transform), -2, 4);
592 
593         checkInterval(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION).transform(transform),
594                 Double.NEGATIVE_INFINITY, 4);
595 
596         checkInterval(Interval.of(-1, Double.POSITIVE_INFINITY, TEST_PRECISION).transform(transform), -2,
597                 Double.POSITIVE_INFINITY);
598 
599         checkInterval(Interval.of(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, TEST_PRECISION).transform(transform),
600                 Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
601     }
602 
603     @Test
604     void testTransform_reflection() {
605         // arrange
606         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(-1);
607 
608         // act/assert
609         checkInterval(Interval.of(-1, 2, TEST_PRECISION).transform(transform), -2, 1);
610 
611         checkInterval(Interval.of(Double.NEGATIVE_INFINITY, 2, TEST_PRECISION).transform(transform),
612                 -2, Double.POSITIVE_INFINITY);
613 
614         checkInterval(Interval.of(-1, Double.POSITIVE_INFINITY, TEST_PRECISION).transform(transform),
615                 Double.NEGATIVE_INFINITY, 1);
616     }
617 
618     @Test
619     void testSplit_full_positiveFacingSplitter() {
620         // arrange
621         final Interval interval = Interval.full();
622         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
623                 Vector1D.of(1), true, TEST_PRECISION);
624 
625         // act
626         final Split<Interval> split = interval.split(splitter);
627 
628         // assert
629         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
630 
631         checkInterval(split.getMinus(), Double.NEGATIVE_INFINITY, 1);
632         checkInterval(split.getPlus(), 1, Double.POSITIVE_INFINITY);
633     }
634 
635     @Test
636     void testSplit_full_negativeFacingSplitter() {
637         // arrange
638         final Interval interval = Interval.full();
639         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
640                 Vector1D.of(1), true, TEST_PRECISION);
641 
642         // act
643         final Split<Interval> split = interval.split(splitter);
644 
645         // assert
646         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
647 
648         checkInterval(split.getMinus(), Double.NEGATIVE_INFINITY, 1);
649         checkInterval(split.getPlus(), 1, Double.POSITIVE_INFINITY);
650     }
651 
652     @Test
653     void testSplit_halfSpace_positiveFacingSplitter() {
654         // arrange
655         final Interval interval = Interval.min(-1, TEST_PRECISION);
656         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
657                 Vector1D.of(1), false, TEST_PRECISION);
658 
659         // act
660         final Split<Interval> split = interval.split(splitter);
661 
662         // assert
663         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
664 
665         checkInterval(split.getMinus(), 1, Double.POSITIVE_INFINITY);
666         checkInterval(split.getPlus(), -1, 1);
667     }
668 
669 
670     @Test
671     void testSplit_halfSpace_negativeFacingSplitter() {
672         // arrange
673         final Interval interval = Interval.min(-1, TEST_PRECISION);
674         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
675                 Vector1D.of(1), false, TEST_PRECISION);
676 
677         // act
678         final Split<Interval> split = interval.split(splitter);
679 
680         // assert
681         Assertions.assertEquals(SplitLocation.BOTH, split.getLocation());
682 
683         checkInterval(split.getMinus(), 1, Double.POSITIVE_INFINITY);
684         checkInterval(split.getPlus(), -1, 1);
685     }
686 
687     @Test
688     void testSplit_splitterBelowInterval() {
689         // arrange
690         final Interval interval = Interval.of(5, 10, TEST_PRECISION);
691         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
692                 Vector1D.of(1), true, TEST_PRECISION);
693 
694         // act
695         final Split<Interval> split = interval.split(splitter);
696 
697         // assert
698         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
699 
700         Assertions.assertSame(interval, split.getPlus());
701     }
702 
703     @Test
704     void testSplit_splitterOnMinBoundary() {
705         // arrange
706         final Interval interval = Interval.of(5, 10, TEST_PRECISION);
707         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
708                 Vector1D.of(5), false, TEST_PRECISION);
709 
710         // act
711         final Split<Interval> split = interval.split(splitter);
712 
713         // assert
714         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
715 
716         Assertions.assertSame(interval, split.getMinus());
717     }
718 
719     @Test
720     void testSplit_splitterAboveInterval() {
721         // arrange
722         final Interval interval = Interval.of(5, 10, TEST_PRECISION);
723         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
724                 Vector1D.of(11), true, TEST_PRECISION);
725 
726         // act
727         final Split<Interval> split = interval.split(splitter);
728 
729         // assert
730         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
731 
732         Assertions.assertSame(interval, split.getMinus());
733     }
734 
735     @Test
736     void testSplit_splitterOnMaxBoundary() {
737         // arrange
738         final Interval interval = Interval.of(5, 10, TEST_PRECISION);
739         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
740                 Vector1D.of(10), false, TEST_PRECISION);
741 
742         // act
743         final Split<Interval> split = interval.split(splitter);
744 
745         // assert
746         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
747 
748         Assertions.assertSame(interval, split.getPlus());
749     }
750 
751     @Test
752     void testSplit_point_minusOnly() {
753         // arrange
754         final Interval interval = Interval.point(2, TEST_PRECISION);
755         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
756                 Vector1D.of(1), false, TEST_PRECISION);
757 
758         // act
759         final Split<Interval> split = interval.split(splitter);
760 
761         // assert
762         Assertions.assertEquals(SplitLocation.MINUS, split.getLocation());
763 
764         checkInterval(split.getMinus(), 2, 2);
765         Assertions.assertNull(split.getPlus());
766     }
767 
768     @Test
769     void testSplit_point_plusOnly() {
770         // arrange
771         final Interval interval = Interval.point(2, TEST_PRECISION);
772         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
773                 Vector1D.of(1), true, TEST_PRECISION);
774 
775         // act
776         final Split<Interval> split = interval.split(splitter);
777 
778         // assert
779         Assertions.assertEquals(SplitLocation.PLUS, split.getLocation());
780 
781         Assertions.assertNull(split.getMinus());
782         checkInterval(split.getPlus(), 2, 2);
783     }
784 
785     @Test
786     void testSplit_point_onPoint() {
787         // arrange
788         final Interval interval = Interval.point(1, TEST_PRECISION);
789         final OrientedPoint splitter = OrientedPoints.fromPointAndDirection(
790                 Vector1D.of(1), true, TEST_PRECISION);
791 
792         // act
793         final Split<Interval> split = interval.split(splitter);
794 
795         // assert
796         Assertions.assertEquals(SplitLocation.NEITHER, split.getLocation());
797 
798         Assertions.assertNull(split.getMinus());
799         Assertions.assertNull(split.getPlus());
800     }
801 
802     @Test
803     void testToString() {
804         // arrange
805         final Interval interval = Interval.of(2, 1, TEST_PRECISION);
806 
807         // act
808         final String str = interval.toString();
809 
810         // assert
811         Assertions.assertTrue(str.contains("Interval"));
812         Assertions.assertTrue(str.contains("min= 1.0"));
813         Assertions.assertTrue(str.contains("max= 2.0"));
814     }
815 
816     @Test
817     void testFull() {
818         // act
819         final Interval full = Interval.full();
820 
821         // assert
822         Assertions.assertTrue(full.isFull());
823         Assertions.assertFalse(full.isEmpty());
824         Assertions.assertFalse(full.hasMinBoundary());
825         Assertions.assertFalse(full.hasMaxBoundary());
826         Assertions.assertTrue(full.isInfinite());
827 
828         Assertions.assertEquals(RegionLocation.INSIDE, full.classify(Double.NEGATIVE_INFINITY));
829         Assertions.assertEquals(RegionLocation.INSIDE, full.classify(Double.POSITIVE_INFINITY));
830     }
831 
832     private static void checkContains(final Interval interval, final boolean contains, final double... points) {
833         for (final double x : points) {
834             final String msg = "Unexpected contains status for point " + x;
835 
836             Assertions.assertEquals(contains, interval.contains(x), msg);
837             Assertions.assertEquals(contains, interval.contains(Vector1D.of(x)), msg);
838         }
839     }
840 
841     private static void checkClassify(final Interval interval, final RegionLocation loc, final double... points) {
842         for (final double x : points) {
843             final String msg = "Unexpected location for point " + x;
844 
845             Assertions.assertEquals(loc, interval.classify(x), msg);
846             Assertions.assertEquals(loc, interval.classify(Vector1D.of(x)), msg);
847         }
848     }
849 
850     private static void checkClassify(final RegionBSPTree1D tree, final RegionLocation loc, final double... points) {
851         for (final double x : points) {
852             final String msg = "Unexpected location for point " + x;
853 
854             Assertions.assertEquals(loc, tree.classify(x), msg);
855             Assertions.assertEquals(loc, tree.classify(Vector1D.of(x)), msg);
856         }
857     }
858 
859     private static void checkBoundaryProjection(final Interval interval, final double location, final double projectedLocation) {
860         final Vector1D pt = Vector1D.of(location);
861 
862         final Vector1D proj = interval.project(pt);
863 
864         Assertions.assertEquals(projectedLocation, proj.getX(), TEST_EPS);
865     }
866 
867     /** Check that the given interval matches the arguments and is internally consistent.
868      * @param interval
869      * @param min
870      * @param max
871      */
872     private static void checkInterval(final Interval interval, final double min, final double max) {
873         checkInterval(interval, min, max, TEST_PRECISION);
874     }
875 
876     /** Check that the given interval matches the arguments and is internally consistent.
877      * @param interval
878      * @param min
879      * @param max
880      * @param precision
881      */
882     private static void checkInterval(final Interval interval, final double min, final double max, final Precision.DoubleEquivalence precision) {
883         Assertions.assertEquals(min, interval.getMin(), TEST_EPS);
884         Assertions.assertEquals(max, interval.getMax(), TEST_EPS);
885 
886         final boolean finiteMin = Double.isFinite(min);
887         final boolean finiteMax = Double.isFinite(max);
888 
889         Assertions.assertEquals(finiteMin, interval.hasMinBoundary());
890         Assertions.assertEquals(finiteMax, interval.hasMaxBoundary());
891 
892         if (finiteMin) {
893             Assertions.assertEquals(min, interval.getMinBoundary().getLocation(), TEST_EPS);
894         } else {
895             Assertions.assertNull(interval.getMinBoundary());
896         }
897 
898         if (finiteMax) {
899             Assertions.assertEquals(max, interval.getMaxBoundary().getLocation(), TEST_EPS);
900         } else {
901             Assertions.assertNull(interval.getMaxBoundary());
902         }
903 
904         Assertions.assertFalse(interval.isEmpty()); // always false
905     }
906 }