package ibis.ipl.support.vivaldi;
import java.io.IOException;
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class representing a coordinate in a Cartesian space. Code copied from
* Zorilla
*
* @author Niels Drost
*
*/
public final class Coordinates implements Serializable {
private static final Logger logger = LoggerFactory.getLogger(Coordinates.class);
// nodes closer than this push each other off in some random direction
public static final double CLOSEBY_THRESHOLD = 0.01;
public static final double COORDINATE_CONTROL = 0.1;
public static final double ERROR_CONTROL = 0.1;
public static final int DIMENSIONS = 3;
public static final int DOUBLE_SIZE = 8;
public static final int LONG_SIZE = 8;
public static final int SIZE = DOUBLE_SIZE + (DOUBLE_SIZE * DIMENSIONS);
private static final long serialVersionUID = 0L;
private final double error;
private final double[] coordinates;
private static double magnitude(double[] vector) {
double total = 0;
for (int i = 0; i < DIMENSIONS; i++) {
total += (vector[i] * vector[i]);
}
return Math.sqrt(total);
}
private static double[] subtract(double[] one, double[] other) {
double[] result = new double[DIMENSIONS];
for (int i = 0; i < DIMENSIONS; i++) {
result[i] = one[i] - other[i];
}
return result;
}
private static double[] add(double[] one, double[] other) {
double[] result = new double[DIMENSIONS];
for (int i = 0; i < DIMENSIONS; i++) {
result[i] = one[i] + other[i];
}
return result;
}
private static double[] mult(double scaler, double[] vector) {
double[] result = new double[DIMENSIONS];
for (int i = 0; i < DIMENSIONS; i++) {
result[i] = scaler * vector[i];
}
return result;
}
private static double[] randomVector() {
logger.debug("randomness commencing!");
double[] result = new double[DIMENSIONS];
for (int i = 0; i < DIMENSIONS; i++) {
result[i] = Math.random();
if (Math.random() > 0.5) {
result[i] *= -1;
}
}
return result;
}
private static double[] unitVector(double[] vector) {
double magnitude = magnitude(vector);
// coordinates which are too close push each other off
while (magnitude < CLOSEBY_THRESHOLD) {
vector = randomVector();
magnitude = magnitude(vector);
}
double[] result = mult(1 / magnitude, vector);
logger.debug("unitVector(" + toString(vector) + ") = "
+ toString(result));
magnitude = magnitude(result);
if (magnitude < 0.99 || magnitude > 1.01) {
logger.error("magnitude of unit vector " + toString(result)
+ " of vector " + toString(vector)
+ " should be 1, but is: " + magnitude(result));
}
return result;
}
private final void int2byte(int src, byte[] dst, int off) {
dst[off + 0] = (byte) (0xff & (src >> 24));
dst[off + 1] = (byte) (0xff & (src >> 16));
dst[off + 2] = (byte) (0xff & (src >> 8));
dst[off + 3] = (byte) (0xff & src);
}
private final int byte2int(byte[] src, int off) {
return (((src[off + 3] & 0xff) << 0) | ((src[off + 2] & 0xff) << 8)
| ((src[off + 1] & 0xff) << 16) | ((src[off + 0] & 0xff) << 24));
}
private final void long2byte(long src, byte[] dst, int off) {
int v1 = (int) (src >> 32);
int v2 = (int) (src);
int2byte(v1, dst, off);
int2byte(v2, dst, off + 4);
}
private final long byte2long(byte[] src, int off) {
int t1, t2;
t1 = byte2int(src, off);
t2 = byte2int(src, off + 4);
return ((((long) t1) << 32) | (t2 & 0xffffffffL));
}
/*
* Creates an "unknown" coordinate.
*/
public Coordinates() {
error = 1.0; // no confidence, high error
// initiate coordinates on some random coordinate (within 0.0 to 1.0)
coordinates = randomVector();
}
public Coordinates(double[] coordinates, double error) {
this.coordinates = coordinates.clone();
this.error = error;
}
public Coordinates(byte[] bytes) throws IOException {
if (bytes.length != SIZE) {
throw new IOException("received coordinates of unknown length");
}
long errorBits = byte2long(bytes, 0);
error = Double.longBitsToDouble(errorBits);
coordinates = new double[DIMENSIONS];
for (int i = 0; i < DIMENSIONS; i++) {
long coordinateBits = byte2long(bytes, LONG_SIZE * (i + 1));
coordinates[i] = Double.longBitsToDouble(coordinateBits);
}
}
public byte[] toBytes() {
byte[] bytes = new byte[SIZE];
long errorBits = Double.doubleToLongBits(error);
long2byte(errorBits, bytes, 0);
for (int i = 0; i < DIMENSIONS; i++) {
long coordinateBits = Double.doubleToLongBits(coordinates[i]);
long2byte(coordinateBits, bytes, LONG_SIZE * (i + 1));
}
return bytes;
}
public double distance(Coordinates other) {
return magnitude(subtract(this.coordinates, other.coordinates));
}
/**
* Updates coordinate with new information...
*/
public Coordinates update(Coordinates remoteCoordinates, double rtt) {
logger.debug("updating " + this + " with " + remoteCoordinates
+ " at distance " + rtt);
double newError;
double[] newCoordinates;
double weight = error / (error + remoteCoordinates.error);
logger.debug("weight = " + weight);
double distance = distance(remoteCoordinates);
double sampleError = Math.abs(distance - rtt) / rtt;
logger.debug("sample error = " + sampleError);
newError = sampleError * ERROR_CONTROL * weight + error
* (1 - ERROR_CONTROL * weight);
if (newError > 1.0) {
newError = 1.0;
}
logger.debug("new error = " + newError);
double step = COORDINATE_CONTROL * weight;
logger.debug("step = " + step);
double[] unitVector = unitVector(subtract(coordinates,
remoteCoordinates.coordinates));
logger.debug("unit vector = " + toString(unitVector));
double[] scaled = mult(step * (rtt - distance(remoteCoordinates)),
unitVector);
logger.debug("scaled = " + toString(scaled));
newCoordinates = add(coordinates, scaled);
logger.debug("new coordinates = " + toString(newCoordinates));
return new Coordinates(newCoordinates, newError);
}
private static String prettyPrint(double number) {
return String.format("% 8.2f", number);
}
private static String toString(double[] vector) {
String result = "[";
for (int i = 0; i < DIMENSIONS - 1; i++) {
result += prettyPrint(vector[i]) + ", ";
}
result += prettyPrint(vector[DIMENSIONS - 1]) + "]";
return result;
}
public double getError() {
return error;
}
public double[] getCoordinates() {
return coordinates.clone();
}
public boolean isOrigin() {
for (int i = 0; i < DIMENSIONS; i++) {
if (coordinates[i] != 0) {
return false;
}
}
return true;
}
public String toString() {
return toString(coordinates) + " error = " + prettyPrint(error);
}
}