This is an automated email from the ASF dual-hosted git repository.

erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-geometry.git

commit b7f391ac5c53503239b063f2d9066d5886880a89
Author: Matt Juntunen <matt.juntu...@hotmail.com>
AuthorDate: Mon Sep 10 22:21:05 2018 -0400

    GEOMETRY-8: adding initial exception hierarchy; using IllegalNormException 
in Vector classes
---
 .../org/apache/commons/geometry/core/Vector.java   | 18 ++--
 .../geometry/core/exception/GeometryException.java | 32 +++++++
 .../core/exception/GeometryStateException.java     | 30 +++++++
 .../core/exception/GeometryValueException.java     | 30 +++++++
 .../core/exception/IllegalNormException.java       | 34 ++++++++
 .../geometry/core/exception/package-info.java      | 23 +++++
 .../commons/geometry/core/GeometryTestUtils.java   | 32 +++++++
 .../geometry/euclidean}/internal/Vectors.java      | 21 ++++-
 .../commons/geometry/euclidean/oned/Vector1D.java  | 50 +++++------
 .../commons/geometry/euclidean/threed/Point3D.java |  2 +-
 .../geometry/euclidean/threed/Rotation.java        | 85 +++++++++----------
 .../geometry/euclidean/threed/Vector3D.java        | 52 +++++-------
 .../commons/geometry/euclidean/twod/Point2D.java   |  2 +-
 .../commons/geometry/euclidean/twod/Vector2D.java  | 45 +++++-----
 .../geometry/euclidean}/internal/VectorsTest.java  | 26 +++++-
 .../geometry/euclidean/oned/Vector1DTest.java      | 89 +++++++++++++++-----
 .../geometry/euclidean/threed/RotationTest.java    |  3 +-
 .../geometry/euclidean/threed/Vector3DTest.java    | 98 ++++++++++++++++++----
 .../geometry/euclidean/twod/Vector2DTest.java      | 87 +++++++++++++++----
 19 files changed, 558 insertions(+), 201 deletions(-)

diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
index d4eb296..b04d644 100644
--- 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/Vector.java
@@ -16,6 +16,8 @@
  */
 package org.apache.commons.geometry.core;
 
+import org.apache.commons.geometry.core.exception.IllegalNormException;
+
 /** Interface representing a vector in a vector space. The most common
  * use of this interface is to represent displacement vectors in an affine
  * space.
@@ -120,9 +122,9 @@ public interface Vector<V extends Vector<V>> extends 
Spatial {
     /** Get a normalized vector aligned with the instance. The returned
      * vector has a magnitude of 1.
      * @return a new normalized vector
-     * @exception IllegalStateException if the norm is zero
+     * @exception IllegalNormException if the norm is zero, NaN, or infinite
      */
-    V normalize() throws IllegalStateException;
+    V normalize() throws IllegalNormException;
 
     /** Multiply the instance by a scalar.
      * @param a scalar
@@ -180,10 +182,10 @@ public interface Vector<V extends Vector<V>> extends 
Spatial {
      * </code>
      * @param base base vector
      * @return the vector projection of the instance onto {@code base}
-     * @exception IllegalStateException if the norm of the base vector is zero
+     * @exception IllegalNormException if the norm of the base vector is zero, 
NaN, or infinite
      * @see #reject(Vector)
      */
-    V project(V base) throws IllegalStateException;
+    V project(V base) throws IllegalNormException;
 
     /** Get the rejection of the instance from the given base vector. The 
returned
      * vector is orthogonal to {@code base}. This operation can be interpreted 
as
@@ -195,15 +197,15 @@ public interface Vector<V extends Vector<V>> extends 
Spatial {
      * </code>
      * @param base base vector
      * @return the vector rejection of the instance from {@code base}
-     * @exception IllegalStateException if the norm of the base vector is zero
+     * @exception IllegalNormException if the norm of the base vector is zero, 
NaN, or infinite
      * @see #project(Vector)
      */
-    V reject(V base) throws IllegalStateException;
+    V reject(V base) throws IllegalNormException;
 
     /** Compute the angular separation between two vectors in radians.
      * @param v other vector
      * @return angular separation between this instance and v in radians
-     * @exception IllegalStateException if either vector has a zero norm
+     * @exception IllegalNormException if either vector has a zero, NaN, or 
infinite norm
      */
