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 204b994e40ba3ce0a917a20a2a8cd41430ae98ec
Author: Matt Juntunen <[email protected]>
AuthorDate: Mon Sep 3 15:04:28 2018 -0400

    GEOMETRY-9, GEOMETRY-8: removing MultiDimensionalVector class since 
project, reject, and angle do not require more than one dimension to work 
mathematically; adding internal ZeroNormException class
---
 .../geometry/core/MultiDimensionalVector.java      | 60 -------------
 .../org/apache/commons/geometry/core/Vector.java   | 35 ++++++++
 .../commons/geometry/euclidean/EuclideanPoint.java |  4 +-
 .../geometry/euclidean/EuclideanVector.java        |  4 +-
 .../euclidean/internal/ZeroNormException.java      | 50 +++++++++++
 .../geometry/euclidean/internal/package-info.java  | 25 ++++++
 .../commons/geometry/euclidean/oned/Vector1D.java  | 47 +++++++++--
 .../geometry/euclidean/threed/Rotation.java        |  2 +-
 .../geometry/euclidean/threed/Vector3D.java        |  8 +-
 .../commons/geometry/euclidean/twod/Vector2D.java  |  8 +-
 .../geometry/euclidean/oned/Vector1DTest.java      | 97 ++++++++++++++++++++++
 11 files changed, 264 insertions(+), 76 deletions(-)

diff --git 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/MultiDimensionalVector.java
 
b/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/MultiDimensionalVector.java
deleted file mode 100644
index cf21b56..0000000
--- 
a/commons-geometry-core/src/main/java/org/apache/commons/geometry/core/MultiDimensionalVector.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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;
-
-/** Interface representing a vector in a vector space with two or more
- * dimensions.
- *
- * @param <V> Vector implementation type
- */
-public interface MultiDimensionalVector<V extends MultiDimensionalVector<V>> 
extends Vector<V> {
-
-    /** Get the projection of the instance onto the given base vector. The 
returned
-     * vector is parallel to {@code base}. Vector projection and rejection onto
-     * a given base are related by the equation
-     * <code>
-     *      <strong>v</strong> = <strong>v<sub>projection</sub></strong> + 
<strong>v<sub>rejection</sub></strong>
-     * </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
-     * @see #reject(MultiDimensionalVector)
-     */
-    V project(V base) throws IllegalStateException;
-
-    /** 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
-     * returning the orthogonal projection of the instance onto the hyperplane
-     * orthogonal to {@code base}. Vector projection and rejection onto
-     * a given base are related by the equation
-     * <code>
-     *      <strong>v</strong> = <strong>v<sub>projection</sub></strong> + 
<strong>v<sub>rejection</sub></strong>
-     * </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
-     * @see #project(MultiDimensionalVector)
-     */
-    V reject(V base) throws IllegalStateException;
-
-    /** Compute the angular separation in radians between two vectors.
-     * @param v other vector
-     * @return angular separation between this instance and v in radians
-     * @exception IllegalStateException if either vector has a zero norm
-     */
-    double angle(V v) throws IllegalStateException;
-}
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 618757a..d4eb296 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
@@ -171,4 +171,39 @@ public interface Vector<V extends Vector<V>> extends 
Spatial {
      * @return the dot product this &middot; v
      */
     double dotProduct(V v);
+
+    /** Get the projection of the instance onto the given base vector. The 
returned
+     * vector is parallel to {@code base}. Vector projection and rejection onto
+     * a given base are related by the equation
+     * <code>
+     *      <strong>v</strong> = <strong>v<sub>projection</sub></strong> + 
<strong>v<sub>rejection</sub></strong>
+     * </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
+     * @see #reject(Vector)
+     */
+    V project(V base) throws IllegalStateException;
+
+    /** 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
+     * returning the orthogonal projection of the instance onto the hyperplane
+     * orthogonal to {@code base}. Vector projection and rejection onto
+     * a given base are related by the equation
+     * <code>
+     *      <strong>v</strong> = <strong>v<sub>projection</sub></strong> + 
<strong>v<sub>rejection</sub></strong>
+     * </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
+     * @see #project(Vector)
+     */
+    V reject(V base) throws IllegalStateException;
+
+    /** 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
+     */
+    double angle(V v) throws IllegalStateException;
 }
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
index 4941306..bde9f67 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanPoint.java
@@ -45,9 +45,11 @@ public interface EuclideanPoint<P extends EuclideanPoint<P, 
V>, V extends Euclid
      * {@code P = (1 - t)*A + t*B}, where {@code A} is the current point and 
{@code B}
      * is the given point. This means that if {@code t = 0}, a point equal to 
