/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.node; import freenet.support.Logger; /** * @author amphibian * * Location of a node in the circular keyspace. ~= specialization. * Any number between 0.0 and 1.0 (inclusive) is considered a valid location. */ public class Location { public static final double LOCATION_INVALID = -1.0; /** * Parses a location. * @param init a location string * @return the location, or LOCATION_INVALID for all invalid locations and on parse errors. */ public static double getLocation(String init) { try { if (init == null) { return LOCATION_INVALID; } double d = Double.parseDouble(init); if (!isValid(d)) { return LOCATION_INVALID; } return d; } catch (NumberFormatException e) { return LOCATION_INVALID; } } /** * Distance between a peer and a location. * @param p a peer with a valid location * @param loc a valid location * @return the absolute distance between the peer and the location in the circular location space. */ public static double distance(PeerNode p, double loc) { return distance(p.getLocation(), loc); } /** * Distance between two valid locations. * @param a a valid location * @param b a valid location * @return the absolute distance between the locations in the circular location space. */ public static double distance(double a, double b) { if (!isValid(a) || !isValid(b)) { String errMsg = "Invalid Location ! a = " + a + " b = " + b + " Please report this bug!"; Logger.error(Location.class, errMsg, new Exception("error")); throw new IllegalArgumentException(errMsg); } return simpleDistance(a, b); } /** * Distance between two potentially invalid locations. * @param a a valid location * @param b a valid location * @return the absolute distance between the locations in the circular location space. * Invalid locations are considered to be at 2.0, and the result is returned accordingly. */ public static double distanceAllowInvalid(double a, double b) { if (!isValid(a) && !isValid(b)) { return 0.0; // Both are out of range so both are equal. } if (!isValid(a)) { return 2.0 - b; } if (!isValid(b)) { return 2.0 - a; } // Both values are valid. return simpleDistance(a, b); } /** * Distance between two valid locations without bounds check. * The behaviour is undefined for invalid locations. * @param a a valid location * @param b a valid location * @return the absolute distance between the two locations in the circular location space. */ private static double simpleDistance(double a, double b) { return Math.abs(change(a, b)); } /** * Distance between two locations, including direction of the change (positive/negative). * When given two values on opposite ends of the keyspace, it will return +0.5. * The behaviour is undefined for invalid locations. * @param from a valid starting location * @param to a valid end location * @return the signed distance from the first to the second location in the circular location space */ public static double change(double from, double to) { double change = to - from; if (change > 0.5) { return change - 1.0; } if (change <= -0.5) { return change + 1.0; } return change; } /** * Normalize a location to within the valid range. * Given an arbitrary double (not bound to [0.0, 1.0)) return the normalized double [0.0, 1.0) which would result in simple * wrapping/overflowing. e.g. normalize(0.3+1.0)==0.3, normalize(0.3-1.0)==0.3, normalize(x)==x with x in [0.0, 1.0) * @bug: if given double has wrapped too many times, the return value may be not be precise. * @param rough any location * @return the normalized location */ public static double normalize(double rough) { double normal = rough % 1.0; if (normal < 0) { return 1.0 + normal; } return normal; } /** * Tests for equality of two locations. * Locations are considered equal if their distance is (almost) zero, e.g. equals(0.0, 1.0) is true, * or if both locations are invalid. * @param a any location * @param b any location * @return whether the two locations are considered equal */ public static boolean equals(double a, double b) { return distanceAllowInvalid(a, b) < Double.MIN_VALUE * 2; } /** * Tests whether a location is valid, e.g. within [0.0, 1.0] * @param loc any location * @return whether the location is valid */ public static boolean isValid(double loc) { return loc >= 0.0 && loc <= 1.0; } }