/*
Copyright 2006 by Sean Luke and George Mason University
Licensed under the Academic Free License version 3.0
See the file "LICENSE" for more information
*/
package sim.util;
/**
Int3D stores three values (x, y, and z) but it is immutable: once the x and y and z values are set, they cannot be changed (they're final). Like the others, Int3D is immutable primarily to prevent hash tables from breaking.
<p>One day in the far future, Int3D should also be HIGHLY efficient; since it is immutable, it can be passed by value rather than by pointer by a smart compiler. Not today, though. But it's not bad.
<p>This class has an elaborate hash code generation that is much more random than Sun's standard generator, but takes more time. For very large numbers of objects, this is a good idea, but we may change it to a simpler version in the future.
<p>Int3D.equals(...) can compare by value against other Int3Ds, MutableInt2Ds, and Double3Ds.
*/
public final class Int3D implements java.io.Serializable
{
private static final long serialVersionUID = 1;
public final int x;
public final int y;
public final int z;
public Int3D() { x = 0; y = 0; z = 0;}
public Int3D(final int x, final int y, final int z) { this.x = x; this.y = y; this.z = z;}
/** Explicitly assumes the z value is set to 0 */
public Int3D(final Int2D p) { x = p.x; y = p.y; z = 0; }
public Int3D(final Int2D p, final int z) { x = p.x; y = p.y; this.z = z; }
public Int3D(final MutableInt2D p) { x = p.x; y = p.y; z = 0; }
public Int3D(final MutableInt2D p, final int z) { x = p.x; y = p.y; this.z = z; }
public final int getX() { return x; }
public final int getY() { return y; }
public final int getZ() { return z; }
public String toString() { return "Int3D["+x+","+y+","+z+"]"; }
public String toCoordinates() { return "(" + x + ", " + y + ", " + z + ")"; }
public int hashCode()
{
int z = this.z;
// basically we need to randomly disperse <int,int,int> --> int
// I do that as x ^ hash(y ^ hash(z) + 17 [or whatever]). Hash function
// taken from http://www.cris.com/~Ttwang/tech/inthash.htm
// Some further discussion. Sun's moved to a new hash table scheme
// which has (of all things!) tables with lengths that are powers of two!
// Normally hash table lengths should be prime numbers, in order to
// compensate for bad hashcodes. To fix matters, Sun now is
// pre-shuffling the hashcodes with the following algorithm (which
// is short but not too bad -- should we adopt it? Dunno). See
// http://developer.java.sun.com/developer/bugParade/bugs/4669519.html
// key += ~(key << 9);
// key ^= (key >>> 14);
// key += (key << 4);
// key ^= (key >>> 10);
// This is good for us because Int2D, Int3D, Double2D, and Double3D
// have hashcodes well distributed with regard to y and z, but when
// you mix in x, they're just linear in x. We could do a final
// shuffle I guess. In Java 1.3, they DON'T do a pre-shuffle, so
// it may be suboptimal. Since we're all moving to 1.4.x, it's not
// a big deal since 1.4.x is shuffling the final result using the
// Sun shuffler above. But I'd appreciate some tests on our method
// below, and suggestions as to whether or not we should adopt the
// shorter, likely suboptimal but faster Sun shuffler instead
// for y and z values. -- Sean
z += ~(z << 15);
z ^= (z >>> 10);
z += (z << 3);
z ^= (z >>> 6);
z += ~(z << 11);
z ^= (z >>> 16);
z ^= y;
z += 17; // a little prime number shifting -- waving a dead chicken? dunno
z += ~(z << 15);
z ^= (z >>> 10);
z += (z << 3);
z ^= (z >>> 6);
z += ~(z << 11);
z ^= (z >>> 16);
// nifty! Now mix in x
return x ^ z;
}
// can't have separate equals(...) methods as the
// argument isn't virtual
public boolean equals(final Object obj)
{
if (obj==null) return false;
else if (obj instanceof Int3D) // do Int3D first
{
Int3D other = (Int3D) obj;
return (other.x == x && other.y == y && other.z == z);
}
else if (obj instanceof MutableInt3D)
{
MutableInt3D other = (MutableInt3D) obj;
return (other.x == x && other.y == y && other.z == z);
}
else if (obj instanceof Double3D)
{
Double3D other = (Double3D) obj;
return (other.x == x && other.y == y && other.z == z);
}
else if (obj instanceof MutableDouble3D)
{
MutableDouble3D other = (MutableDouble3D) obj;
return (other.x == x && other.y == y && other.z == z);
}
else return false;
}
/** Returns the distance FROM this Int3D TO the specified point */
public double distance(final double x, final double y, final double z)
{
final double dx = (double)this.x - x;
final double dy = (double)this.y - y;
final double dz = (double)this.z - z;
return Math.sqrt(dx*dx+dy*dy+dz*dz);
}
/** Returns the distance FROM this Int3D TO the specified point. */
public double distance(final Double3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return Math.sqrt(dx*dx+dy*dy+dz*dz);
}
/** Returns the distance FROM this Int3D TO the specified point. */
public double distance(final MutableInt3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return Math.sqrt(dx*dx+dy*dy+dz*dz);
}
/** Returns the distance FROM this Int3D TO the specified point. */
public double distance(final Int3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return Math.sqrt(dx*dx+dy*dy+dz*dz);
}
/** Returns the squared distance FROM this Int3D TO the specified point */
public double distanceSq(final double x, final double y, final double z)
{
final double dx = (double)this.x - x;
final double dy = (double)this.y - y;
final double dz = (double)this.z - z;
return (dx*dx+dy*dy+dz*dz);
}
/** Returns the squared distance FROM this Int3D TO the specified point. */
public double distanceSq(final Double3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return (dx*dx+dy*dy+dz*dz);
}
/** Returns the squared distance FROM this Int3D TO the specified point. */
public double distanceSq(final MutableInt3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return (dx*dx+dy*dy+dz*dz);
}
/** Returns the squared distance FROM this Int3D TO the specified point. */
public double distanceSq(final Int3D p)
{
final double dx = (double)this.x - p.x;
final double dy = (double)this.y - p.y;
final double dz = (double)this.z - p.z;
return (dx*dx+dy*dy+dz*dz);
}
/** Returns the manhattan distance FROM this Int3D TO the specified point. */
public long manhattanDistance(final int x, final int y, final int z)
{
return Math.abs((long)this.x-x) + Math.abs((long)this.y-y) + Math.abs((long)this.z-z);
}
/** Returns the manhattan distance FROM this Int3D TO the specified point. */
public long manhattanDistance(final MutableInt3D p)
{
return Math.abs((long)this.x-p.x) + Math.abs((long)this.y-p.y) + Math.abs((long)this.z-p.z);
}
/** Returns the manhattan distance FROM this Int3D TO the specified point. */
public long manhattanDistance(final Int3D p)
{
return Math.abs((long)this.x-p.x) + Math.abs((long)this.y-p.y) + Math.abs((long)this.z-p.z);
}
}