the current
      * point will be returned. If {@code t = 1}, a point equal to the argument 
will be returned.
+     * The {@code t} parameter is not constrained to the range {@code [0, 1]}, 
meaning that
+     * linear extrapolation can also be performed with this method.
      * @param p other point
      * @param t interpolation parameter
-     * @return interpolated point
+     * @return interpolated or extrapolated point
      */
     P lerp(P p, double t);
 }
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
index 8df3a91..ad4f21b 100644
--- 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/EuclideanVector.java
@@ -37,9 +37,11 @@ public interface EuclideanVector<P extends EuclideanPoint<P, 
V>, V extends Eucli
      * {@code V = (1 - t)*A + t*B}, where {@code A} is the current vector and 
{@code B}
      * is the given vector. This means that if {@code t = 0}, a vector equal 
to the current
      * vector will be returned. If {@code t = 1}, a vector equal to the 
argument will be returned.
+     * The {@code t} parameter is not constrained to the range {@code [0, 1]}, 
meaning that
+     * linear extrapolation can also be performed with this method.
      * @param v other vector
      * @param t interpolation parameter
-     * @return interpolated vector
+     * @return interpolated or extrapolated vector
      */
     V lerp(V v, double t);
 }
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/ZeroNormException.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/ZeroNormException.java
new file mode 100644
index 0000000..fe9ecf1
--- /dev/null
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/ZeroNormException.java
@@ -0,0 +1,50 @@
+/*
+ * 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.euclidean.internal;
+
+/** Internal exception class with constants for frequently used messages.
+ * This exception is thrown when vector operations requiring a non-zero
+ * vector norm are attempted with a vector with a zero norm.
+ */
+public class ZeroNormException extends IllegalStateException {
+
+    /** Default zero-norm error message. */
+    public static final String ZERO_NORM_MSG = "Norm is zero";
+
+    /** Error message for cases where code is attempting to use a zero-norm 
vector
+     * as a base vector.
+     */
+    public static final String INVALID_BASE = "Invalid base vector: norm is 
zero";
+
+    /** Serializable version identifier. */
+    private static final long serialVersionUID = 20180903L;
+
+    /**
+     * Simple constructor, using the default error message.
+     */
+    public ZeroNormException() {
+        this(ZERO_NORM_MSG);
+    }
+
+    /**
+     * Constructs an instance with the given error message.
+     * @param msg error message
+     */
+    public ZeroNormException(String msg) {
+        super(msg);
+    }
+}
diff --git 
a/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/package-info.java
 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/package-info.java
new file mode 100644
index 0000000..e70a48b
--- /dev/null
+++ 
b/commons-geometry-euclidean/src/main/java/org/apache/commons/geometry/euclidean/internal/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * 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 Euclidean geometry utilities and classes intended
+ * for internal use only. No guarantees are made for the stability of the
+ * contained APIs.
+ * </p>
+ */
+package org.apache.commons.geometry.euclidean.internal;
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 be97a91..68fec66 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
@@ -16,9 +16,11 @@
  */
 package org.apache.commons.geometry.euclidean.oned;
 
+import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.core.util.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
+import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in one-dimensional Euclidean space.
@@ -51,9 +53,6 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
     /** Serializable UID. */
     private static final long serialVersionUID = 20180710L;
 
-    /** Error message when a norm is zero. */
-    private static final String ZERO_NORM_MSG = "Norm is zero";
-
     /** Simple constructor.
      * @param x abscissa (coordinate value)
      */