-    double angle(V v) throws IllegalStateException;
+    double angle(V v) throws IllegalNormException;
 }
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java
new file mode 100644
index 0000000..48e4b2a
--- /dev/null
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.core.exception;
+
+/** Base class for geometry exceptions.
+ */
+public class GeometryException extends RuntimeException {
+
+    /** Serializable version identifier */
+    private static final long serialVersionUID = 20180909L;
+
+    /** Simple constructor with error message.
+     * @param msg exception message string
+     */
+    public GeometryException(String msg) {
+        super(msg);
+    }
+}
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java
new file mode 100644
index 0000000..4a8080e
--- /dev/null
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryStateException.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.core.exception;
+
+public class GeometryStateException extends GeometryException {
+
+    /** Serializable version identifier */
+    private static final long serialVersionUID = 20180909L;
+
+    /** Simple constructor with error message.
+     * @param msg exception message string
+     */
+    public GeometryStateException(String msg) {
+        super(msg);
+    }
+}
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java
new file mode 100644
index 0000000..5d7b0d6
--- /dev/null
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/GeometryValueException.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.core.exception;
+
+public class GeometryValueException extends GeometryException {
+
+    /** Serializable version identifier */
+    private static final long serialVersionUID = 20180909L;
+
+    /** Simple constructor with error message.
+     * @param msg exception message string
+     */
+    public GeometryValueException(String msg) {
+        super(msg);
+    }
+}
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java
new file mode 100644
index 0000000..b0d80e8
--- /dev/null
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/IllegalNormException.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.geometry.core.exception;
+
+/** Exception thrown when an illegal vector norm value is encountered
+ * in an operation.
+ */
+public class IllegalNormException extends GeometryValueException {
+
+    /** Serializable version identifier */
+    private static final long serialVersionUID = 20180909L;
+
+    /**
+     * Simple constructor accepting the illegal norm value.
+     * @param norm the illegal norm value
+     */
+    public IllegalNormException(double norm) {
+        super("Illegal norm: " + norm);
+    }
+}
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java
new file mode 100644
index 0000000..8180f81
--- /dev/null
+++ 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/exception/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package contains exception types used by this library.
+ * </p>
+ */
+package org.apache.commons.geometry.core.exception;
diff --git 
a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java
 
b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java
index 3dbf58a..60bdeea 100644
--- 
a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java
+++ 
b/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/GeometryTestUtils.java
@@ -45,6 +45,38 @@ public class GeometryTestUtils {
         Assert.assertTrue(msg, value < 0);
     }
 
+    /** Asserts that the given Runnable throws an exception of the given type.
+     * @param r the Runnable instance
+     * @param exceptionType the expected exception type
+     */
+    public static void assertThrows(Runnable r, Class<?> exceptionType) {
+        assertThrows(r, exceptionType, null);
+    }
+
+    /** Asserts that the given Runnable throws an exception of the given type. 
If
+     * {@code message} is not null, the exception message is asserted to equal 
the
+     * given value.
+     * @param r the Runnable instance
+     * @param exceptionType the expected exception type
+     * @param message the expected exception message; ignored if null
+     */
+    public static void assertThrows(Runnable r, Class<?> exceptionType, String 
message) {
+        try {
+            r.run();
+            Assert.fail("Operation should have thrown an exception");
+        }
+        catch (Exception exc) {
+            Class<?> actualType = exc.getClass();
+
+            Assert.assertTrue("Expected exception of type " + 
exceptionType.getName() + " but was " + actualType.getName(),
+                    exceptionType.isAssignableFrom(actualType));
+
+            if (message != null) {
+                Assert.assertEquals(message, exc.getMessage());
+            }
+        }
+    }
+
     /**
      * Serializes and then recovers an object from a byte array. Returns the 
deserialized object.
      *
diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java
similarity index 91%
rename from 
commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java
rename to 
commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java
index 673d53b..cbb19b2 100644
--- 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/internal/Vectors.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/Vectors.java
@@ -14,7 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.core.internal;
+package org.apache.commons.geometry.euclidean.internal;
+
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 
 /** This class consists exclusively of static vector utility methods.
  */
