001package squidpony.squidmath;
002
003import squidpony.StringKit;
004
005import java.io.Serializable;
006
007/**
008 * A UUID-like identifier; not compatible with Java's standard UUID but will work on GWT.
009 * <br>
010 * Meant to be used as an identity type for things like SpatialMap, especially when no special game-specific logic is
011 * needed for identities.
012 * Created by Tommy Ettinger on 4/30/2016.
013 */
014public class SquidID implements Serializable, Comparable<SquidID> {
015    private static final long serialVersionUID = 8946534790126874460L;
016    private static LongPeriodRNG rng = new LongPeriodRNG();
017    public final long low, high;
018
019    /**
020     * Constructs a new random SquidID. If you want different random IDs with every run, the defaults should be fine.
021     * If you want stable IDs to be generated, use SquidID.stabilize(), but be careful about collisions!
022     */
023    public SquidID() {
024        low = rng.nextLong();
025        high = rng.nextLong();
026    }
027
028    /**
029     * Constructs a fixed SquidID with the given low and high 64-bit longs.
030     * @param low the least-significant bits of the ID
031     * @param high the most-significant bits of the ID
032     */
033    public SquidID(long low, long high) {
034        this.low = low;
035        this.high = high;
036    }
037
038    /**
039     * Gets a new random SquidID, the same as calling the no-argument constructor.
040     * The name is for compatibility with Java's standard UUID class.
041     * @return a newly-constructed random SquidID.
042     */
043    public static SquidID randomUUID()
044    {
045        return new SquidID();
046    }
047
048    /**
049     * Makes the IDs generated after calling this repeatable, with the same IDs generated in order after this is called.
050     * This class uses a random number generator with a random seed by default to produce IDs, and properties of the
051     * LongPeriodRNG this uses make it incredibly unlikely that IDs will repeat even if the game was run for the rest
052     * of your natural lifespan. For the purposes of tests, you may want stable SquidID values to be generated, the same
053     * for every startup of the program, generating the same IDs in order. This will change the seed used internally to
054     * a constant (large) seed the first time it is called, and it should only be called at or near the start of your
055     * program, no more than once. If an ID is requested immediately after calling this method, and then this method is
056     * called again, the next ID to be generated will be identical to the previous one generated (a collision). There
057     * may be reasons you want this during testing, so there isn't any check for multiple calls to this method. If IDs
058     * can persist between runs of the game (i.e. saved in a file), using this is generally a bad idea, and the default
059     * random IDs should more than suffice.
060     * <br>
061     * You can "undo" the effects of this method with randomize(), changing the seed to a new random value.
062     * <br>
063     * Because IDs aren't likely to have gameplay significance, this uses one seed, the opening paragraph of The
064     * Wonderful Wizard of Oz, by Frank L. Baum, which is in the public domain. Changing the seed is unlikely to change
065     * the likelihood of collisions, which should be less likely than a tornado transporting you to Oz, as long as this
066     * method is called at most once per program run.
067     */
068    public static void stabilize()
069    {
070        rng.reseed(
071                "Dorothy lived in the midst of the great Kansas prairies, with Uncle Henry, who was a "+
072                        "farmer, and Aunt Em, who was the farmer's wife. Their house was small, for the "+
073                        "lumber to build it had to be carried by wagon many miles. There were four walls, "+
074                        "a floor and a roof, which made one room; and this room contained a rusty looking "+
075                        "cookstove, a cupboard for the dishes, a table, three or four chairs, and the beds."+
076                        " Uncle Henry and Aunt Em had a big bed in one corner, and Dorothy a little bed in "+
077                        "another corner. There was no garret at all, and no cellar—except a small hole dug "+
078                        "in the ground, called a cyclone cellar, where the family could go in case one of "+
079                        "those great whirlwinds arose, mighty enough to crush any building in its path. It "+
080                        "was reached by a trap door in the middle of the floor, from which a ladder led "+
081                        "down into the small, dark hole.");
082    }
083
084    /**
085     * Makes the IDs generated after calling this non-repeatable, with a random 1024-bit seed.
086     * This class uses a random number generator with a random seed by default to produce IDs, and properties of the
087     * LongPeriodRNG this uses make it incredibly unlikely that IDs will repeat even if the game was run for the rest
088     * of your natural lifespan. However, if you call stabilize(), generate some IDs, call stabilize() again, and
089     * generate some more IDs, the first, second, third, etc. IDs generated after each call will be identical -- hardly
090     * the unique ID you usually want. You can "undo" the effects of stabilize by calling this method, making the seed
091     * a new random value. This does not affect the constructor that takes two longs to produce an exact ID, nor will
092     * it change any IDs.
093     */
094    public static void randomize()
095    {
096        rng.reseed();
097    }
098
099    /**
100     * Gets the least-significant bits, also accessible by the field low.
101     * The name is for compatibility with Java's standard UUID class.
102     * @return the least-significant bits as a long
103     */
104    public long getLeastSignificantBits()
105    {
106        return low;
107    }
108
109    /**
110     * Gets the most-significant bits, also accessible by the field high.
111     * The name is for compatibility with Java's standard UUID class.
112     * @return the most-significant bits as a long
113     */
114    public long getMostSignificantBits()
115    {
116        return high;
117    }
118
119    @Override
120    public boolean equals(Object o) {
121        if (this == o) return true;
122        if (o == null || getClass() != o.getClass()) return false;
123
124        SquidID squidID = (SquidID) o;
125
126        if (low != squidID.low) return false;
127        return high == squidID.high;
128
129    }
130
131    @Override
132    public int hashCode() {
133        int result = (int) (low ^ (low >>> 32));
134        result = 31 * result + (int) (high ^ (high >>> 32));
135        return result;
136    }
137
138    @Override
139    public String toString()
140    {
141        return StringKit.hex(high) + '-' + StringKit.hex(low);
142    }
143
144    @Override
145    public int compareTo(SquidID o) {
146        if(o == null)
147            return 1;
148        long diff = high - o.high;
149        if(diff == 0)
150            diff = low - o.low;
151        return diff > 0 ? 1 : diff < 0 ? -1 : 0;
152    }
153}