@@ -125,7 +124,7 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
         else if (x < 0.0) {
             return new Vector1D(-magnitude);
         }
-        throw new IllegalStateException(ZERO_NORM_MSG);
+        throw new ZeroNormException();
     }
 
     /** {@inheritDoc} */
@@ -168,7 +167,7 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
         else if (x < 0.0) {
             return MINUS_ONE;
         }
-        throw new IllegalStateException(ZERO_NORM_MSG);
+        throw new ZeroNormException();
     }
 
     /** {@inheritDoc} */
@@ -207,6 +206,44 @@ public final class Vector1D extends Cartesian1D implements 
EuclideanVector<Point
         return getX() * v.getX();
     }
 
+    /** {@inheritDoc}
+     * <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);
+        }
+        return this;
+    }
+
+    /** {@inheritDoc}
+     * <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);
+        }
+        return Vector1D.ZERO;
+    }
+
+    /** {@inheritDoc}
+     * <p>For the one-dimensional case, this method returns 0 if the vector x 
values have
+     * the same sign and {@code pi} if they are opposite.</p>
+     */
+    @Override
+    public double angle(final Vector1D v) throws IllegalStateException {
+        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;
+    }
+
     /**
      * Get a hashCode for the vector.
      * <p>All NaN values have the same hash code.</p>
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 a24129c..272d5c0 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
@@ -94,7 +94,7 @@ public class Rotation implements Serializable {
   public static final Rotation IDENTITY = new Rotation(1.0, 0.0, 0.0, 0.0, 
false);
 
   /** Serializable version identifier */
-  private static final long serialVersionUID = -2153622329907944313L;
+  private static final long serialVersionUID = 20180903L;
 
   /** Error message for Cardan angle singularities */
   private static final String CARDAN_SINGULARITY_MSG = "Cardan angles 
singularity";
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 640f232..2a1a5f7 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,16 +16,16 @@
  */
 package org.apache.commons.geometry.euclidean.threed;
 
-import org.apache.commons.geometry.core.MultiDimensionalVector;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.core.util.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
+import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in three-dimensional Euclidean space.
  * Instances of this class are guaranteed to be immutable.
  */