@@ -23,6 +25,23 @@ public final class Vectors {
     /** Private constructor. */
     private Vectors() {}
 
+
+    /** Throws an {@link IllegalNormException} if the  given norm value
+     * is not finite (ie, NaN or infinite) or zero. The argument is returned
+     * to allow this method to be called in-line.
+     * @param norm vector norm value
+     * @return the validated norm value
+     * @throws IllegalNormException if the given norm value is NaN, infinite,
+     *  or zero
+     */
+    public static double checkFiniteNonZeroNorm(final double norm) throws 
IllegalNormException {
+        if (!Double.isFinite(norm) || norm == 0.0) {
+            throw new IllegalNormException(norm);
+        }
+
+        return norm;
+    }
+
     /** Get the L<sub>1</sub> norm for the vector with the given components.
      * This is defined as the sum of the absolute values of all vector 
components.
      * @param x vector component
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
index df40670..c5d455d 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/oned/Vector1D.java
@@ -17,10 +17,10 @@
 package org.apache.commons.geometry.euclidean.oned;
 
 import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
-import org.apache.commons.geometry.core.internal.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
-import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
+import org.apache.commons.geometry.euclidean.internal.Vectors;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in one-dimensional Euclidean space.
@@ -117,14 +117,9 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
     /** {@inheritDoc} */
     @Override
     public Vector1D withMagnitude(double magnitude) {
-        final double x = getX();
-        if (x > 0.0) {
-            return new Vector1D(magnitude);
-        }
-        else if (x < 0.0) {
-            return new Vector1D(-magnitude);
-        }
-        throw new ZeroNormException();
+        Vectors.checkFiniteNonZeroNorm(getNorm());
+
+        return (getX() > 0.0)? new Vector1D(magnitude) : new 
Vector1D(-magnitude);
     }
 
     /** {@inheritDoc} */
@@ -159,15 +154,10 @@ public final class Vector1D extends Cartesian1D 
implements EuclideanVector<Point
 
     /** {@inheritDoc} */
     @Override
-    public Vector1D normalize() throws IllegalStateException {
-        final double x = getX();
-        if (x > 0.0) {
-            return ONE;
-        }
-        else if (x < 0.0) {
-            return MINUS_ONE;
-        }
-        throw new ZeroNormException();
+    public Vector1D normalize() throws IllegalNormException {
+        Vectors.checkFiniteNonZeroNorm(getNorm());
+
+        return (getX() > 0.0) ? ONE : MINUS_ONE;
     }
 
     /** {@inheritDoc} */
@@ -210,10 +200,9 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
      * <p>For the one-dimensional case, this method simply returns the current 
instance.</p>
      */
     @Override
-    public Vector1D project(final Vector1D base) throws IllegalStateException {
-        if (base.getX() == 0) {
-            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
-        }
+    public Vector1D project(final Vector1D base) throws IllegalNormException {
+        Vectors.checkFiniteNonZeroNorm(base.getNorm());
+
         return this;
     }
 
@@ -221,10 +210,9 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
      * <p>For the one-dimensional case, this method simply returns the zero 
vector.</p>
      */
     @Override
-    public Vector1D reject(final Vector1D base) throws IllegalStateException {
-        if (base.getX() == 0) {
-            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
-        }
+    public Vector1D reject(final Vector1D base) throws IllegalNormException {
+        Vectors.checkFiniteNonZeroNorm(base.getNorm());
+
         return Vector1D.ZERO;
     }
 
@@ -233,13 +221,13 @@ public final class Vector1D extends Cartesian1D 
implements EuclideanVector<Point
      * the same sign and {@code pi} if they are opposite.</p>
      */
     @Override
-    public double angle(final Vector1D v) throws IllegalStateException {
+    public double angle(final Vector1D v) throws IllegalNormException {
+        Vectors.checkFiniteNonZeroNorm(getNorm());
+        Vectors.checkFiniteNonZeroNorm(v.getNorm());
+
         final double sig1 = Math.signum(getX());
         final double sig2 = Math.signum(v.getX());
 
-        if (sig1 == 0 || sig2 == 0) {
-            throw new ZeroNormException();
-        }
         // the angle is 0 if the x value signs are the same and pi if not
         return (sig1 == sig2) ? 0.0 : Geometry.PI;
     }
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
index 8e15230..b9a4e3e 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Point3D.java
@@ -18,8 +18,8 @@
 package org.apache.commons.geometry.euclidean.threed;
 
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
-import org.apache.commons.geometry.core.internal.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanPoint;
+import org.apache.commons.geometry.euclidean.internal.Vectors;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a point in three-dimensional Euclidean space.
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
index 272d5c0..7ab3ec3 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Rotation.java
@@ -19,6 +19,7 @@ package org.apache.commons.geometry.euclidean.threed;
 
 import java.io.Serializable;
 
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /**
@@ -264,54 +265,48 @@ public class Rotation implements Serializable {
    * @param u2 second vector of the origin pair
    * @param v1 desired image of u1 by the rotation
    * @param v2 desired image of u2 by the rotation
-   * @exception IllegalArgumentException if the norm of one of the vectors is 
zero,
-   * or if one of the pair is degenerated (i.e. the vectors of the pair are 
collinear)
+   * @exception IllegalNormException if the norm of one of the vectors is 
zero, NaN, infinite,
+   *    or if one of the pair is degenerated (i.e. the vectors of the pair are 
collinear)
    */
   public Rotation(Vector3D u1, Vector3D u2, Vector3D v1, Vector3D v2)
-      throws IllegalArgumentException {
-
-      try {
-          // build orthonormalized base from u1, u2
-          // this fails when vectors are null or collinear, which is forbidden 
to define a rotation
-          final Vector3D u3 = u1.crossProduct(u2).normalize();
-          u2 = u3.crossProduct(u1).normalize();
-          u1 = u1.normalize();
-
-          // build an orthonormalized base from v1, v2
-          // this fails when vectors are null or collinear, which is forbidden 
to define a rotation
-          final Vector3D v3 = v1.crossProduct(v2).normalize();
-          v2 = v3.crossProduct(v1).normalize();
-          v1 = v1.normalize();
-
-          // buid a matrix transforming the first base into the second one
-          final double[][] m = new double[][] {
-              {
-                  LinearCombination.value(u1.getX(), v1.getX(), u2.getX(), 
v2.getX(), u3.getX(), v3.getX()),
-                  LinearCombination.value(u1.getY(), v1.getX(), u2.getY(), 
v2.getX(), u3.getY(), v3.getX()),
-                  LinearCombination.value(u1.getZ(), v1.getX(), u2.getZ(), 
v2.getX(), u3.getZ(), v3.getX())
-              },
-              {
-                  LinearCombination.value(u1.getX(), v1.getY(), u2.getX(), 
v2.getY(), u3.getX(), v3.getY()),
-                  LinearCombination.value(u1.getY(), v1.getY(), u2.getY(), 
v2.getY(), u3.getY(), v3.getY()),
-                  LinearCombination.value(u1.getZ(), v1.getY(), u2.getZ(), 
v2.getY(), u3.getZ(), v3.getY())
-              },
-              {
-                  LinearCombination.value(u1.getX(), v1.getZ(), u2.getX(), 
v2.getZ(), u3.getX(), v3.getZ()),
-                  LinearCombination.value(u1.getY(), v1.getZ(), u2.getY(), 
v2.getZ(), u3.getY(), v3.getZ()),
-                  LinearCombination.value(u1.getZ(), v1.getZ(), u2.getZ(), 
v2.getZ(), u3.getZ(), v3.getZ())
-              }
-          };
-
-          double[] quat = mat2quat(m);
-          q0 = quat[0];
-          q1 = quat[1];
-          q2 = quat[2];
-          q3 = quat[3];
-
-      } catch (IllegalStateException exc) {
-          throw new IllegalArgumentException("Invalid rotation vector pairs", 
exc);
-      }
+      throws IllegalNormException {
+
+      // build orthonormalized base from u1, u2
+      // this fails when vectors are null or collinear, which is forbidden to 
define a rotation
+      final Vector3D u3 = u1.crossProduct(u2).normalize();
+      u2 = u3.crossProduct(u1).normalize();
+      u1 = u1.normalize();
+
+      // build an orthonormalized base from v1, v2
+      // this fails when vectors are null or collinear, which is forbidden to 
define a rotation
+      final Vector3D v3 = v1.crossProduct(v2).normalize();
+      v2 = v3.crossProduct(v1).normalize();
+      v1 = v1.normalize();
+
+      // buid a matrix transforming the first base into the second one
+      final double[][] m = new double[][] {
+          {
+              LinearCombination.value(u1.getX(), v1.getX(), u2.getX(), 
v2.getX(), u3.getX(), v3.getX()),
+              LinearCombination.value(u1.getY(), v1.getX(), u2.getY(), 
v2.getX(), u3.getY(), v3.getX()),
+              LinearCombination.value(u1.getZ(), v1.getX(), u2.getZ(), 
v2.getX(), u3.getZ(), v3.getX())
+          },
+          {
+              LinearCombination.value(u1.getX(), v1.getY(), u2.getX(), 
v2.getY(), u3.getX(), v3.getY()),
+              LinearCombination.value(u1.getY(), v1.getY(), u2.getY(), 
v2.getY(), u3.getY(), v3.getY()),
+              LinearCombination.value(u1.getZ(), v1.getY(), u2.getZ(), 
v2.getY(), u3.getZ(), v3.getY())
+          },
+          {
+              LinearCombination.value(u1.getX(), v1.getZ(), u2.getX(), 
v2.getZ(), u3.getX(), v3.getZ()),
+              LinearCombination.value(u1.getY(), v1.getZ(), u2.getY(), 
v2.getZ(), u3.getY(), v3.getZ()),
+              LinearCombination.value(u1.getZ(), v1.getZ(), u2.getZ(), 
v2.getZ(), u3.getZ(), v3.getZ())
+          }
+      };
 
+      double[] quat = mat2quat(m);
+      q0 = quat[0];
+      q1 = quat[1];
+      q2 = quat[2];
+      q3 = quat[3];
   }
 
   /** Build one of the rotations that transform one vector into another one.
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
index fd52558..d8de801 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/threed/Vector3D.java
@@ -16,10 +16,10 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
-import org.apache.commons.geometry.core.internal.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
-import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
+import org.apache.commons.geometry.euclidean.internal.Vectors;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in three-dimensional Euclidean space.
@@ -131,7 +131,7 @@ public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point
     /** {@inheritDoc} */
     @Override
     public Vector3D withMagnitude(double magnitude) {
-        final double invNorm = 1.0 / getNonZeroNorm();
+        final double invNorm = 1.0 / getFiniteNonZeroNorm();
 
         return new Vector3D(
                     magnitude * getX() * invNorm,
@@ -188,8 +188,8 @@ public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point
 
     /** {@inheritDoc} */
     @Override
-    public Vector3D normalize() throws IllegalStateException {
-        return scalarMultiply(1.0 / getNonZeroNorm());
+    public Vector3D normalize() throws IllegalNormException {
+        return scalarMultiply(1.0 / getFiniteNonZeroNorm());
     }
 
     /** Get a vector orthogonal to the instance.
@@ -205,10 +205,11 @@ public final class Vector3D extends Cartesian3D 
implements EuclideanVector<Point
      *   Vector3D j = Vector3D.crossProduct(k, i);
      * </code></pre>
      * @return a new normalized vector orthogonal to the instance
-     * @exception IllegalStateException if the norm of the instance is zero
+     * @exception IllegalNormException if the norm of the instance is zero, 
NaN,
+     *  or infinite
      */
-    public Vector3D orthogonal() throws IllegalStateException {
-        double threshold = 0.6 * getNonZeroNorm();
+    public Vector3D orthogonal() throws IllegalNormException {
+        double threshold = 0.6 * getFiniteNonZeroNorm();
 
         final double x = getX();
         final double y = getY();
@@ -233,8 +234,8 @@ public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point
      * other.</p>
      */
     @Override
-    public double angle(Vector3D v) throws IllegalStateException {
-        double normProduct = getNonZeroNorm() * v.getNonZeroNorm();
+    public double angle(Vector3D v) throws IllegalNormException {
+        double normProduct = getFiniteNonZeroNorm() * v.getFiniteNonZeroNorm();
 
         double dot = dotProduct(v);
         double threshold = normProduct * 0.9999;
@@ -322,13 +323,13 @@ public final class Vector3D extends Cartesian3D 
implements EuclideanVector<Point
 
     /** {@inheritDoc} */
     @Override
-    public Vector3D project(Vector3D base) throws IllegalStateException {
+    public Vector3D project(Vector3D base) throws IllegalNormException {
         return getComponent(base, false);
     }
 
     /** {@inheritDoc} */
     @Override
-    public Vector3D reject(Vector3D base) throws IllegalStateException {
+    public Vector3D reject(Vector3D base) throws IllegalNormException {
         return getComponent(base, true);
     }
 
@@ -382,17 +383,13 @@ public final class Vector3D extends Cartesian3D 
implements EuclideanVector<Point
         return false;
     }
 
-    /** Returns the vector norm, throwing an IllegalStateException if the norm 
is zero.
-     * @return the non-zero norm value
-     * @throws IllegalStateException if the norm is zero
+    /** Returns the vector norm, throwing an IllegalNormException if the norm 
is zero,
+     * NaN, or infinite.
+     * @return the finite, non-zero norm value
+     * @throws IllegalNormException if the norm is zero, NaN, or infinite
      */
-    private double getNonZeroNorm() throws IllegalStateException {
-        final double n = getNorm();
-        if (n == 0) {
-            throw new ZeroNormException();
-        }
-
-        return n;
+    private double getFiniteNonZeroNorm() throws IllegalNormException {
+        return Vectors.checkFiniteNonZeroNorm(getNorm());
     }
 
     /** Returns a component of the current instance relative to the given base
@@ -404,17 +401,14 @@ public final class Vector3D extends Cartesian3D 
implements EuclideanVector<Point
      *      is returned.
      * @return The projection or rejection of this instance relative to {@code 
base},
      *      depending on the value of {@code reject}.
-     * @throws IllegalStateException if {@code base} has a zero norm
+     * @throws IllegalNormException if {@code base} has a zero, NaN, or 
infinite norm
      */
-    private Vector3D getComponent(Vector3D base, boolean reject) throws 
IllegalStateException {
+    private Vector3D getComponent(Vector3D base, boolean reject) throws 
IllegalNormException {
         final double aDotB = dotProduct(base);
 
-        final double baseMagSq = base.getNormSq();
-        if (baseMagSq == 0.0) {
-            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
-        }
+        final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm());
 
-        final double scale = aDotB / baseMagSq;
+        final double scale = aDotB / (baseMag * baseMag);
 
         final double projX = scale * base.getX();
         final double projY = scale * base.getY();
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
index e7a89cb..323d8e6 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Point2D.java
@@ -17,8 +17,8 @@
 package org.apache.commons.geometry.euclidean.twod;
 
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
-import org.apache.commons.geometry.core.internal.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanPoint;
+import org.apache.commons.geometry.euclidean.internal.Vectors;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a point in two-dimensional Euclidean space.
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
index fec9764..298d052 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/twod/Vector2D.java
@@ -16,10 +16,10 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
-import org.apache.commons.geometry.core.internal.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
-import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
+import org.apache.commons.geometry.euclidean.internal.Vectors;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in two-dimensional Euclidean space.
@@ -131,7 +131,7 @@ public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point
     /** {@inheritDoc} */
     @Override
     public Vector2D withMagnitude(double magnitude) {
-        final double invNorm = 1.0 / getNonZeroNorm();
+        final double invNorm = 1.0 / getFiniteNonZeroNorm();
 
         return new Vector2D(
                     magnitude * getX() * invNorm,
@@ -171,8 +171,8 @@ public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point
 
     /** {@inheritDoc} */
     @Override
-    public Vector2D normalize() throws IllegalStateException {
-        return scalarMultiply(1.0 / getNonZeroNorm());
+    public Vector2D normalize() throws IllegalNormException {
+        return scalarMultiply(1.0 / getFiniteNonZeroNorm());
     }
 
     /** {@inheritDoc} */
@@ -213,13 +213,13 @@ public final class Vector2D extends Cartesian2D 
implements EuclideanVector<Point
 
     /** {@inheritDoc} */
     @Override
-    public Vector2D project(Vector2D base) throws IllegalStateException {
+    public Vector2D project(Vector2D base) throws IllegalNormException {
         return getComponent(base, false);
     }
 
     /** {@inheritDoc} */
     @Override
-    public Vector2D reject(Vector2D base) throws IllegalStateException {
+    public Vector2D reject(Vector2D base) throws IllegalNormException {
         return getComponent(base, true);
     }
 
@@ -231,8 +231,8 @@ public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point
      * other.</p>
      */
     @Override
-    public double angle(Vector2D v) throws IllegalStateException {
-        double normProduct = getNonZeroNorm() * v.getNonZeroNorm();
+    public double angle(Vector2D v) throws IllegalNormException {
+        double normProduct = getFiniteNonZeroNorm() * v.getFiniteNonZeroNorm();
 
         double dot = dotProduct(v);
         double threshold = normProduct * 0.9999;
@@ -328,17 +328,13 @@ public final class Vector2D extends Cartesian2D 
implements EuclideanVector<Point
         return false;
     }
 
-    /** Returns the vector norm, throwing an IllegalStateException if the norm 
is zero.
-     * @return the non-zero norm value
-     * @throws IllegalStateException if the norm is zero
+    /** Returns the vector norm, throwing an IllegalNormException if the norm 
is zero,
+     * NaN, or infinite.
+     * @return the finite, non-zero norm value
+     * @throws IllegalNormException if the norm is zero, NaN, or infinite
      */
-    private double getNonZeroNorm() throws IllegalStateException {
-        final double n = getNorm();
-        if (n == 0) {
-            throw new ZeroNormException();
-        }
-
-        return n;
+    private double getFiniteNonZeroNorm() throws IllegalNormException {
+        return Vectors.checkFiniteNonZeroNorm(getNorm());
     }
 
     /** Returns a component of the current instance relative to the given base
@@ -350,17 +346,14 @@ public final class Vector2D extends Cartesian2D 
implements EuclideanVector<Point
      *      is returned.
      * @return The projection or rejection of this instance relative to {@code 
base},
      *      depending on the value of {@code reject}.
-     * @throws IllegalStateException if {@code base} has a zero norm
+     * @throws IllegalNormException if {@code base} has a zero, NaN, or 
infinite norm
      */
-    private Vector2D getComponent(Vector2D base, boolean reject) throws 
IllegalStateException {
+    private Vector2D getComponent(Vector2D base, boolean reject) throws 
IllegalNormException {
         final double aDotB = dotProduct(base);
 
-        final double baseMagSq = base.getNormSq();
-        if (baseMagSq == 0.0) {
-            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
-        }
+        final double baseMag = Vectors.checkFiniteNonZeroNorm(base.getNorm());
 
-        final double scale = aDotB / baseMagSq;
+        final double scale = aDotB / (baseMag * baseMag);
 
         final double projX = scale * base.getX();
         final double projY = scale * base.getY();
diff --git 
a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java
 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java
similarity index 83%
rename from 
commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java
rename to 
commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java
index 9817b28..2d38904 100644
--- 
a/commons-geometry-core/src/test/java/org/apache/commons/geometry/core/internal/VectorsTest.java
+++ 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/internal/VectorsTest.java
@@ -14,9 +14,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.commons.geometry.core.internal;
+package org.apache.commons.geometry.euclidean.internal;
 
-import org.apache.commons.geometry.core.internal.Vectors;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -26,6 +27,27 @@ public class VectorsTest {
     private static final double EPS = Math.ulp(1d);
 
     @Test
+    public void testCheckFiniteNonZeroNorm() {
+        // act/assert
+        Assert.assertEquals(1.0, Vectors.checkFiniteNonZeroNorm(1.0), EPS);
+        Assert.assertEquals(23.12, Vectors.checkFiniteNonZeroNorm(23.12), EPS);
+        Assert.assertEquals(2e-12, Vectors.checkFiniteNonZeroNorm(2e-12), EPS);
+
+        Assert.assertEquals(-1.0, Vectors.checkFiniteNonZeroNorm(-1.0), EPS);
+        Assert.assertEquals(-23.12, Vectors.checkFiniteNonZeroNorm(-23.12), 
EPS);
+        Assert.assertEquals(-2e-12, Vectors.checkFiniteNonZeroNorm(-2e-12), 
EPS);
+
+        GeometryTestUtils.assertThrows(() -> 
Vectors.checkFiniteNonZeroNorm(0.0),
+                IllegalNormException.class, "Illegal norm: 0.0");
+        GeometryTestUtils.assertThrows(() -> 
Vectors.checkFiniteNonZeroNorm(Double.NaN),
+                IllegalNormException.class, "Illegal norm: NaN");
+        GeometryTestUtils.assertThrows(() -> 
Vectors.checkFiniteNonZeroNorm(Double.POSITIVE_INFINITY),
+                IllegalNormException.class, "Illegal norm: Infinity");
+        GeometryTestUtils.assertThrows(() -> 
Vectors.checkFiniteNonZeroNorm(Double.NEGATIVE_INFINITY),
+                IllegalNormException.class, "Illegal norm: -Infinity");
+    }
+
+    @Test
     public void testNorm1_oneD() {
         // act/assert
         Assert.assertEquals(0.0, Vectors.norm1(0.0), EPS);
diff --git 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
index e5b580a..ee817cc 100644
--- 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
+++ 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/oned/Vector1DTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.oned;
 import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -119,10 +121,17 @@ public class Vector1DTest {
         checkVector(Vector1D.of(-5).withMagnitude(3.0), -3.0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testWithMagnitude_zeroNorm() {
+    @Test
+    public void testWithMagnitude_illegalNorm() {
         // act/assert
-        Vector1D.ZERO.withMagnitude(1.0);
+        GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector1D.NaN.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.POSITIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.NEGATIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
     }
 
     @Test
@@ -196,10 +205,17 @@ public class Vector1DTest {
         checkVector(Vector1D.of(-5).normalize(), -1);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testNormalize_zeroNorm() {
-        // act
-        Vector1D.ZERO.normalize();
+    @Test
+    public void testNormalize_illegalNorm() {
+        // act/assert
+        GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector1D.NaN.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.POSITIVE_INFINITY.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.NEGATIVE_INFINITY.normalize(),
+                IllegalNormException.class);
     }
 
     @Test
@@ -317,10 +333,17 @@ public class Vector1DTest {
         checkVector(v3.project(v3), 4);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testProject_baseHasZeroNorm() {
+    @Test
+    public void testProject_baseHasIllegalNorm() {
         // act/assert
-        Vector1D.of(2.0).project(Vector1D.ZERO);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).project(Vector1D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).project(Vector1D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).project(Vector1D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).project(Vector1D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -340,10 +363,17 @@ public class Vector1DTest {
         checkVector(v2.reject(v2), 0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testReject_baseHasZeroNorm() {
+    @Test
+    public void testReject_baseHasIllegalNorm() {
         // act/assert
-        Vector1D.of(2.0).reject(Vector1D.ZERO);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).reject(Vector1D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).reject(Vector1D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).reject(Vector1D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.of(2.0).reject(Vector1D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -376,16 +406,29 @@ public class Vector1DTest {
         Assert.assertEquals(0.0, v4.angle(v4), TEST_TOLERANCE);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testAngle_firstVectorZero() {
-        // act/assert
-        Vector1D.ZERO.angle(Vector1D.of(1.0));
-    }
-
-    @Test(expected = IllegalStateException.class)
-    public void testAngle_secondVectorZero() {
-        // act/assert
-        Vector1D.of(1.0).angle(Vector1D.ZERO);
+    @Test
+    public void testAngle_illegalNorm() {
+        // arrange
+        Vector1D v = Vector1D.of(1.0);
+
+        // act/assert
+        GeometryTestUtils.assertThrows(() -> Vector1D.ZERO.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector1D.NaN.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.POSITIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector1D.NEGATIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector1D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector1D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector1D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
diff --git 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
index ca28f44..9e5bad0 100644
--- 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
+++ 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/RotationTest.java
@@ -17,6 +17,7 @@
 
 package org.apache.commons.geometry.euclidean.threed;
 
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.numbers.angle.PlaneAngleRadians;
 import org.junit.Assert;
 import org.junit.Test;
@@ -235,7 +236,7 @@ public class RotationTest {
     try {
         new Rotation(u1, u2, Vector3D.ZERO, v2);
         Assert.fail("an exception should have been thrown");
-    } catch (IllegalArgumentException e) {
+    } catch (IllegalNormException e) {
       // expected behavior
     }
 
diff --git 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
index 3aa2dd7..4d34a91 100644
--- 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
+++ 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/threed/Vector3DTest.java
@@ -20,6 +20,8 @@ package org.apache.commons.geometry.euclidean.threed;
 import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
 import org.apache.commons.numbers.core.Precision;
 import org.apache.commons.rng.UniformRandomProvider;
@@ -144,10 +146,17 @@ public class Vector3DTest {
         checkVector(Vector3D.of(x, y, z).withMagnitude(3), 3 * normX, 3 * 
normY, 3 * normZ);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testWithMagnitude_zeroNorm() {
+    @Test
+    public void testWithMagnitude_illegalNorm() {
         // act/assert
-        Vector3D.ZERO.withMagnitude(1.0);
+        GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector3D.NaN.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.POSITIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.NEGATIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
     }
 
     @Test
@@ -250,10 +259,17 @@ public class Vector3DTest {
         Assert.assertEquals(1.0, Vector3D.of(5, -4, 2).normalize().getNorm(), 
1.0e-12);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testNormalize_zeroNorm() {
+    @Test
+    public void testNormalize_illegalNorm() {
         // act/assert
-        Vector3D.ZERO.normalize();
+        GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector3D.NaN.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.POSITIVE_INFINITY.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.NEGATIVE_INFINITY.normalize(),
+                IllegalNormException.class);
     }
 
     @Test
@@ -271,10 +287,17 @@ public class Vector3DTest {
         Assert.assertEquals(0.0, v4.dotProduct(v4.orthogonal()), EPS);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testOrthogonal_zeroNorm() {
+    @Test
+    public void testOrthogonal_illegalNorm() {
         // act/assert
-        Vector3D.ZERO.orthogonal();
+        GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.orthogonal(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector3D.NaN.orthogonal(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.POSITIVE_INFINITY.orthogonal(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.NEGATIVE_INFINITY.orthogonal(),
+                IllegalNormException.class);
     }
 
     @Test
@@ -299,10 +322,29 @@ public class Vector3DTest {
         Assert.assertEquals(Geometry.HALF_PI, 
Vector3D.PLUS_X.angle(Vector3D.MINUS_Z), tolerance);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testAngle_zeroNorm() {
+    @Test
+    public void testAngle_illegalNorm() {
+        // arrange
+        Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
+
         // act/assert
-        Vector3D.ZERO.angle(Vector3D.PLUS_X);
+        GeometryTestUtils.assertThrows(() -> Vector3D.ZERO.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector3D.NaN.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.POSITIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector3D.NEGATIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector3D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector3D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector3D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -592,10 +634,20 @@ public class Vector3DTest {
         checkVector(v2.project(Vector3D.of(-1.0, -1.0, -1.0)), -6.0, -6.0, 
-6.0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testProject_baseHasZeroNorm() {
+    @Test
+    public void testProject_baseHasIllegalNorm() {
+        // arrange
+        Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
+
         // act/assert
-        Vector3D.of(1.0, 1.0, 1.0).project(Vector3D.ZERO);
+        GeometryTestUtils.assertThrows(() -> v.project(Vector3D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.project(Vector3D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.project(Vector3D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.project(Vector3D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -628,10 +680,20 @@ public class Vector3DTest {
         checkVector(v2.reject(Vector3D.of(-1.0, -1.0, -1.0)), 1.0, 0.0, -1.0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testReject_baseHasZeroNorm() {
+    @Test
+    public void testReject_baseHasIllegalNorm() {
+        // arrange
+        Vector3D v = Vector3D.of(1.0, 1.0, 1.0);
+
         // act/assert
-        Vector3D.of(1.0, 1.0, 1.0).reject(Vector3D.ZERO);
+        GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.reject(Vector3D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.reject(Vector3D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.reject(Vector3D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
diff --git 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
index 62e0c8e..791f1c6 100644
--- 
a/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
+++ 
b/commons-geometry-euclidean/src/test/java/org/apache/commons/geometry/euclidean/twod/Vector2DTest.java
@@ -19,6 +19,8 @@ package org.apache.commons.geometry.euclidean.twod;
 import java.util.regex.Pattern;
 
 import org.apache.commons.geometry.core.Geometry;
+import org.apache.commons.geometry.core.GeometryTestUtils;
+import org.apache.commons.geometry.core.exception.IllegalNormException;
 import org.apache.commons.geometry.euclidean.EuclideanTestUtils;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
@@ -159,10 +161,17 @@ public class Vector2DTest {
         checkVector(Vector2D.of(0.5, 0.5).withMagnitude(2), Math.sqrt(2), 
Math.sqrt(2));
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testWithMagnitude_zeroNorm() {
+    @Test
+    public void testWithMagnitude_illegalNorm() {
         // act/assert
-        Vector2D.ZERO.withMagnitude(1.0);
+        GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector2D.NaN.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.POSITIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.NEGATIVE_INFINITY.withMagnitude(2.0),
+                IllegalNormException.class);
     }
 
     @Test
@@ -243,9 +252,17 @@ public class Vector2DTest {
         checkVector(Vector2D.of(-1, 2).normalize(), -1.0/Math.sqrt(5), 
2.0/Math.sqrt(5));
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testNormalize_zeroNorm() {
-        Vector2D.ZERO.normalize();
+    @Test
+    public void testNormalize_illegalNorm() {
+        // act/assert
+        GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector2D.NaN.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.POSITIVE_INFINITY.normalize(),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.NEGATIVE_INFINITY.normalize(),
+                IllegalNormException.class);
     }
 
     @Test
@@ -376,9 +393,29 @@ public class Vector2DTest {
     }
 
 
-    @Test(expected = IllegalStateException.class)
-    public void testAngle_zeroNorm() {
-        Vector2D.of(1, 1).angle(Vector2D.ZERO);
+    @Test
+    public void testAngle_illegalNorm() {
+        // arrange
+        Vector2D v = Vector2D.of(1.0, 1.0);
+
+        // act/assert
+        GeometryTestUtils.assertThrows(() -> Vector2D.ZERO.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> Vector2D.NaN.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.POSITIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
Vector2D.NEGATIVE_INFINITY.angle(v),
+                IllegalNormException.class);
+
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.angle(Vector2D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector2D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.angle(Vector2D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -418,10 +455,20 @@ public class Vector2DTest {
         checkVector(v2.project(v1), (19.0 / 25.0) * 3.0, (19.0 / 25.0) * 4.0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testProject_baseHasZeroNorm() {
+    @Test
+    public void testProject_baseHasIllegalNorm() {
+        // arrange
+        Vector2D v = Vector2D.of(1.0, 1.0);
+
         // act/assert
-        Vector2D.of(1.0, 1.0).project(Vector2D.ZERO);
+        GeometryTestUtils.assertThrows(() -> v.project(Vector2D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.project(Vector2D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.project(Vector2D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.project(Vector2D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test
@@ -445,10 +492,20 @@ public class Vector2DTest {
         checkVector(v2.reject(v1), (-32.0 / 25.0), (6.0 / 25.0) * 4.0);
     }
 
-    @Test(expected = IllegalStateException.class)
-    public void testReject_baseHasZeroNorm() {
+    @Test
+    public void testReject_baseHasIllegalNorm() {
+        // arrange
+        Vector2D v = Vector2D.of(1.0, 1.0);
+
         // act/assert
-        Vector2D.of(1.0, 1.0).reject(Vector2D.ZERO);
+        GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.ZERO),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> v.reject(Vector2D.NaN),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.reject(Vector2D.POSITIVE_INFINITY),
+                IllegalNormException.class);
+        GeometryTestUtils.assertThrows(() -> 
v.reject(Vector2D.NEGATIVE_INFINITY),
+                IllegalNormException.class);
     }
 
     @Test

Reply via email to