[
https://issues.apache.org/jira/browse/GEOMETRY-162?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Ivan Shuba updated GEOMETRY-162:
--------------------------------
Description:
h1. Overview
This issue is related to that the same value of hash code is returned where it
is expected to be different for non-identical instances of `Vector2D` and
`Vector3D` classes.
In particular, for 2D case, if we generate a pair of vectors which are
symmetrical about the X, or Y coordinate axes, then both such vectors will
return same hash code value. For 3D case, the hash code seems to be equal only
if the vectors are symmetrical about YZ plane.
h2. Examples
The following examples illustrate the problem and can be easily reproduced.
h4. The Vector2D case, symmetrical about Y axis
For example, if we have a following pair of collinear `Vector2D` instances:
{code:java}
Vector2D a = Vector2D.of(-10.0, 0); //400556032
Vector2D b = Vector2D.of(+10.0, 0); //400556032 {code}
Then they both will return a value of 400556032 if the `hashCode()` method is
called.
The same will happen if we make non-collinear pair of symmetrical vectors (
with non-zero value of Y coordinate ):
{code:java}
Vector2D a = Vector2D.of(-20.0, 10.0); //-326631424
Vector2D b = Vector2D.of(+20.0, 10.0); //-326631424 {code}
h4. The Vector2D case, symmetrical about X axis
The same holds if we consider a pair of vectors symmetrical about X axis
{code:java}
Vector2D a = Vector2D.of(10.0, -30.0); //-1251213312
Vector2D b = Vector2D.of(10.0, +30.0); //-1251213312 {code}
Even if we generate arbitrary random number to test the issue, the vectors will
still return the same hash code value.
{code:java}
final double ANY = Math.random();
int hashA = Vector2D.of(10.0, ANY);
int hashB = Vector2D.of(10.0, ANY);
Assertions.assertNotEquals(hashA, hashB); // fails {code}
h4. The Vector3D case
For Vector3D class, the issue seems to arise only if the instances are
symmetrical about YZ plane.
{code:java}
final double ANY = Math.random();
final double NEG = -ANY;
final double POS = +ANY;
int negX = Vector3D.of(NEG, 0.0, 0.0);
int posX = Vector3D.of(POS, 0.0, 0.0);
int negY = Vector3D.of(0.0, NEG, 0.0);int posY = Vector3D.of(0.0, POS, 0.0);
int negZ = Vector3D.of(0.0, 0.0, NEG);
int posZ = Vector3D.of(0.0, 0.0, POS);
Assertions.assertNotEquals(negX, posX); // fails
Assertions.assertNotEquals(negY, posY); // passes
Assertions.assertNotEquals(negZ, posZ); // passes {code}
h2. Possible solution
To avoid clashes one could use modified version of the `hashCode` method.
Here's one for Vector3D class:
{code:java}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits;
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits;
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(z);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
} {code}
P.S. I've prepared a couple of simple unit tests in `Vector2DTest` and
`Vector3DTest` classes illustrating the issue.
was:
h1. Overview
This issue is related to that the same value of hash code is returned where it
is expected to be different for non-identical instances of `Vector2D` and
`Vector3D` classes.
In particular, for 2D case, if we generate a pair of vectors which are
symmetrical about the X, or Y coordinate axes, then both such vectors will
return same hash code value. For 3D case, the hash code seems to be equal only
if the vectors are symmetrical about YZ plane.
h2. Examples
The following examples illustrate the problem and can be easily reproduced.
h4. The Vector2D case, symmetrical about Y axis
For example, if we have a following pair of collinear `Vector2D` instances:
{{Vector2D a = Vector2D.of(-10.0, 0); //400556032}}
{{Vector2D b = Vector2D.of(+10.0, 0); //400556032}}
Then they both will return a value of 400556032 if the `hashCode()` method is
called.
The same will happen if we make non-collinear pair of symmetrical vectors (
with non-zero value of Y coordinate ):
{{Vector2D a = Vector2D.of(-20.0, 10.0); //-326631424}}
{{Vector2D b = Vector2D.of(+20.0, 10.0); //-326631424}}
h4. The Vector2D case, symmetrical about X axis
The same holds if we consider a pair of vectors symmetrical about X axis
{{Vector2D a = Vector2D.of(10.0, -30.0); //-1251213312}}
{{Vector2D b = Vector2D.of(10.0, +30.0); //-1251213312}}
Even if we generate arbitrary random number to test the issue, the vectors will
still return the same hash code value.
final double ANY = Math.random();
{{int hashA = Vector2D.of(10.0, ANY); }}
{{int hashB = Vector2D.of(10.0, ANY); }}
{{Assertions.assertNotEquals(hashA, hashB); // fails}}
h4. The Vector3D case
For Vector3D class, the issue seems to arise only if the instances are
symmetrical about YZ plane.
{{final double ANY = Math.random();}}
{{final double NEG = -ANY;}}
{{final double POS = +ANY;}}
{{int negX = Vector3D.of(NEG, 0.0, 0.0);}}
{{int posX = Vector3D.of(POS, 0.0, 0.0);}}
int negY = Vector3D.of(0.0, NEG, 0.0);{{int posY = Vector3D.of(0.0, POS, 0.0);}}
{{int negZ = Vector3D.of(0.0, 0.0, NEG);}}
{{int posZ = Vector3D.of(0.0, 0.0, POS);}}
{{Assertions.assertNotEquals(negX, posX); // fails}}
{{Assertions.assertNotEquals(negY, posY); // passes}}
{{Assertions.assertNotEquals(negZ, posZ); // passes}}
h2. Possible solution
To avoid clashes one could use modified version of the `hashCode` method.
Here's one for Vector3D class:
{{@Override}}
{{public int hashCode() {}}
{{ int result;}}
{{ long temp;}}
{{ temp = Double.doubleToLongBits(x);}}
{{ result = (int) (temp ^ (temp >>> 32));}}
{{ temp = Double.doubleToLongBits(y);}}
{{ result = 31 * result + (int) (temp ^ (temp >>> 32));}}
{{ temp = Double.doubleToLongBits(z);}}
{{ result = 31 * result + (int) (temp ^ (temp >>> 32));}}
{{ return result;}}
{{}}}
P.S. I've prepared a couple of simple unit tests in `Vector2DTest` and
`Vector3DTest` classes illustrating the issue.
> Hash collision for vectors symmetrical about coordinate hyperplanes
> -------------------------------------------------------------------
>
> Key: GEOMETRY-162
> URL: https://issues.apache.org/jira/browse/GEOMETRY-162
> Project: Commons Geometry
> Issue Type: Bug
> Components: euclidean2D, euclidean3D
> Reporter: Ivan Shuba
> Priority: Minor
> Labels: Bug
>
> h1. Overview
> This issue is related to that the same value of hash code is returned where
> it is expected to be different for non-identical instances of `Vector2D` and
> `Vector3D` classes.
> In particular, for 2D case, if we generate a pair of vectors which are
> symmetrical about the X, or Y coordinate axes, then both such vectors will
> return same hash code value. For 3D case, the hash code seems to be equal
> only if the vectors are symmetrical about YZ plane.
> h2. Examples
> The following examples illustrate the problem and can be easily reproduced.
> h4. The Vector2D case, symmetrical about Y axis
> For example, if we have a following pair of collinear `Vector2D` instances:
> {code:java}
> Vector2D a = Vector2D.of(-10.0, 0); //400556032
> Vector2D b = Vector2D.of(+10.0, 0); //400556032 {code}
> Then they both will return a value of 400556032 if the `hashCode()` method is
> called.
> The same will happen if we make non-collinear pair of symmetrical vectors (
> with non-zero value of Y coordinate ):
> {code:java}
> Vector2D a = Vector2D.of(-20.0, 10.0); //-326631424
> Vector2D b = Vector2D.of(+20.0, 10.0); //-326631424 {code}
> h4. The Vector2D case, symmetrical about X axis
> The same holds if we consider a pair of vectors symmetrical about X axis
> {code:java}
> Vector2D a = Vector2D.of(10.0, -30.0); //-1251213312
> Vector2D b = Vector2D.of(10.0, +30.0); //-1251213312 {code}
> Even if we generate arbitrary random number to test the issue, the vectors
> will still return the same hash code value.
> {code:java}
> final double ANY = Math.random();
> int hashA = Vector2D.of(10.0, ANY);
> int hashB = Vector2D.of(10.0, ANY);
> Assertions.assertNotEquals(hashA, hashB); // fails {code}
> h4. The Vector3D case
> For Vector3D class, the issue seems to arise only if the instances are
> symmetrical about YZ plane.
> {code:java}
> final double ANY = Math.random();
> final double NEG = -ANY;
> final double POS = +ANY;
> int negX = Vector3D.of(NEG, 0.0, 0.0);
> int posX = Vector3D.of(POS, 0.0, 0.0);
> int negY = Vector3D.of(0.0, NEG, 0.0);int posY = Vector3D.of(0.0, POS, 0.0);
> int negZ = Vector3D.of(0.0, 0.0, NEG);
> int posZ = Vector3D.of(0.0, 0.0, POS);
> Assertions.assertNotEquals(negX, posX); // fails
> Assertions.assertNotEquals(negY, posY); // passes
> Assertions.assertNotEquals(negZ, posZ); // passes {code}
> h2. Possible solution
> To avoid clashes one could use modified version of the `hashCode` method.
> Here's one for Vector3D class:
>
> {code:java}
> @Override
> public int hashCode() {
> int result;
> long temp;
> temp = Double.doubleToLongBits;
> result = (int) (temp ^ (temp >>> 32));
> temp = Double.doubleToLongBits;
> result = 31 * result + (int) (temp ^ (temp >>> 32));
> temp = Double.doubleToLongBits(z);
> result = 31 * result + (int) (temp ^ (temp >>> 32));
> return result;
> } {code}
>
> P.S. I've prepared a couple of simple unit tests in `Vector2DTest` and
> `Vector3DTest` classes illustrating the issue.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)