From 2ddf9fcd4e81a2e4f5e768b4be88bbe02f66b85b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Willi=20Sch=C3=B6nborn?= Date: Tue, 19 Apr 2022 14:07:58 +0200 Subject: [PATCH] Added equals/hashCode contract to Vector2D/Vector3D --- .../kmath/geometry/Euclidean2DSpace.kt | 24 ++++++++- .../kmath/geometry/Euclidean3DSpace.kt | 24 ++++++++- .../kscience/kmath/geometry/Vector2DTest.kt | 50 +++++++++++++++++ .../kscience/kmath/geometry/Vector3DTest.kt | 53 +++++++++++++++++++ 4 files changed, 149 insertions(+), 2 deletions(-) diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt index d00575bcc..6b1f9c2eb 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean2DSpace.kt @@ -24,6 +24,19 @@ public interface Vector2D : Point, Vector { } override operator fun iterator(): Iterator = listOf(x, y).iterator() + + /** + * Indicates whether some other vector is "equal to" this one. + * Any [Vector3D] is considered equal, if it contains the same components. + */ + override fun equals(other: Any?): Boolean + + /** + * Returns a hash code value for this vector. + * The hash code is generated as if all the input values were placed into + * a list, and that list were hashed by calling `listOf(x, y).hashCode()`. + */ + override fun hashCode(): Int } public val Vector2D.r: Double @@ -35,7 +48,16 @@ public fun Vector2D(x: Double, y: Double): Vector2D = Vector2DImpl(x, y) private data class Vector2DImpl( override val x: Double, override val y: Double, -) : Vector2D +) : Vector2D { + + override fun equals(other: Any?) = when (other) { + this -> true + is Vector2D -> x == other.x && y == other.y + else -> false + } + + override fun hashCode(): Int = listOf(x, y).hashCode() +} /** * 2D Euclidean space diff --git a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt index e12563b46..bc991d597 100644 --- a/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt +++ b/kmath-geometry/src/commonMain/kotlin/space/kscience/kmath/geometry/Euclidean3DSpace.kt @@ -26,6 +26,19 @@ public interface Vector3D : Point, Vector { } override operator fun iterator(): Iterator = listOf(x, y, z).iterator() + + /** + * Indicates whether some other vector is "equal to" this one. + * Any [Vector3D] is considered equal, if it contains the same components. + */ + override fun equals(other: Any?): Boolean + + /** + * Returns a hash code value for this vector. + * The hash code is generated as if all the input values were placed into + * a list, and that list were hashed by calling `listOf(x, y, z).hashCode()`. + */ + override fun hashCode(): Int } @Suppress("FunctionName") @@ -37,7 +50,16 @@ private data class Vector3DImpl( override val x: Double, override val y: Double, override val z: Double, -) : Vector3D +) : Vector3D { + + override fun equals(other: Any?) = when (other) { + this -> true + is Vector3D -> x == other.x && y == other.y && z == other.z + else -> false + } + + override fun hashCode() = listOf(x, y, z).hashCode() +} public object Euclidean3DSpace : GeometrySpace, ScaleOperations { override val zero: Vector3D by lazy { Vector3D(0.0, 0.0, 0.0) } diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt index 5e45b4870..94ec7ef90 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector2DTest.kt @@ -37,4 +37,54 @@ internal class Vector2DTest { fun y() { assertEquals(-7.999, vector.y) } + + @Test + fun equality() { + val vector2 = AlternativeVector2D(1.0, -7.999) + + // reflexive + assertEquals(vector, vector) + assertEquals(vector2, vector2) + + // symmetric + assertEquals(vector, vector2) + assertEquals(vector2, vector) + + // transitive + val vector3 = AlternativeVector2D(1.0, -7.999) + assertEquals(vector, vector2) + assertEquals(vector2, vector3) + assertEquals(vector3, vector) + } + + @Test + fun hash() { + val vector2 = AlternativeVector2D(1.0, -7.999) + + assertEquals(vector, vector2) + assertEquals(vector.hashCode(), vector2.hashCode()) + } + + private data class AlternativeVector2D( + override val x: Double, + override val y: Double, + ) : Vector2D { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other !is Vector2D) return false + + if (x != other.x) return false + if (y != other.y) return false + + return true + } + + override fun hashCode(): Int { + var result = 1 + result = 31 * result + x.hashCode() + result = 31 * result + y.hashCode() + return result + } + } } diff --git a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt index 55bab4775..92fbf37aa 100644 --- a/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt +++ b/kmath-geometry/src/commonTest/kotlin/space/kscience/kmath/geometry/Vector3DTest.kt @@ -43,4 +43,57 @@ internal class Vector3DTest { fun z() { assertEquals(0.001, vector.z) } + + @Test + fun equality() { + val vector2 = AlternativeVector3D(1.0, -7.999, 0.001) + + // reflexive + assertEquals(vector, vector) + assertEquals(vector2, vector2) + + // symmetric + assertEquals(vector, vector2) + assertEquals(vector2, vector) + + // transitive + val vector3 = AlternativeVector3D(1.0, -7.999, 0.001) + assertEquals(vector, vector2) + assertEquals(vector2, vector3) + assertEquals(vector3, vector) + } + + @Test + fun hash() { + val vector2 = AlternativeVector3D(1.0, -7.999, 0.001) + + assertEquals(vector, vector2) + assertEquals(vector.hashCode(), vector2.hashCode()) + } + + private data class AlternativeVector3D( + override val x: Double, + override val y: Double, + override val z: Double, + ) : Vector3D { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || other !is Vector3D) return false + + if (x != other.x) return false + if (y != other.y) return false + if (z != other.z) return false + + return true + } + + override fun hashCode(): Int { + var result = 1 + result = 31 * result + x.hashCode() + result = 31 * result + y.hashCode() + result = 31 * result + z.hashCode() + return result + } + } }