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 java.util.function.UnaryOperator;
20  
21  import org.apache.commons.geometry.core.GeometryTestUtils;
22  import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
23  import org.apache.commons.numbers.angle.PlaneAngleRadians;
24  import org.junit.Assert;
25  import org.junit.Test;
26  
27  public class AffineTransformMatrix1DTest {
28  
29      private static final double EPS = 1e-12;
30  
31      @Test
32      public void testOf() {
33          // act
34          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.of(1, 2);
35  
36          // assert
37          Assert.assertTrue(transform.preservesOrientation());
38  
39          final double[] result = transform.toArray();
40          Assert.assertArrayEquals(new double[] {1, 2}, result, 0.0);
41      }
42  
43      @Test
44      public void testOf_invalidDimensions() {
45          // act/assert
46          GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.of(1),
47                  IllegalArgumentException.class, "Dimension mismatch: 1 != 2");
48      }
49  
50      @Test
51      public void testFrom() {
52          // act/assert
53          Assert.assertArrayEquals(new double[] {1, 0},
54                  AffineTransformMatrix1D.from(UnaryOperator.identity()).toArray(), EPS);
55          Assert.assertArrayEquals(new double[] {1, 2},
56                  AffineTransformMatrix1D.from(v -> v.add(Vector1D.of(2))).toArray(), EPS);
57          Assert.assertArrayEquals(new double[] {3, 0},
58                  AffineTransformMatrix1D.from(v -> v.multiply(3)).toArray(), EPS);
59          Assert.assertArrayEquals(new double[] {3, 6},
60                  AffineTransformMatrix1D.from(v -> v.add(Vector1D.of(2)).multiply(3)).toArray(), EPS);
61      }
62  
63      @Test
64      public void testFrom_invalidFunction() {
65          // act/assert
66          GeometryTestUtils.assertThrows(() -> {
67              AffineTransformMatrix1D.from(v -> v.multiply(0));
68          }, IllegalArgumentException.class);
69      }
70  
71      @Test
72      public void testIdentity() {
73          // act
74          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
75  
76          // assert
77          Assert.assertTrue(transform.preservesOrientation());
78  
79          final double[] expected = {1, 0};
80          Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
81      }
82  
83      @Test
84      public void testCreateTranslation_value() {
85          // act
86          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(2);
87  
88          // assert
89          Assert.assertTrue(transform.preservesOrientation());
90  
91          final double[] expected = {1, 2};
92          Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
93      }
94  
95      @Test
96      public void testCreateTranslation_vector() {
97          // act
98          final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(Vector1D.of(5));
99  
100         // assert
101         Assert.assertTrue(transform.preservesOrientation());
102 
103         final double[] expected = {1, 5};
104         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
105     }
106 
107     @Test
108     public void testTranslate_value() {
109         // arrange
110         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
111 
112         // act
113         final AffineTransformMatrix1D result = a.translate(4);
114 
115         // assert
116         Assert.assertTrue(result.preservesOrientation());
117 
118         final double[] expected = {2, 14};
119         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
120     }
121 
122     @Test
123     public void testTranslate_vector() {
124         // arrange
125         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
126 
127         // act
128         final AffineTransformMatrix1D result = a.translate(Vector1D.of(7));
129 
130         // assert
131         Assert.assertTrue(result.preservesOrientation());
132 
133         final double[] expected = {2, 17};
134         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
135     }
136 
137     @Test
138     public void testCreateScale_vector() {
139         // act
140         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(Vector1D.of(4));
141 
142         // assert
143         Assert.assertTrue(transform.preservesOrientation());
144 
145         final double[] expected = {4, 0};
146         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
147     }
148 
149     @Test
150     public void testCreateScale_value() {
151         // act
152         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(7);
153 
154         // assert
155         Assert.assertTrue(transform.preservesOrientation());
156 
157         final double[] expected = {7, 0};
158         Assert.assertArrayEquals(expected, transform.toArray(), 0.0);
159     }
160 
161     @Test
162     public void testScale_value() {
163         // arrange
164         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
165 
166         // act
167         final AffineTransformMatrix1D result = a.scale(4);
168 
169         // assert
170         Assert.assertTrue(result.preservesOrientation());
171 
172         final double[] expected = {8, 40};
173         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
174     }
175 
176     @Test
177     public void testScale_vector() {
178         // arrange
179         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 10);
180 
181         // act
182         final AffineTransformMatrix1D result = a.scale(Vector1D.of(7));
183 
184         // assert
185         Assert.assertTrue(result.preservesOrientation());
186 
187         final double[] expected = {14, 70};
188         Assert.assertArrayEquals(expected, result.toArray(), 0.0);
189     }
190 
191     @Test
192     public void testApply_identity() {
193         // arrange
194         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
195 
196         // act/assert
197         runWithCoordinates(x -> {
198             final Vector1D v = Vector1D.of(x);
199 
200             EuclideanTestUtils.assertCoordinatesEqual(v, transform.apply(v), EPS);
201         });
202     }
203 
204     @Test
205     public void testApply_translate() {
206         // arrange
207         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
208 
209         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
210                 .translate(translation);
211 
212         // act/assert
213         runWithCoordinates(x -> {
214             final Vector1D vec = Vector1D.of(x);
215 
216             final Vector1D expectedVec = vec.add(translation);
217 
218             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
219         });
220     }
221 
222     @Test
223     public void testApply_scale() {
224         // arrange
225         final Vector1D factor = Vector1D.of(2.0);
226 
227         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
228                 .scale(factor);
229 
230         // act/assert
231         runWithCoordinates(x -> {
232             final Vector1D vec = Vector1D.of(x);
233 
234             final Vector1D expectedVec = Vector1D.of(factor.getX() * x);
235 
236             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
237         });
238     }
239 
240     @Test
241     public void testApply_translateThenScale() {
242         // arrange
243         final Vector1D translation = Vector1D.of(-2.0);
244         final Vector1D scale = Vector1D.of(5.0);
245 
246         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
247                 .translate(translation)
248                 .scale(scale);
249 
250         // act/assert
251         EuclideanTestUtils.assertCoordinatesEqual(Vector1D.of(-5), transform.apply(Vector1D.of(1)), EPS);
252 
253         runWithCoordinates(x -> {
254             final Vector1D vec = Vector1D.of(x);
255 
256             final Vector1D expectedVec = Vector1D.of(
257                         (x + translation.getX()) * scale.getX()
258                     );
259 
260             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
261         });
262     }
263 
264     @Test
265     public void testApply_scaleThenTranslate() {
266         // arrange
267         final Vector1D scale = Vector1D.of(5.0);
268         final Vector1D translation = Vector1D.of(-2.0);
269 
270         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
271                 .scale(scale)
272                 .translate(translation);
273 
274         // act/assert
275         runWithCoordinates(x -> {
276             final Vector1D vec = Vector1D.of(x);
277 
278             final Vector1D expectedVec = Vector1D.of(
279                         (x * scale.getX()) + translation.getX()
280                     );
281 
282             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
283         });
284     }
285 
286     @Test
287     public void testApplyVector_identity() {
288         // arrange
289         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
290 
291         // act/assert
292         runWithCoordinates(x -> {
293             final Vector1D v = Vector1D.of(x);
294 
295             EuclideanTestUtils.assertCoordinatesEqual(v, transform.applyVector(v), EPS);
296         });
297     }
298 
299     @Test
300     public void testApplyVector_translate() {
301         // arrange
302         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
303 
304         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
305                 .translate(translation);
306 
307         // act/assert
308         runWithCoordinates(x -> {
309             final Vector1D vec = Vector1D.of(x);
310 
311             EuclideanTestUtils.assertCoordinatesEqual(vec, transform.applyVector(vec), EPS);
312         });
313     }
314 
315     @Test
316     public void testApplyVector_scale() {
317         // arrange
318         final Vector1D factor = Vector1D.of(2.0);
319 
320         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
321                 .scale(factor);
322 
323         // act/assert
324         runWithCoordinates(x -> {
325             final Vector1D vec = Vector1D.of(x);
326 
327             final Vector1D expectedVec = Vector1D.of(factor.getX() * x);
328 
329             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyVector(vec), EPS);
330         });
331     }
332 
333     @Test
334     public void testApplyVector_representsDisplacement() {
335         // arrange
336         final Vector1D p1 = Vector1D.of(PlaneAngleRadians.PI);
337 
338         final Vector1D translation = Vector1D.of(-2.0);
339         final Vector1D scale = Vector1D.of(5.0);
340 
341         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
342                 .translate(translation)
343                 .scale(scale);
344 
345         // act/assert
346         runWithCoordinates(x -> {
347             final Vector1D p2 = Vector1D.of(x);
348             final Vector1D input = p1.subtract(p2);
349 
350             final Vector1D expectedVec = transform.apply(p1).subtract(transform.apply(p2));
351 
352             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyVector(input), EPS);
353         });
354     }
355 
356     @Test
357     public void testApplyDirection_identity() {
358         // arrange
359         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity();
360 
361         // act/assert
362         runWithCoordinates(x -> {
363             final Vector1D v = Vector1D.of(x);
364 
365             EuclideanTestUtils.assertCoordinatesEqual(v.normalize(), transform.applyDirection(v), EPS);
366         }, true);
367     }
368 
369     @Test
370     public void testApplyDirection_translate() {
371         // arrange
372         final Vector1D translation = Vector1D.of(-PlaneAngleRadians.PI);
373 
374         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
375                 .translate(translation);
376 
377         // act/assert
378         runWithCoordinates(x -> {
379             final Vector1D vec = Vector1D.of(x);
380 
381             EuclideanTestUtils.assertCoordinatesEqual(vec.normalize(), transform.applyDirection(vec), EPS);
382         }, true);
383     }
384 
385     @Test
386     public void testApplyDirection_scale() {
387         // arrange
388         final Vector1D factor = Vector1D.of(2.0);
389 
390         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
391                 .scale(factor);
392 
393         // act/assert
394         runWithCoordinates(x -> {
395             final Vector1D vec = Vector1D.of(x);
396 
397             final Vector1D expectedVec = Vector1D.of(factor.getX() * x).normalize();
398 
399             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyDirection(vec), EPS);
400         }, true);
401     }
402 
403     @Test
404     public void testApplyDirection_representsNormalizedDisplacement() {
405         // arrange
406         final Vector1D p1 = Vector1D.of(PlaneAngleRadians.PI);
407 
408         final Vector1D translation = Vector1D.of(-2.0);
409         final Vector1D scale = Vector1D.of(5.0);
410 
411         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.identity()
412                 .translate(translation)
413                 .scale(scale);
414 
415         // act/assert
416         runWithCoordinates(x -> {
417             final Vector1D p2 = Vector1D.of(x);
418             final Vector1D input = p1.subtract(p2);
419 
420             final Vector1D expectedVec = transform.apply(p1).subtract(transform.apply(p2)).normalize();
421 
422             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.applyDirection(input), EPS);
423         });
424     }
425 
426     @Test
427     public void testApplyDirection_illegalNorm() {
428         // act/assert
429         GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.createScale(0).applyDirection(Vector1D.Unit.PLUS),
430                 IllegalArgumentException.class);
431         GeometryTestUtils.assertThrows(() -> AffineTransformMatrix1D.createScale(2).applyDirection(Vector1D.ZERO),
432                 IllegalArgumentException.class);
433     }
434 
435     @Test
436     public void testDeterminant() {
437         // act/assert
438         Assert.assertEquals(0.0, AffineTransformMatrix1D.of(0, 1).determinant(), EPS);
439         Assert.assertEquals(1.0, AffineTransformMatrix1D.of(1, 0).determinant(), EPS);
440         Assert.assertEquals(-1.0, AffineTransformMatrix1D.of(-1, 2).determinant(), EPS);
441     }
442 
443     @Test
444     public void testPreservesOrientation() {
445         // act/assert
446         Assert.assertFalse(AffineTransformMatrix1D.of(0, 1).preservesOrientation());
447         Assert.assertTrue(AffineTransformMatrix1D.of(1, 0).preservesOrientation());
448         Assert.assertFalse(AffineTransformMatrix1D.of(-1, 2).preservesOrientation());
449     }
450 
451     @Test
452     public void testMultiply() {
453         // arrange
454         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 3);
455         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(13, 14);
456 
457         // act
458         final AffineTransformMatrix1D result = a.multiply(b);
459 
460         // assert
461         final double[] arr = result.toArray();
462         Assert.assertArrayEquals(new double[] {26, 31}, arr, EPS);
463     }
464 
465     @Test
466     public void testMultiply_combinesTransformOperations() {
467         // arrange
468         final Vector1D translation1 = Vector1D.of(1);
469         final double scale = 2.0;
470         final Vector1D translation2 = Vector1D.of(4);
471 
472         final AffineTransformMatrix1D a = AffineTransformMatrix1D.createTranslation(translation1);
473         final AffineTransformMatrix1D b = AffineTransformMatrix1D.createScale(scale);
474         final AffineTransformMatrix1D c = AffineTransformMatrix1D.identity();
475         final AffineTransformMatrix1D d = AffineTransformMatrix1D.createTranslation(translation2);
476 
477         // act
478         final AffineTransformMatrix1D transform = d.multiply(c).multiply(b).multiply(a);
479 
480         // assert
481         runWithCoordinates(x -> {
482             final Vector1D vec = Vector1D.of(x);
483 
484             final Vector1D expectedVec = vec
485                     .add(translation1)
486                     .multiply(scale)
487                     .add(translation2);
488 
489             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
490         });
491     }
492 
493     @Test
494     public void testPremultiply() {
495         // arrange
496         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(2, 3);
497         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(13, 14);
498 
499         // act
500         final AffineTransformMatrix1D result = b.premultiply(a);
501 
502         // assert
503         final double[] arr = result.toArray();
504         Assert.assertArrayEquals(new double[] {26, 31}, arr, EPS);
505     }
506 
507     @Test
508     public void testPremultiply_combinesTransformOperations() {
509         // arrange
510         final Vector1D translation1 = Vector1D.of(1);
511         final double scale = 2.0;
512         final Vector1D translation2 = Vector1D.of(4);
513 
514         final AffineTransformMatrix1D a = AffineTransformMatrix1D.createTranslation(translation1);
515         final AffineTransformMatrix1D b = AffineTransformMatrix1D.createScale(scale);
516         final AffineTransformMatrix1D c = AffineTransformMatrix1D.identity();
517         final AffineTransformMatrix1D d = AffineTransformMatrix1D.createTranslation(translation2);
518 
519         // act
520         final AffineTransformMatrix1D transform = a.premultiply(b).premultiply(c).premultiply(d);
521 
522         // assert
523         runWithCoordinates(x -> {
524             final Vector1D vec = Vector1D.of(x);
525 
526             final Vector1D expectedVec = vec
527                     .add(translation1)
528                     .multiply(scale)
529                     .add(translation2);
530 
531             EuclideanTestUtils.assertCoordinatesEqual(expectedVec, transform.apply(vec), EPS);
532         });
533     }
534 
535     @Test
536     public void testLinear() {
537         // arrange
538         final AffineTransformMatrix1D mat = AffineTransformMatrix1D.of(2, 3);
539 
540         // act
541         final AffineTransformMatrix1D result = mat.linear();
542 
543         // assert
544         Assert.assertArrayEquals(new double[] {2, 0}, result.toArray(), 0.0);
545     }
546 
547     @Test
548     public void testLinearTranspose() {
549         // arrange
550         final AffineTransformMatrix1D mat = AffineTransformMatrix1D.of(2, 3);
551 
552         // act
553         final AffineTransformMatrix1D result = mat.linearTranspose();
554 
555         // assert
556         Assert.assertArrayEquals(new double[] {2, 0}, result.toArray(), 0.0);
557     }
558 
559     @Test
560     public void testNormalTransform() {
561         // act/assert
562         checkNormalTransform(AffineTransformMatrix1D.identity());
563 
564         checkNormalTransform(AffineTransformMatrix1D.createTranslation(4));
565         checkNormalTransform(AffineTransformMatrix1D.createTranslation(-4));
566 
567         checkNormalTransform(AffineTransformMatrix1D.createScale(2));
568         checkNormalTransform(AffineTransformMatrix1D.createScale(-2));
569 
570         checkNormalTransform(AffineTransformMatrix1D.createScale(2).translate(3));
571         checkNormalTransform(AffineTransformMatrix1D.createScale(2).translate(-3));
572         checkNormalTransform(AffineTransformMatrix1D.createTranslation(2).scale(-3));
573         checkNormalTransform(AffineTransformMatrix1D.createTranslation(-4).scale(-1));
574     }
575 
576     private void checkNormalTransform(final AffineTransformMatrix1D transform) {
577         final AffineTransformMatrix1D normalTransform = transform.normalTransform();
578 
579         final Vector1D expectedPlus = transform.apply(Vector1D.Unit.PLUS)
580                 .subtract(transform.apply(Vector1D.ZERO))
581                 .normalize();
582 
583         final Vector1D expectedMinus = transform.apply(Vector1D.Unit.MINUS)
584                 .subtract(transform.apply(Vector1D.ZERO))
585                 .normalize();
586 
587         EuclideanTestUtils.assertCoordinatesEqual(expectedPlus,
588                 normalTransform.apply(Vector1D.Unit.PLUS).normalize(), EPS);
589         EuclideanTestUtils.assertCoordinatesEqual(expectedMinus,
590                 normalTransform.apply(Vector1D.Unit.MINUS).normalize(), EPS);
591     }
592 
593     @Test
594     public void testNormalTransform_nonInvertible() {
595         // act/assert
596         GeometryTestUtils.assertThrows(() -> {
597             AffineTransformMatrix1D.createScale(0).normalTransform();
598         }, IllegalStateException.class);
599     }
600 
601     @Test
602     public void testInverse_identity() {
603         // act
604         final AffineTransformMatrix1D inverse = AffineTransformMatrix1D.identity().inverse();
605 
606         // assert
607         final double[] expected = {1, 0};
608         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
609     }
610 
611     @Test
612     public void testInverse_multiplyByInverse_producesIdentity() {
613         // arrange
614         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 3);
615 
616         final AffineTransformMatrix1D inv = a.inverse();
617 
618         // act
619         final AffineTransformMatrix1D result = inv.multiply(a);
620 
621         // assert
622         final double[] expected = {1, 0};
623         Assert.assertArrayEquals(expected, result.toArray(), EPS);
624     }
625 
626     @Test
627     public void testInverse_translate() {
628         // arrange
629         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createTranslation(3);
630 
631         // act
632         final AffineTransformMatrix1D inverse = transform.inverse();
633 
634         // assert
635         final double[] expected = {1, -3};
636         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
637     }
638 
639     @Test
640     public void testInverse_scale() {
641         // arrange
642         final AffineTransformMatrix1D transform = AffineTransformMatrix1D.createScale(10);
643 
644         // act
645         final AffineTransformMatrix1D inverse = transform.inverse();
646 
647         // assert
648         final double[] expected = {0.1, 0};
649         Assert.assertArrayEquals(expected, inverse.toArray(), 0.0);
650     }
651 
652     @Test
653     public void testInverse_undoesOriginalTransform_translationAndScale() {
654         // arrange
655         final Vector1D v1 = Vector1D.ZERO;
656         final Vector1D v2 = Vector1D.Unit.PLUS;
657         final Vector1D v3 = Vector1D.of(1.5);
658         final Vector1D v4 = Vector1D.of(-2);
659 
660         // act/assert
661         runWithCoordinates(x -> {
662             final AffineTransformMatrix1D transform = AffineTransformMatrix1D
663                         .createTranslation(x)
664                         .scale(2)
665                         .translate(x / 3);
666 
667             final AffineTransformMatrix1D inverse = transform.inverse();
668 
669             EuclideanTestUtils.assertCoordinatesEqual(v1, inverse.apply(transform.apply(v1)), EPS);
670             EuclideanTestUtils.assertCoordinatesEqual(v2, inverse.apply(transform.apply(v2)), EPS);
671             EuclideanTestUtils.assertCoordinatesEqual(v3, inverse.apply(transform.apply(v3)), EPS);
672             EuclideanTestUtils.assertCoordinatesEqual(v4, inverse.apply(transform.apply(v4)), EPS);
673         });
674     }
675 
676     @Test
677     public void testInverse_nonInvertible() {
678         // act/assert
679         GeometryTestUtils.assertThrows(() -> {
680             AffineTransformMatrix1D.of(0, 0).inverse();
681         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is 0.0");
682 
683         GeometryTestUtils.assertThrows(() -> {
684             AffineTransformMatrix1D.of(Double.NaN, 0).inverse();
685         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is NaN");
686 
687         GeometryTestUtils.assertThrows(() -> {
688             AffineTransformMatrix1D.of(Double.NEGATIVE_INFINITY, 0.0).inverse();
689         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is -Infinity");
690 
691         GeometryTestUtils.assertThrows(() -> {
692             AffineTransformMatrix1D.of(Double.POSITIVE_INFINITY, 0).inverse();
693         }, IllegalStateException.class, "Matrix is not invertible; matrix determinant is Infinity");
694 
695         GeometryTestUtils.assertThrows(() -> {
696             AffineTransformMatrix1D.of(1, Double.NaN).inverse();
697         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: NaN");
698 
699         GeometryTestUtils.assertThrows(() -> {
700             AffineTransformMatrix1D.of(1, Double.NEGATIVE_INFINITY).inverse();
701         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: -Infinity");
702 
703         GeometryTestUtils.assertThrows(() -> {
704             AffineTransformMatrix1D.of(1, Double.POSITIVE_INFINITY).inverse();
705         }, IllegalStateException.class, "Matrix is not invertible; invalid matrix element: Infinity");
706     }
707 
708     @Test
709     public void testHashCode() {
710         // act
711         final int orig = AffineTransformMatrix1D.of(1, 2).hashCode();
712         final int same = AffineTransformMatrix1D.of(1, 2).hashCode();
713 
714         // assert
715         Assert.assertEquals(orig, same);
716 
717         Assert.assertNotEquals(orig, AffineTransformMatrix1D.of(0, 2).hashCode());
718         Assert.assertNotEquals(orig, AffineTransformMatrix1D.of(1, 0).hashCode());
719     }
720 
721     @Test
722     public void testEquals() {
723         // arrange
724         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 2);
725 
726         // act/assert
727         Assert.assertEquals(a, a);
728 
729         Assert.assertFalse(a.equals(null));
730         Assert.assertFalse(a.equals(new Object()));
731 
732         Assert.assertNotEquals(a, AffineTransformMatrix1D.of(0, 2));
733         Assert.assertNotEquals(a, AffineTransformMatrix1D.of(1, 0));
734     }
735 
736     @Test
737     public void testEqualsAndHashCode_signedZeroConsistency() {
738         // arrange
739         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(0.0, -0.0);
740         final AffineTransformMatrix1D b = AffineTransformMatrix1D.of(-0.0, 0.0);
741         final AffineTransformMatrix1D c = AffineTransformMatrix1D.of(0.0, -0.0);
742         final AffineTransformMatrix1D d = AffineTransformMatrix1D.of(-0.0, 0.0);
743 
744         // act/assert
745         Assert.assertFalse(a.equals(b));
746 
747         Assert.assertTrue(a.equals(c));
748         Assert.assertEquals(a.hashCode(), c.hashCode());
749 
750         Assert.assertTrue(b.equals(d));
751         Assert.assertEquals(b.hashCode(), d.hashCode());
752     }
753 
754     @Test
755     public void testToString() {
756         // arrange
757         final AffineTransformMatrix1D a = AffineTransformMatrix1D.of(1, 2);
758 
759         // act
760         final String result = a.toString();
761 
762         // assert
763         Assert.assertEquals("[ 1.0, 2.0 ]", result);
764     }
765 
766     @FunctionalInterface
767     private interface Coordinate1DTest {
768 
769         void run(double x);
770     }
771 
772     private static void runWithCoordinates(final Coordinate1DTest test) {
773         runWithCoordinates(test, false);
774     }
775 
776     private static void runWithCoordinates(final Coordinate1DTest test, final boolean skipZero) {
777         runWithCoordinates(test, -1e-2, 1e-2, 5e-3, skipZero);
778         runWithCoordinates(test, -1e2, 1e2, 5, skipZero);
779     }
780 
781     private static void runWithCoordinates(final Coordinate1DTest test, final double min, final double max, final double step, final boolean skipZero) {
782         for (double x = min; x <= max; x += step) {
783             if (!skipZero || x != 0.0) {
784                 test.run(x);
785             }
786         }
787     }
788 }