-public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point3D, Vector3D>, MultiDimensionalVector<Vector3D> {
+public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point3D, Vector3D> {
 
     /** Zero (null) vector (coordinates: 0, 0, 0). */
     public static final Vector3D ZERO   = new Vector3D(0, 0, 0);
@@ -389,7 +389,7 @@ public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point
     private double getNonZeroNorm() throws IllegalStateException {
         final double n = getNorm();
         if (n == 0) {
-            throw new IllegalStateException("Norm is zero");
+            throw new ZeroNormException();
         }
 
         return n;
@@ -411,7 +411,7 @@ public final class Vector3D extends Cartesian3D implements 
EuclideanVector<Point
 
         final double baseMagSq = base.getNormSq();
         if (baseMagSq == 0.0) {
-            throw new IllegalStateException("Invalid base vector: norm is 
zero");
+            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
         }
 
         final double scale = aDotB / baseMagSq;
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 c94b52d..60877cb 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,16 +16,16 @@
  */
 package org.apache.commons.geometry.euclidean.twod;
 
-import org.apache.commons.geometry.core.MultiDimensionalVector;
 import org.apache.commons.geometry.core.internal.SimpleTupleFormat;
 import org.apache.commons.geometry.core.util.Vectors;
 import org.apache.commons.geometry.euclidean.EuclideanVector;
+import org.apache.commons.geometry.euclidean.internal.ZeroNormException;
 import org.apache.commons.numbers.arrays.LinearCombination;
 
 /** This class represents a vector in two-dimensional Euclidean space.
  * Instances of this class are guaranteed to be immutable.
  */
-public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point2D, Vector2D>, MultiDimensionalVector<Vector2D> {
+public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point2D, Vector2D> {
 
     /** Zero vector (coordinates: 0, 0). */
     public static final Vector2D ZERO   = new Vector2D(0, 0);
@@ -335,7 +335,7 @@ public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point
     private double getNonZeroNorm() throws IllegalStateException {
         final double n = getNorm();
         if (n == 0) {
-            throw new IllegalStateException("Norm is zero");
+            throw new ZeroNormException();
         }
 
         return n;
@@ -357,7 +357,7 @@ public final class Vector2D extends Cartesian2D implements 
EuclideanVector<Point
 
         final double baseMagSq = base.getNormSq();
         if (baseMagSq == 0.0) {
-            throw new IllegalStateException("Invalid base vector: norm is 
zero");
+            throw new ZeroNormException(ZeroNormException.INVALID_BASE);
         }
 
         final double scale = aDotB / baseMagSq;
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 14b40b7..20396fe 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
@@ -18,6 +18,7 @@ package org.apache.commons.geometry.euclidean.oned;
 
 import java.util.regex.Pattern;
 
+import org.apache.commons.geometry.core.Geometry;
 import org.apache.commons.numbers.core.Precision;
 import org.junit.Assert;
 import org.junit.Test;
@@ -292,6 +293,102 @@ public class Vector1DTest {
     }
 
     @Test
+    public void testProject() {
+        // arrange
+        Vector1D v1 = Vector1D.of(2);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(4);
+
+        // act/assert
+        checkVector(Vector1D.ZERO.project(v1), 0);
+        checkVector(Vector1D.ZERO.project(v2), 0);
+        checkVector(Vector1D.ZERO.project(v3), 0);
+
+        checkVector(v1.project(v1), 2);
+        checkVector(v1.project(v2), 2);
+        checkVector(v1.project(v3), 2);
+
+        checkVector(v2.project(v1), -3);
+        checkVector(v2.project(v2), -3);
+        checkVector(v2.project(v3), -3);
+
+        checkVector(v3.project(v1), 4);
+        checkVector(v3.project(v2), 4);
+        checkVector(v3.project(v3), 4);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testProject_baseHasZeroNorm() {
+        // act/assert
+        Vector1D.of(2.0).project(Vector1D.ZERO);
+    }
+
+    @Test
+    public void testReject() {
+        // arrange
+        Vector1D v1 = Vector1D.of(2);
+        Vector1D v2 = Vector1D.of(-3);
+
+        // act/assert
+        checkVector(Vector1D.ZERO.reject(v1), 0);
+        checkVector(Vector1D.ZERO.reject(v2), 0);
+
+        checkVector(v1.reject(v1), 0);
+        checkVector(v1.reject(v2), 0);
+
+        checkVector(v2.reject(v1), 0);
+        checkVector(v2.reject(v2), 0);
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testReject_baseHasZeroNorm() {
+        // act/assert
+        Vector1D.of(2.0).reject(Vector1D.ZERO);
+    }
+
+    @Test
+    public void testAngle() {
+        // arrange
+        Vector1D v1 = Vector1D.of(2);
+        Vector1D v2 = Vector1D.of(-3);
+        Vector1D v3 = Vector1D.of(4);
+        Vector1D v4 = Vector1D.of(-5);
+
+        // act/assert
+        Assert.assertEquals(0.0, v1.angle(v1), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v1.angle(v2), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, v1.angle(v3), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v1.angle(v4), TEST_TOLERANCE);
+
+        Assert.assertEquals(Geometry.PI, v2.angle(v1), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, v2.angle(v2), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v2.angle(v3), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, v2.angle(v4), TEST_TOLERANCE);
+
+        Assert.assertEquals(0.0, v3.angle(v1), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v3.angle(v2), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, v3.angle(v3), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v3.angle(v4), TEST_TOLERANCE);
+
+        Assert.assertEquals(Geometry.PI, v4.angle(v1), TEST_TOLERANCE);
+        Assert.assertEquals(0.0, v4.angle(v2), TEST_TOLERANCE);
+        Assert.assertEquals(Geometry.PI, v4.angle(v3), TEST_TOLERANCE);
+        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 testLerp() {
         // arrange
         Vector1D v1 = Vector1D.of(1);

Reply via email to