diff --git a/Dorkbox-Util/LICENSE.TXT b/Dorkbox-Util/LICENSE.TXT new file mode 100644 index 0000000..5a460bb --- /dev/null +++ b/Dorkbox-Util/LICENSE.TXT @@ -0,0 +1,94 @@ +Legal: + + + + - Copyright (c) 2010 and beyond, dorkbox llc. All rights reserved. + + This software is a commercial product. Use, redistribution, and + modification without a license is NOT permitted. Contact + license@dorkbox.com for a license. + + Neither the name of dorkbox, dorkbox llc, or dorkbox.com or the names of + its contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + + THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS + ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE SOFTWARE + COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR + ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). + + THIS AGREEMENT IS GOVERNED THE INTELLECTUAL PROPERTY LAWS OF THE UNITED + STATES OF AMERICA. NO PARTY TO THIS AGREEMENT WILL BRING A LEGAL ACTION + UNDER THIS AGREEMENT MORE THAN ONE YEAR AFTER THE CAUSE OF ACTION AROSE. + EACH PARTY WAIVES ITS RIGHTS TO A JURY TRIAL IN ANY RESULTING LITIGATION. + + + + + + + - This software product uses encryption algorithms that are controlled by + the United States Export Administration Regulations (EAR). + https://bxa.ntis.gov/ + + Details of the U.S. Commercial Encryption Export Controls can be found at + the Bureau of Industry and Security (BIS) web site. + http://www.bis.doc.gov/ + + PROHIBITED END USERS + ALL products are prohibited for export/reexport to the following: + + - Any company or national of Cuba, Iran, North Korea, Sudan, and Syria. + Licenses to these countries and parties are presumed denied. + + - Re-export to these countries is prohibited; if you "know or have reason + to know" that an illegal reshipment will take place, you may not ship to + such a user. + + - Entities listed on any U.S. Government Denied Party/Person List. See BIS's + The Denied Persons List, the Office of Foreign Assets Control's Economic + and Trade sanctions list, (OFAC), and the Office of Defense Trade + Controls (DTC). + + - Any customer you know or have reason to know, who is involved in the + design, development, manufacture or production of nuclear technology, or + nuclear, biological or chemical "weapons of mass destruction." + + All products are subject to the U.S. Export Laws, and diversion contrary + to U.S. law is prohibited. + + + + - BouncyCastle - MIT X11 License + http://www.bouncycastle.org + Copyright (c) 2000 - 2009 The Legion Of The Bouncy Castle + + + + - MathUtils, IntArray, IntMap - Apache 2.0 license + http://github.com/libgdx/libgdx/ + Copyright (c) 2013 + Mario Zechner + Nathan Sweet + + + + - MersenneTwisterFast, v20 - BSD license + http://www.cs.gmu.edu/~sean/research/mersenne/MersenneTwisterFast.java + Copyright (c) 2003 by Sean Luke. + Portions copyright (c) 1993 by Michael Lecuyer. diff --git a/Dorkbox-Util/src/dorkbox/util/MathUtils.java b/Dorkbox-Util/src/dorkbox/util/MathUtils.java new file mode 100644 index 0000000..33fd74b --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/MathUtils.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// pruned/limited version from libGDX + +package dorkbox.util; + + +public class MathUtils { + + // --- + + private static ThreadLocal random = new ThreadLocal(); + + /** Returns a random integer */ + static public final int randomInt () { + return random.get().nextInt(); + } + + /** Returns a random number between 0 (inclusive) and the specified value (inclusive). */ + static public final int randomInt (int range) { + return random.get().nextInt(range + 1); + } + + /** Returns a random number between start (inclusive) and end (inclusive). */ + static public final int randomInt (int start, int end) { + return start + random.get().nextInt(end - start + 1); + } + + /** Returns a random boolean value. */ + static public final boolean randomBoolean () { + return random.get().nextBoolean(); + } + + /** Returns random number between 0.0 (inclusive) and 1.0 (exclusive). */ + static public final float randomFloat () { + return random.get().nextFloat(); + } + + /** Returns a random number between 0 (inclusive) and the specified value (exclusive). */ + static public final float randomFloat (float range) { + return random.get().nextFloat() * range; + } + + /** Returns a random number between start (inclusive) and end (exclusive). */ + static public final float randomFloat (float start, float end) { + return start + random.get().nextFloat() * (end - start); + } + + // --- + + + /** + * Returns the next power of two. Returns the specified value if the value + * is already a power of two. + */ + public static int nextPowerOfTwo(int value) { + if (value == 0) { + return 1; + } + + value--; + + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + + return value + 1; + } + + public static boolean isPowerOfTwo(int value) { + return value != 0 && (value & value - 1) == 0; + } +} diff --git a/Dorkbox-Util/src/dorkbox/util/MersenneTwisterFast.java b/Dorkbox-Util/src/dorkbox/util/MersenneTwisterFast.java new file mode 100644 index 0000000..5defad8 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/MersenneTwisterFast.java @@ -0,0 +1,1233 @@ +package dorkbox.util; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.Serializable; + +/** + *

MersenneTwister and MersenneTwisterFast

+ *

Version 20, based on version MT199937(99/10/29) + * of the Mersenne Twister algorithm found at + * + * The Mersenne Twister Home Page, with the initialization + * improved using the new 2002/1/26 initialization algorithm + * By Sean Luke, October 2004. + * + *

MersenneTwister is a drop-in subclass replacement + * for java.util.Random. It is properly synchronized and + * can be used in a multithreaded environment. On modern VMs such + * as HotSpot, it is approximately 1/3 slower than java.util.Random. + * + *

MersenneTwisterFast is not a subclass of java.util.Random. It has + * the same public methods as Random does, however, and it is + * algorithmically identical to MersenneTwister. MersenneTwisterFast + * has hard-code inlined all of its methods directly, and made all of them + * final (well, the ones of consequence anyway). Further, these + * methods are not synchronized, so the same MersenneTwisterFast + * instance cannot be shared by multiple threads. But all this helps + * MersenneTwisterFast achieve well over twice the speed of MersenneTwister. + * java.util.Random is about 1/3 slower than MersenneTwisterFast. + * + *

About the Mersenne Twister

+ *

This is a Java version of the C-program for MT19937: Integer version. + * The MT19937 algorithm was created by Makoto Matsumoto and Takuji Nishimura, + * who ask: "When you use this, send an email to: matumoto@math.keio.ac.jp + * with an appropriate reference to your work". Indicate that this + * is a translation of their algorithm into Java. + * + *

Reference. + * Makato Matsumoto and Takuji Nishimura, + * "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform + * Pseudo-Random Number Generator", + * ACM Transactions on Modeling and. Computer Simulation, + * Vol. 8, No. 1, January 1998, pp 3--30. + * + *

About this Version

+ * + *

Changes since V19: nextFloat(boolean, boolean) now returns float, + * not double. + * + *

Changes since V18: Removed old final declarations, which used to + * potentially speed up the code, but no longer. + * + *

Changes since V17: Removed vestigial references to &= 0xffffffff + * which stemmed from the original C code. The C code could not guarantee that + * ints were 32 bit, hence the masks. The vestigial references in the Java + * code were likely optimized out anyway. + * + *

Changes since V16: Added nextDouble(includeZero, includeOne) and + * nextFloat(includeZero, includeOne) to allow for half-open, fully-closed, and + * fully-open intervals. + * + *

Changes Since V15: Added serialVersionUID to quiet compiler warnings + * from Sun's overly verbose compilers as of JDK 1.5. + * + *

Changes Since V14: made strictfp, with StrictMath.log and StrictMath.sqrt + * in nextGaussian instead of Math.log and Math.sqrt. This is largely just to be safe, + * as it presently makes no difference in the speed, correctness, or results of the + * algorithm. + * + *

Changes Since V13: clone() method CloneNotSupportedException removed. + * + *

Changes Since V12: clone() method added. + * + *

Changes Since V11: stateEquals(...) method added. MersenneTwisterFast + * is equal to other MersenneTwisterFasts with identical state; likewise + * MersenneTwister is equal to other MersenneTwister with identical state. + * This isn't equals(...) because that requires a contract of immutability + * to compare by value. + * + *

Changes Since V10: A documentation error suggested that + * setSeed(int[]) required an int[] array 624 long. In fact, the array + * can be any non-zero length. The new version also checks for this fact. + * + *

Changes Since V9: readState(stream) and writeState(stream) + * provided. + * + *

Changes Since V8: setSeed(int) was only using the first 28 bits + * of the seed; it should have been 32 bits. For small-number seeds the + * behavior is identical. + * + *

Changes Since V7: A documentation error in MersenneTwisterFast + * (but not MersenneTwister) stated that nextDouble selects uniformly from + * the full-open interval [0,1]. It does not. nextDouble's contract is + * identical across MersenneTwisterFast, MersenneTwister, and java.util.Random, + * namely, selection in the half-open interval [0,1). That is, 1.0 should + * not be returned. A similar contract exists in nextFloat. + * + *

Changes Since V6: License has changed from LGPL to BSD. + * New timing information to compare against + * java.util.Random. Recent versions of HotSpot have helped Random increase + * in speed to the point where it is faster than MersenneTwister but slower + * than MersenneTwisterFast (which should be the case, as it's a less complex + * algorithm but is synchronized). + * + *

Changes Since V5: New empty constructor made to work the same + * as java.util.Random -- namely, it seeds based on the current time in + * milliseconds. + * + *

Changes Since V4: New initialization algorithms. See + * (see + * http://www.math.keio.ac.jp/matumoto/MT2002/emt19937ar.html) + * + *

The MersenneTwister code is based on standard MT19937 C/C++ + * code by Takuji Nishimura, + * with suggestions from Topher Cooper and Marc Rieffel, July 1997. + * The code was originally translated into Java by Michael Lecuyer, + * January 1999, and the original code is Copyright (c) 1999 by Michael Lecuyer. + * + *

Java notes

+ * + *

This implementation implements the bug fixes made + * in Java 1.2's version of Random, which means it can be used with + * earlier versions of Java. See + * + * the JDK 1.2 java.util.Random documentation for further documentation + * on the random-number generation contracts made. Additionally, there's + * an undocumented bug in the JDK java.util.Random.nextBytes() method, + * which this code fixes. + * + *

Just like java.util.Random, this + * generator accepts a long seed but doesn't use all of it. java.util.Random + * uses 48 bits. The Mersenne Twister instead uses 32 bits (int size). + * So it's best if your seed does not exceed the int range. + * + *

MersenneTwister can be used reliably + * on JDK version 1.1.5 or above. Earlier Java versions have serious bugs in + * java.util.Random; only MersenneTwisterFast (and not MersenneTwister nor + * java.util.Random) should be used with them. + * + *

License

+ * + * Copyright (c) 2003 by Sean Luke.
+ * Portions copyright (c) 1993 by Michael Lecuyer.
+ * All rights reserved.
+ * + *

Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + *

    + *
  • Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + *
  • Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + *
  • Neither the name of the copyright owners, their employers, nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + *
+ *

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + @version 20 +*/ + + +// Note: this class is hard-inlined in all of its methods. This makes some of +// the methods well-nigh unreadable in their complexity. In fact, the Mersenne +// Twister is fairly easy code to understand: if you're trying to get a handle +// on the code, I strongly suggest looking at MersenneTwister.java first. +// -- Sean + +public strictfp class MersenneTwisterFast implements Serializable, Cloneable { + // Serialization + private static final long serialVersionUID = -8219700664442619525L; // locked + // as of + // Version + // 15 + + // Period parameters + private static final int N = 624; + + private static final int M = 397; + + private static final int MATRIX_A = 0x9908b0df; // private + // static + // final + // * + // constant + // vector + // a + + private static final int UPPER_MASK = 0x80000000; // most + // significant + // w-r + // bits + + private static final int LOWER_MASK = 0x7fffffff; // least + // significant + // r + // bits + + // Tempering parameters + private static final int TEMPERING_MASK_B = 0x9d2c5680; + + private static final int TEMPERING_MASK_C = 0xefc60000; + + private int mt[]; // the + // array + // for + // the + // state + // vector + + private int mti; // mti==N+1 + // means + // mt[N] + // is + // not + // initialized + + private int mag01[]; + + // a good initial seed (of int size, though stored in a long) + // private static final long GOOD_SEED = 4357; + + private double __nextNextGaussian; + + private boolean __haveNextNextGaussian; + + /* + * We're overriding all internal data, to my knowledge, so this should be + * okay + */ + @Override + public Object clone() { + try { + MersenneTwisterFast f = (MersenneTwisterFast) super.clone(); + f.mt = this.mt.clone(); + f.mag01 = this.mag01.clone(); + return f; + } catch (CloneNotSupportedException e) { + throw new InternalError(); + } // should never happen + } + + public boolean stateEquals(Object o) { + if (o == this) { + return true; + } + if (o == null || !(o instanceof MersenneTwisterFast)) { + return false; + } + MersenneTwisterFast other = (MersenneTwisterFast) o; + if (this.mti != other.mti) { + return false; + } + for (int x = 0; x < this.mag01.length; x++) { + if (this.mag01[x] != other.mag01[x]) { + return false; + } + } + for (int x = 0; x < this.mt.length; x++) { + if (this.mt[x] != other.mt[x]) { + return false; + } + } + return true; + } + + /** Reads the entire state of the MersenneTwister RNG from the stream */ + public void readState(DataInputStream stream) throws IOException { + int len = this.mt.length; + for (int x = 0; x < len; x++) { + this.mt[x] = stream.readInt(); + } + + len = this.mag01.length; + for (int x = 0; x < len; x++) { + this.mag01[x] = stream.readInt(); + } + + this.mti = stream.readInt(); + this.__nextNextGaussian = stream.readDouble(); + this.__haveNextNextGaussian = stream.readBoolean(); + } + + /** Writes the entire state of the MersenneTwister RNG to the stream */ + public void writeState(DataOutputStream stream) throws IOException { + int len = this.mt.length; + for (int x = 0; x < len; x++) { + stream.writeInt(this.mt[x]); + } + + len = this.mag01.length; + for (int x = 0; x < len; x++) { + stream.writeInt(this.mag01[x]); + } + + stream.writeInt(this.mti); + stream.writeDouble(this.__nextNextGaussian); + stream.writeBoolean(this.__haveNextNextGaussian); + } + + /** + * Constructor using the default seed. + */ + public MersenneTwisterFast() { + this(System.currentTimeMillis()); + } + + /** + * Constructor using a given seed. Though you pass this seed in as a long, + * it's best to make sure it's actually an integer. + * + */ + public MersenneTwisterFast(long seed) { + setSeed(seed); + } + + /** + * Constructor using an array of integers as seed. Your array must have a + * non-zero length. Only the first 624 integers in the array are used; if + * the array is shorter than this then integers are repeatedly used in a + * wrap-around fashion. + */ + public MersenneTwisterFast(int[] array) { + setSeed(array); + } + + /** + * Initalize the pseudo random number generator. Don't pass in a long that's + * bigger than an int (Mersenne Twister only uses the first 32 bits for its + * seed). + */ + + synchronized public void setSeed(long seed) { + // Due to a bug in java.util.Random clear up to 1.2, we're + // doing our own Gaussian variable. + this.__haveNextNextGaussian = false; + + this.mt = new int[N]; + + this.mag01 = new int[2]; + this.mag01[0] = 0x0; + this.mag01[1] = MATRIX_A; + + this.mt[0] = (int) (seed & 0xffffffff); + for (this.mti = 1; this.mti < N; this.mti++) { + this.mt[this.mti] = + 1812433253 * (this.mt[this.mti-1] ^ this.mt[this.mti-1] >>> 30) + this.mti; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + // mt[mti] &= 0xffffffff; + /* for >32 bit machines */ + } + } + + /** + * Sets the seed of the MersenneTwister using an array of integers. Your + * array must have a non-zero length. Only the first 624 integers in the + * array are used; if the array is shorter than this then integers are + * repeatedly used in a wrap-around fashion. + */ + + synchronized public void setSeed(int[] array) { + if (array.length == 0) { + throw new IllegalArgumentException("Array length must be greater than zero"); + } + int i, j, k; + setSeed(19650218); + i = 1; + j = 0; + k = N > array.length ? N : array.length; + for (; k != 0; k--) { + this.mt[i] = (this.mt[i] ^ (this.mt[i-1] ^ this.mt[i-1] >>> 30) * 1664525) + array[j] + j; /* non linear */ + // mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ + i++; + j++; + if (i >= N) { + this.mt[0] = this.mt[N - 1]; + i = 1; + } + if (j >= array.length) { + j = 0; + } + } + for (k = N - 1; k != 0; k--) { + this.mt[i] = (this.mt[i] ^ (this.mt[i - 1] ^ this.mt[i - 1] >>> 30) * 1566083941) - i; /* non linear */ + // mt[i] &= 0xffffffff; /* for WORDSIZE > 32 machines */ + i++; + if (i >= N) { + this.mt[0] = this.mt[N - 1]; + i = 1; + } + } + this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ + } + + + public int nextInt() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return y; + } + + public short nextShort() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (short) (y >>> 16); + } + + public char nextChar() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (char) (y >>> 16); + } + + public boolean nextBoolean() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return y >>> 31 != 0; + } + + /** + * This generates a coin flip with a probability probability of returning true, else returning false. + * probability must be between 0.0 and 1.0, inclusive. Not as precise a random real event as + * nextBoolean(double), but twice as fast. To explicitly use this, remember you may need to cast to float first. + */ + + public boolean nextBoolean(float probability) { + int y; + + if (probability < 0.0f || probability > 1.0f) { + throw new IllegalArgumentException("probability must be between 0.0 and 1.0 inclusive."); + } + if (probability == 0.0f) { + return false; // fix half-open issues + } else if (probability == 1.0f) { + return true; // fix half-open issues + } + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (y >>> 8) / (float) (1 << 24) < probability; + } + + /** + * This generates a coin flip with a probability probability of returning true, else returning false. + * probability must be between 0.0 and 1.0, inclusive. + */ + + public boolean nextBoolean(double probability) { + int y; + int z; + + if (probability < 0.0 || probability > 1.0) { + throw new IllegalArgumentException("probability must be between 0.0 and 1.0 inclusive."); + } + if (probability == 0.0) { + return false; // fix half-open issues + } else if (probability == 1.0) { + return true; // fix half-open issues + } + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ z >>> 1 ^ mag01[z & 0x1]; + } + for (; kk < N - 1; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ z >>> 1 ^ mag01[z & 0x1]; + } + z = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ z >>> 1 ^ mag01[z & 0x1]; + + this.mti = 0; + } + + z = this.mt[this.mti++]; + z ^= z >>> 11; // TEMPERING_SHIFT_U(z) + z ^= z << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(z) + z ^= z << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(z) + z ^= z >>> 18; // TEMPERING_SHIFT_L(z) + + /* derived from nextDouble documentation in jdk 1.2 docs, see top */ + return (((long) (y >>> 6) << 27) + (z >>> 5)) / (double) (1L << 53) < probability; + } + + public byte nextByte() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (byte) (y >>> 24); + } + + public void nextBytes(byte[] bytes) { + int y; + + for (int x = 0; x < bytes.length; x++) { + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + bytes[x] = (byte) (y >>> 24); + } + } + + public long nextLong() { + int y; + int z; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ z >>> 1 ^ mag01[z & 0x1]; + } + for (; kk < N - 1; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ z >>> 1 ^ mag01[z & 0x1]; + } + z = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ z >>> 1 ^ mag01[z & 0x1]; + + this.mti = 0; + } + + z = this.mt[this.mti++]; + z ^= z >>> 11; // TEMPERING_SHIFT_U(z) + z ^= z << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(z) + z ^= z << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(z) + z ^= z >>> 18; // TEMPERING_SHIFT_L(z) + + return ((long) y << 32) + z; + } + + /** + * Returns a long drawn uniformly from 0 to n-1. Suffice it to say, n must be > 0, or an IllegalArgumentException is + * raised. + */ + public long nextLong(long n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive, got: " + n); + } + + long bits, val; + do { + int y; + int z; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ z >>> 1 ^ mag01[z & 0x1]; + } + for (; kk < N - 1; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ z >>> 1 ^ mag01[z & 0x1]; + } + z = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ z >>> 1 ^ mag01[z & 0x1]; + + this.mti = 0; + } + + z = this.mt[this.mti++]; + z ^= z >>> 11; // TEMPERING_SHIFT_U(z) + z ^= z << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(z) + z ^= z << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(z) + z ^= z >>> 18; // TEMPERING_SHIFT_L(z) + + bits = ((long) y << 32) + z >>> 1; + val = bits % n; + } while (bits - val + n - 1 < 0); + return val; + } + + /** + * Returns a random double in the half-open range from [0.0,1.0). Thus 0.0 is a valid result but 1.0 is not. + */ + public double nextDouble() { + int y; + int z; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ z >>> 1 ^ mag01[z & 0x1]; + } + for (; kk < N - 1; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ z >>> 1 ^ mag01[z & 0x1]; + } + z = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ z >>> 1 ^ mag01[z & 0x1]; + + this.mti = 0; + } + + z = this.mt[this.mti++]; + z ^= z >>> 11; // TEMPERING_SHIFT_U(z) + z ^= z << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(z) + z ^= z << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(z) + z ^= z >>> 18; // TEMPERING_SHIFT_L(z) + + /* derived from nextDouble documentation in jdk 1.2 docs, see top */ + return (((long) (y >>> 6) << 27) + (z >>> 5)) / (double) (1L << 53); + } + + /** + * Returns a double in the range from 0.0 to 1.0, possibly inclusive of 0.0 and 1.0 themselves. Thus: + * + *

+ * + * + * + * + * + *
+ * Expression + * Interval + *
nextDouble(false, false) + * (0.0, 1.0) + *
nextDouble(true, false) + * [0.0, 1.0) + *
nextDouble(false, true) + * (0.0, 1.0] + *
nextDouble(true, true) + * [0.0, 1.0] + *
+ * + *

+ * This version preserves all possible random values in the double range. + */ + public double nextDouble(boolean includeZero, boolean includeOne) { + double d = 0.0; + do { + d = nextDouble(); // grab a value, initially from half-open [0.0, 1.0) + if (includeOne && nextBoolean()) { + d += 1.0; // if includeOne, with 1/2 probability, push to [1.0, 2.0) + } + } while (d > 1.0 || // everything above 1.0 is always invalid + !includeZero && d == 0.0); // if we're not including zero, 0.0 is invalid + return d; + } + + public double nextGaussian() { + if (this.__haveNextNextGaussian) { + this.__haveNextNextGaussian = false; + return this.__nextNextGaussian; + } else { + double v1, v2, s; + do { + int y; + int z; + int a; + int b; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ z >>> 1 ^ mag01[z & 0x1]; + } + for (; kk < N - 1; kk++) { + z = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ z >>> 1 ^ mag01[z & 0x1]; + } + z = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ z >>> 1 ^ mag01[z & 0x1]; + + this.mti = 0; + } + + z = this.mt[this.mti++]; + z ^= z >>> 11; // TEMPERING_SHIFT_U(z) + z ^= z << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(z) + z ^= z << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(z) + z ^= z >>> 18; // TEMPERING_SHIFT_L(z) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + a = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ a >>> 1 ^ mag01[a & 0x1]; + } + for (; kk < N - 1; kk++) { + a = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ a >>> 1 ^ mag01[a & 0x1]; + } + a = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ a >>> 1 ^ mag01[a & 0x1]; + + this.mti = 0; + } + + a = this.mt[this.mti++]; + a ^= a >>> 11; // TEMPERING_SHIFT_U(a) + a ^= a << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(a) + a ^= a << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(a) + a ^= a >>> 18; // TEMPERING_SHIFT_L(a) + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + b = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ b >>> 1 ^ mag01[b & 0x1]; + } + for (; kk < N - 1; kk++) { + b = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ b >>> 1 ^ mag01[b & 0x1]; + } + b = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ b >>> 1 ^ mag01[b & 0x1]; + + this.mti = 0; + } + + b = this.mt[this.mti++]; + b ^= b >>> 11; // TEMPERING_SHIFT_U(b) + b ^= b << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(b) + b ^= b << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(b) + b ^= b >>> 18; // TEMPERING_SHIFT_L(b) + + /* derived from nextDouble documentation in jdk 1.2 docs, see top */ + v1 = 2 * ((((long) (y >>> 6) << 27) + (z >>> 5)) / (double) (1L << 53)) - 1; + v2 = 2 * ((((long) (a >>> 6) << 27) + (b >>> 5)) / (double) (1L << 53)) - 1; + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = StrictMath.sqrt(-2 * StrictMath.log(s) / s); + this.__nextNextGaussian = v2 * multiplier; + this.__haveNextNextGaussian = true; + return v1 * multiplier; + } + } + + /** + * Returns a random float in the half-open range from [0.0f,1.0f). Thus 0.0f is a valid result but 1.0f is not. + */ + public float nextFloat() { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (y >>> 8) / (float) (1 << 24); + } + + /** + * Returns a float in the range from 0.0f to 1.0f, possibly inclusive of 0.0f and 1.0f themselves. Thus: + * + *

+ * + * + * + * + * + *
+ * Expression + * Interval + *
nextFloat(false, false) + * (0.0f, 1.0f) + *
nextFloat(true, false) + * [0.0f, 1.0f) + *
nextFloat(false, true) + * (0.0f, 1.0f] + *
nextFloat(true, true) + * [0.0f, 1.0f] + *
+ * + *

+ * This version preserves all possible random values in the float range. + */ + public float nextFloat(boolean includeZero, boolean includeOne) { + float d = 0.0f; + do { + d = nextFloat(); // grab a value, initially from half-open [0.0f, 1.0f) + if (includeOne && nextBoolean()) { + d += 1.0f; // if includeOne, with 1/2 probability, push to [1.0f, 2.0f) + } + } while (d > 1.0f || // everything above 1.0f is always invalid + !includeZero && d == 0.0f); // if we're not including zero, 0.0f is invalid + return d; + } + + + /** + * Returns an integer drawn uniformly from 0 to n-1. Suffice it to say, n must be > 0, or an + * IllegalArgumentException is raised. + */ + public int nextInt(int n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive, got: " + n); + } + + if ((n & -n) == n) // i.e., n is a power of 2 + { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + return (int) (n * (long) (y >>> 1) >> 31); + } + + int bits, val; + do { + int y; + + if (this.mti >= N) // generate N words at one time + { + int kk; + final int[] mt = this.mt; // locals are slightly faster + final int[] mag01 = this.mag01; // locals are slightly faster + + for (kk = 0; kk < N - M; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M] ^ y >>> 1 ^ mag01[y & 0x1]; + } + for (; kk < N - 1; kk++) { + y = mt[kk] & UPPER_MASK | mt[kk + 1] & LOWER_MASK; + mt[kk] = mt[kk + M - N] ^ y >>> 1 ^ mag01[y & 0x1]; + } + y = mt[N - 1] & UPPER_MASK | mt[0] & LOWER_MASK; + mt[N - 1] = mt[M - 1] ^ y >>> 1 ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + y ^= y >>> 11; // TEMPERING_SHIFT_U(y) + y ^= y << 7 & TEMPERING_MASK_B; // TEMPERING_SHIFT_S(y) + y ^= y << 15 & TEMPERING_MASK_C; // TEMPERING_SHIFT_T(y) + y ^= y >>> 18; // TEMPERING_SHIFT_L(y) + + bits = y >>> 1; + val = bits % n; + } while (bits - val + n - 1 < 0); + return val; + } +} \ No newline at end of file diff --git a/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntArray.java b/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntArray.java new file mode 100644 index 0000000..2b202d4 --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntArray.java @@ -0,0 +1,384 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// from libGDX + +package dorkbox.util.primativeCollections; + + +import java.util.Arrays; + +import dorkbox.util.MathUtils; + +/** A resizable, ordered or unordered int array. Avoids the boxing that occurs with ArrayList. If unordered, this class + * avoids a memory copy when removing elements (the last element is moved to the removed element's position). + * @author Nathan Sweet */ +public class IntArray { + public int[] items; + public int size; + public boolean ordered; + + /** Creates an ordered array with a capacity of 16. */ + public IntArray () { + this(true, 16); + } + + /** Creates an ordered array with the specified capacity. */ + public IntArray (int capacity) { + this(true, capacity); + } + + /** @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a + * memory copy. + * @param capacity Any elements added beyond this will cause the backing array to be grown. */ + public IntArray (boolean ordered, int capacity) { + this.ordered = ordered; + this.items = new int[capacity]; + } + + /** Creates a new array containing the elements in the specific array. The new array will be ordered if the specific array is + * ordered. The capacity is set to the number of elements, so any subsequent elements added will cause the backing array to be + * grown. */ + public IntArray (IntArray array) { + this.ordered = array.ordered; + this.size = array.size; + this.items = new int[this.size]; + System.arraycopy(array.items, 0, this.items, 0, this.size); + } + + /** Creates a new ordered array containing the elements in the specified array. The capacity is set to the number of elements, + * so any subsequent elements added will cause the backing array to be grown. */ + public IntArray (int[] array) { + this(true, array, 0, array.length); + } + + /** Creates a new array containing the elements in the specified array. The capacity is set to the number of elements, so any + * subsequent elements added will cause the backing array to be grown. + * @param ordered If false, methods that remove elements may change the order of other elements in the array, which avoids a + * memory copy. */ + public IntArray (boolean ordered, int[] array, int startIndex, int count) { + this(ordered, array.length); + this.size = count; + System.arraycopy(array, startIndex, this.items, 0, count); + } + + public void add (int value) { + int[] items = this.items; + if (this.size == items.length) { + items = resize(Math.max(8, (int)(this.size * 1.75f))); + } + items[this.size++] = value; + } + + public void addAll (IntArray array) { + addAll(array, 0, array.size); + } + + public void addAll (IntArray array, int offset, int length) { + if (offset + length > array.size) { + throw new IllegalArgumentException("offset + length must be <= size: " + offset + " + " + length + " <= " + array.size); + } + addAll(array.items, offset, length); + } + + public void addAll (int[] array) { + addAll(array, 0, array.length); + } + + public void addAll (int[] array, int offset, int length) { + int[] items = this.items; + int sizeNeeded = this.size + length; + if (sizeNeeded >= items.length) { + items = resize(Math.max(8, (int)(sizeNeeded * 1.75f))); + } + System.arraycopy(array, offset, items, this.size, length); + this.size += length; + } + + public int get (int index) { + if (index >= this.size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + return this.items[index]; + } + + public void set (int index, int value) { + if (index >= this.size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + this.items[index] = value; + } + + public void insert (int index, int value) { + if (index > this.size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + int[] items = this.items; + if (this.size == items.length) { + items = resize(Math.max(8, (int)(this.size * 1.75f))); + } + if (this.ordered) { + System.arraycopy(items, index, items, index + 1, this.size - index); + } else { + items[this.size] = items[index]; + } + this.size++; + items[index] = value; + } + + public void swap (int first, int second) { + if (first >= this.size) { + throw new IndexOutOfBoundsException(String.valueOf(first)); + } + if (second >= this.size) { + throw new IndexOutOfBoundsException(String.valueOf(second)); + } + int[] items = this.items; + int firstValue = items[first]; + items[first] = items[second]; + items[second] = firstValue; + } + + public boolean contains (int value) { + int i = this.size - 1; + int[] items = this.items; + while (i >= 0) { + if (items[i--] == value) { + return true; + } + } + return false; + } + + public int indexOf (int value) { + int[] items = this.items; + for (int i = 0, n = this.size; i < n; i++) { + if (items[i] == value) { + return i; + } + } + return -1; + } + + public int lastIndexOf (int value) { + int[] items = this.items; + for (int i = this.size - 1; i >= 0; i--) { + if (items[i] == value) { + return i; + } + } + return -1; + } + + public boolean removeValue (int value) { + int[] items = this.items; + for (int i = 0, n = this.size; i < n; i++) { + if (items[i] == value) { + removeIndex(i); + return true; + } + } + return false; + } + + /** Removes and returns the item at the specified index. */ + public int removeIndex (int index) { + if (index >= this.size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + int[] items = this.items; + int value = items[index]; + this.size--; + if (this.ordered) { + System.arraycopy(items, index + 1, items, index, this.size - index); + } else { + items[index] = items[this.size]; + } + return value; + } + + /** Removes from this array all of elements contained in the specified array. + * @return true if this array was modified. */ + public boolean removeAll (IntArray array) { + int size = this.size; + int startSize = size; + int[] items = this.items; + for (int i = 0, n = array.size; i < n; i++) { + int item = array.get(i); + for (int ii = 0; ii < size; ii++) { + if (item == items[ii]) { + removeIndex(ii); + size--; + break; + } + } + } + return size != startSize; + } + + /** Removes and returns the last item. */ + public int pop () { + return this.items[--this.size]; + } + + /** Returns the last item. */ + public int peek () { + return this.items[this.size - 1]; + } + + /** Returns the first item. */ + public int first () { + if (this.size == 0) { + throw new IllegalStateException("Array is empty."); + } + return this.items[0]; + } + + public void clear () { + this.size = 0; + } + + /** Reduces the size of the backing array to the size of the actual items. This is useful to release memory when many items have + * been removed, or if it is known that more items will not be added. */ + public void shrink () { + resize(this.size); + } + + /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many + * items to avoid multiple backing array resizes. + * @return {@link #items} */ + public int[] ensureCapacity (int additionalCapacity) { + int sizeNeeded = this.size + additionalCapacity; + if (sizeNeeded >= this.items.length) { + resize(Math.max(8, sizeNeeded)); + } + return this.items; + } + + protected int[] resize (int newSize) { + int[] newItems = new int[newSize]; + int[] items = this.items; + System.arraycopy(items, 0, newItems, 0, Math.min(this.size, newItems.length)); + this.items = newItems; + return newItems; + } + + public void sort () { + Arrays.sort(this.items, 0, this.size); + } + + public void reverse () { + for (int i = 0, lastIndex = this.size - 1, n = this.size / 2; i < n; i++) { + int ii = lastIndex - i; + int temp = this.items[i]; + this.items[i] = this.items[ii]; + this.items[ii] = temp; + } + } + + public void shuffle () { + for (int i = this.size - 1; i >= 0; i--) { + int ii = MathUtils.randomInt(i); + int temp = this.items[i]; + this.items[i] = this.items[ii]; + this.items[ii] = temp; + } + } + + /** Reduces the size of the array to the specified size. If the array is already smaller than the specified size, no action is + * taken. */ + public void truncate (int newSize) { + if (this.size > newSize) { + this.size = newSize; + } + } + + /** Returns a random item from the array, or zero if the array is empty. */ + public int random () { + if (this.size == 0) { + return 0; + } + return this.items[MathUtils.randomInt(0, this.size - 1)]; + } + + public int[] toArray () { + int[] array = new int[this.size]; + System.arraycopy(this.items, 0, array, 0, this.size); + return array; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(this.items); + result = prime * result + (this.ordered ? 1231 : 1237); + result = prime * result + this.size; + return result; + } + + @Override + public boolean equals (Object object) { + if (object == this) { + return true; + } + if (!(object instanceof IntArray)) { + return false; + } + IntArray array = (IntArray)object; + int n = this.size; + if (n != array.size) { + return false; + } + for (int i = 0; i < n; i++) { + if (this.items[i] != array.items[i]) { + return false; + } + } + return true; + } + + @Override + public String toString () { + if (this.size == 0) { + return "[]"; + } + int[] items = this.items; + StringBuilder buffer = new StringBuilder(32); + buffer.append('['); + buffer.append(items[0]); + for (int i = 1; i < this.size; i++) { + buffer.append(", "); + buffer.append(items[i]); + } + buffer.append(']'); + return buffer.toString(); + } + + public String toString (String separator) { + if (this.size == 0) { + return ""; + } + int[] items = this.items; + StringBuilder buffer = new StringBuilder(32); + buffer.append(items[0]); + for (int i = 1; i < this.size; i++) { + buffer.append(separator); + buffer.append(items[i]); + } + return buffer.toString(); + } +} \ No newline at end of file diff --git a/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntMap.java b/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntMap.java new file mode 100644 index 0000000..b671c6b --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/primativeCollections/IntMap.java @@ -0,0 +1,870 @@ +/******************************************************************************* + * Copyright 2011 See AUTHORS file. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// slightly tweaked from libGDX + +package dorkbox.util.primativeCollections; + + +import java.util.Iterator; +import java.util.NoSuchElementException; + +import dorkbox.util.MathUtils; + +/** An unordered map that uses int keys. This implementation is a cuckoo hash map using 3 hashes, random walking, and a small stash + * for problematic keys. Null values are allowed. No allocation is done except when growing the table size.
+ *
+ * This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, + * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the + * next higher POT size. + * @author Nathan Sweet */ + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class IntMap { + @SuppressWarnings("unused") + private static final int PRIME1 = 0xbe1f14b1; + private static final int PRIME2 = 0xb4b82e39; + private static final int PRIME3 = 0xced1c241; + private static final int EMPTY = 0; + + public int size; + + int[] keyTable; + V[] valueTable; + int capacity, stashSize; + V zeroValue; + boolean hasZeroValue; + + private float loadFactor; + private int hashShift, mask, threshold; + private int stashCapacity; + private int pushIterations; + + private Entries entries1, entries2; + private Values values1, values2; + private Keys keys1, keys2; + + /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the + * backing table. */ + public IntMap () { + this(32, 0.8f); + } + + /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing + * table. */ + public IntMap (int initialCapacity) { + this(initialCapacity, 0.8f); + } + + /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items + * before growing the backing table. */ + public IntMap (int initialCapacity, float loadFactor) { + if (initialCapacity < 0) { + throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); + } + if (this.capacity > 1 << 30) { + throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); + } + this.capacity = MathUtils.nextPowerOfTwo(initialCapacity); + + if (loadFactor <= 0) { + throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); + } + this.loadFactor = loadFactor; + + this.threshold = (int)(this.capacity * loadFactor); + this.mask = this.capacity - 1; + this.hashShift = 31 - Integer.numberOfTrailingZeros(this.capacity); + this.stashCapacity = Math.max(3, (int)Math.ceil(Math.log(this.capacity)) * 2); + this.pushIterations = Math.max(Math.min(this.capacity, 8), (int)Math.sqrt(this.capacity) / 8); + + this.keyTable = new int[this.capacity + this.stashCapacity]; + this.valueTable = (V[])new Object[this.keyTable.length]; + } + + public V put (int key, V value) { + if (key == 0) { + V oldValue = this.zeroValue; + this.zeroValue = value; + if (!this.hasZeroValue) { + this.hasZeroValue = true; + this.size++; + } + return oldValue; + } + + int[] keyTable = this.keyTable; + + // Check for existing keys. + int index1 = key & this.mask; + int key1 = keyTable[index1]; + if (key1 == key) { + V oldValue = this.valueTable[index1]; + this.valueTable[index1] = value; + return oldValue; + } + + int index2 = hash2(key); + int key2 = keyTable[index2]; + if (key2 == key) { + V oldValue = this.valueTable[index2]; + this.valueTable[index2] = value; + return oldValue; + } + + int index3 = hash3(key); + int key3 = keyTable[index3]; + if (key3 == key) { + V oldValue = this.valueTable[index3]; + this.valueTable[index3] = value; + return oldValue; + } + + // Update key in the stash. + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (keyTable[i] == key) { + V oldValue = this.valueTable[i]; + this.valueTable[i] = value; + return oldValue; + } + } + + // Check for empty buckets. + if (key1 == EMPTY) { + keyTable[index1] = key; + this.valueTable[index1] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return null; + } + + if (key2 == EMPTY) { + keyTable[index2] = key; + this.valueTable[index2] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return null; + } + + if (key3 == EMPTY) { + keyTable[index3] = key; + this.valueTable[index3] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return null; + } + + push(key, value, index1, key1, index2, key2, index3, key3); + return null; + } + + public void putAll (IntMap map) { + for (Entry entry : map.entries()) { + put(entry.key, entry.value); + } + } + + /** Skips checks for existing keys. */ + private void putResize (int key, V value) { + if (key == 0) { + this.zeroValue = value; + this.hasZeroValue = true; + return; + } + + // Check for empty buckets. + int index1 = key & this.mask; + int key1 = this.keyTable[index1]; + if (key1 == EMPTY) { + this.keyTable[index1] = key; + this.valueTable[index1] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + int index2 = hash2(key); + int key2 = this.keyTable[index2]; + if (key2 == EMPTY) { + this.keyTable[index2] = key; + this.valueTable[index2] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + int index3 = hash3(key); + int key3 = this.keyTable[index3]; + if (key3 == EMPTY) { + this.keyTable[index3] = key; + this.valueTable[index3] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + push(key, value, index1, key1, index2, key2, index3, key3); + } + + private void push (int insertKey, V insertValue, int index1, int key1, int index2, int key2, int index3, int key3) { + int[] keyTable = this.keyTable; + + V[] valueTable = this.valueTable; + int mask = this.mask; + + // Push keys until an empty bucket is found. + int evictedKey; + V evictedValue; + int i = 0, pushIterations = this.pushIterations; + do { + // Replace the key and value for one of the hashes. + switch (MathUtils.randomInt(2)) { + case 0: + evictedKey = key1; + evictedValue = valueTable[index1]; + keyTable[index1] = insertKey; + valueTable[index1] = insertValue; + break; + case 1: + evictedKey = key2; + evictedValue = valueTable[index2]; + keyTable[index2] = insertKey; + valueTable[index2] = insertValue; + break; + default: + evictedKey = key3; + evictedValue = valueTable[index3]; + keyTable[index3] = insertKey; + valueTable[index3] = insertValue; + break; + } + + // If the evicted key hashes to an empty bucket, put it there and stop. + index1 = evictedKey & mask; + key1 = keyTable[index1]; + if (key1 == EMPTY) { + keyTable[index1] = evictedKey; + valueTable[index1] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + index2 = hash2(evictedKey); + key2 = keyTable[index2]; + if (key2 == EMPTY) { + keyTable[index2] = evictedKey; + valueTable[index2] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + index3 = hash3(evictedKey); + key3 = keyTable[index3]; + if (key3 == EMPTY) { + keyTable[index3] = evictedKey; + valueTable[index3] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + if (++i == pushIterations) { + break; + } + + insertKey = evictedKey; + insertValue = evictedValue; + } while (true); + + putStash(evictedKey, evictedValue); + } + + private void putStash (int key, V value) { + if (this.stashSize == this.stashCapacity) { + // Too many pushes occurred and the stash is full, increase the table size. + resize(this.capacity << 1); + put(key, value); + return; + } + // Store key in the stash. + int index = this.capacity + this.stashSize; + this.keyTable[index] = key; + this.valueTable[index] = value; + this.stashSize++; + this.size++; + } + + public V get (int key) { + if (key == 0) { + if (!this.hasZeroValue) { + return null; + } + return this.zeroValue; + } + int index = key & this.mask; + if (this.keyTable[index] != key) { + index = hash2(key); + if (this.keyTable[index] != key) { + index = hash3(key); + if (this.keyTable[index] != key) { + return getStash(key, null); + } + } + } + return this.valueTable[index]; + } + + public V get (int key, V defaultValue) { + if (key == 0) { + if (!this.hasZeroValue) { + return defaultValue; + } + return this.zeroValue; + } + int index = key & this.mask; + if (this.keyTable[index] != key) { + index = hash2(key); + if (this.keyTable[index] != key) { + index = hash3(key); + if (this.keyTable[index] != key) { + return getStash(key, defaultValue); + } + } + } + return this.valueTable[index]; + } + + private V getStash (int key, V defaultValue) { + int[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (keyTable[i] == key) { + return this.valueTable[i]; + } + } + return defaultValue; + } + + public V remove (int key) { + if (key == 0) { + if (!this.hasZeroValue) { + return null; + } + V oldValue = this.zeroValue; + this.zeroValue = null; + this.hasZeroValue = false; + this.size--; + return oldValue; + } + + int index = key & this.mask; + if (this.keyTable[index] == key) { + this.keyTable[index] = EMPTY; + V oldValue = this.valueTable[index]; + this.valueTable[index] = null; + this.size--; + return oldValue; + } + + index = hash2(key); + if (this.keyTable[index] == key) { + this.keyTable[index] = EMPTY; + V oldValue = this.valueTable[index]; + this.valueTable[index] = null; + this.size--; + return oldValue; + } + + index = hash3(key); + if (this.keyTable[index] == key) { + this.keyTable[index] = EMPTY; + V oldValue = this.valueTable[index]; + this.valueTable[index] = null; + this.size--; + return oldValue; + } + + return removeStash(key); + } + + V removeStash (int key) { + int[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (keyTable[i] == key) { + V oldValue = this.valueTable[i]; + removeStashIndex(i); + this.size--; + return oldValue; + } + } + return null; + } + + void removeStashIndex (int index) { + // If the removed location was not last, move the last tuple to the removed location. + this.stashSize--; + int lastIndex = this.capacity + this.stashSize; + if (index < lastIndex) { + this.keyTable[index] = this.keyTable[lastIndex]; + this.valueTable[index] = this.valueTable[lastIndex]; + this.valueTable[lastIndex] = null; + } else { + this.valueTable[index] = null; + } + } + + public void clear () { + int[] keyTable = this.keyTable; + V[] valueTable = this.valueTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + keyTable[i] = EMPTY; + valueTable[i] = null; + } + this.size = 0; + this.stashSize = 0; + this.zeroValue = null; + this.hasZeroValue = false; + } + + /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be + * an expensive operation. + * @param identity If true, uses == to compare the specified value with values in the map. If false, uses + * {@link #equals(Object)}. */ + public boolean containsValue (Object value, boolean identity) { + V[] valueTable = this.valueTable; + if (value == null) { + if (this.hasZeroValue && this.zeroValue == null) { + return true; + } + int[] keyTable = this.keyTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (keyTable[i] != EMPTY && valueTable[i] == null) { + return true; + } + } + } else if (identity) { + if (value == this.zeroValue) { + return true; + } + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (valueTable[i] == value) { + return true; + } + } + } else { + if (this.hasZeroValue && value.equals(this.zeroValue)) { + return true; + } + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (value.equals(valueTable[i])) { + return true; + } + } + } + return false; + } + + public boolean containsKey (int key) { + if (key == 0) { + return this.hasZeroValue; + } + int index = key & this.mask; + if (this.keyTable[index] != key) { + index = hash2(key); + if (this.keyTable[index] != key) { + index = hash3(key); + if (this.keyTable[index] != key) { + return containsKeyStash(key); + } + } + } + return true; + } + + private boolean containsKeyStash (int key) { + int[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (keyTable[i] == key) { + return true; + } + } + return false; + } + + /** Returns the key for the specified value, or notFound if it is not in the map. Note this traverses the entire map + * and compares every value, which may be an expensive operation. + * @param identity If true, uses == to compare the specified value with values in the map. If false, uses + * {@link #equals(Object)}. */ + public int findKey (Object value, boolean identity, int notFound) { + V[] valueTable = this.valueTable; + if (value == null) { + if (this.hasZeroValue && this.zeroValue == null) { + return 0; + } + int[] keyTable = this.keyTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (keyTable[i] != EMPTY && valueTable[i] == null) { + return keyTable[i]; + } + } + } else if (identity) { + if (value == this.zeroValue) { + return 0; + } + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (valueTable[i] == value) { + return this.keyTable[i]; + } + } + } else { + if (this.hasZeroValue && value.equals(this.zeroValue)) { + return 0; + } + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (value.equals(valueTable[i])) { + return this.keyTable[i]; + } + } + } + return notFound; + } + + /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many + * items to avoid multiple backing array resizes. */ + public void ensureCapacity (int additionalCapacity) { + int sizeNeeded = this.size + additionalCapacity; + if (sizeNeeded >= this.threshold) { + resize(MathUtils.nextPowerOfTwo((int)(sizeNeeded / this.loadFactor))); + } + } + + private void resize (int newSize) { + int oldEndIndex = this.capacity + this.stashSize; + + this.capacity = newSize; + this.threshold = (int)(newSize * this.loadFactor); + this.mask = newSize - 1; + this.hashShift = 31 - Integer.numberOfTrailingZeros(newSize); + this.stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); + this.pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); + + int[] oldKeyTable = this.keyTable; + V[] oldValueTable = this.valueTable; + + this.keyTable = new int[newSize + this.stashCapacity]; + this.valueTable = (V[])new Object[newSize + this.stashCapacity]; + + this.size = this.hasZeroValue ? 1 : 0; + this.stashSize = 0; + for (int i = 0; i < oldEndIndex; i++) { + int key = oldKeyTable[i]; + if (key != EMPTY) { + putResize(key, oldValueTable[i]); + } + } + } + + private int hash2 (int h) { + h *= PRIME2; + return (h ^ h >>> this.hashShift) & this.mask; + } + + private int hash3 (int h) { + h *= PRIME3; + return (h ^ h >>> this.hashShift) & this.mask; + } + + @Override + public String toString () { + if (this.size == 0) { + return "[]"; + } + StringBuilder buffer = new StringBuilder(32); + buffer.append('['); + int[] keyTable = this.keyTable; + V[] valueTable = this.valueTable; + int i = keyTable.length; + if (this.hasZeroValue) { + buffer.append("0="); + buffer.append(this.zeroValue); + } else { + while (i-- > 0) { + int key = keyTable[i]; + if (key == EMPTY) { + continue; + } + buffer.append(key); + buffer.append('='); + buffer.append(valueTable[i]); + break; + } + } + while (i-- > 0) { + int key = keyTable[i]; + if (key == EMPTY) { + continue; + } + buffer.append(", "); + buffer.append(key); + buffer.append('='); + buffer.append(valueTable[i]); + } + buffer.append(']'); + return buffer.toString(); + } + + /** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each + * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ + public Entries entries () { + if (this.entries1 == null) { + this.entries1 = new Entries(this); + this.entries2 = new Entries(this); + } + if (!this.entries1.valid) { + this.entries1.reset(); + this.entries1.valid = true; + this.entries2.valid = false; + return this.entries1; + } + this.entries2.reset(); + this.entries2.valid = true; + this.entries1.valid = false; + return this.entries2; + } + + /** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each + * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ + public Values values () { + if (this.values1 == null) { + this.values1 = new Values(this); + this.values2 = new Values(this); + } + if (!this.values1.valid) { + this.values1.reset(); + this.values1.valid = true; + this.values2.valid = false; + return this.values1; + } + this.values2.reset(); + this.values2.valid = true; + this.values1.valid = false; + return this.values2; + } + + /** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each time + * this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */ + public Keys keys () { + if (this.keys1 == null) { + this.keys1 = new Keys(this); + this.keys2 = new Keys(this); + } + if (!this.keys1.valid) { + this.keys1.reset(); + this.keys1.valid = true; + this.keys2.valid = false; + return this.keys1; + } + this.keys2.reset(); + this.keys2.valid = true; + this.keys1.valid = false; + return this.keys2; + } + + static public class Entry { + public int key; + public V value; + + @Override + public String toString () { + return this.key + "=" + this.value; + } + } + + static private class MapIterator { + static final int INDEX_ILLEGAL = -2; + static final int INDEX_ZERO = -1; + + public boolean hasNext; + + final IntMap map; + int nextIndex, currentIndex; + boolean valid = true; + + public MapIterator (IntMap map) { + this.map = map; + reset(); + } + + public void reset () { + this.currentIndex = INDEX_ILLEGAL; + this.nextIndex = INDEX_ZERO; + if (this.map.hasZeroValue) { + this.hasNext = true; + } else { + findNextIndex(); + } + } + + void findNextIndex () { + this.hasNext = false; + int[] keyTable = this.map.keyTable; + for (int n = this.map.capacity + this.map.stashSize; ++this.nextIndex < n;) { + if (keyTable[this.nextIndex] != EMPTY) { + this.hasNext = true; + break; + } + } + } + + public void remove () { + if (this.currentIndex == INDEX_ZERO && this.map.hasZeroValue) { + this.map.zeroValue = null; + this.map.hasZeroValue = false; + } else if (this.currentIndex < 0) { + throw new IllegalStateException("next must be called before remove."); + } else if (this.currentIndex >= this.map.capacity) { + this.map.removeStashIndex(this.currentIndex); + } else { + this.map.keyTable[this.currentIndex] = EMPTY; + this.map.valueTable[this.currentIndex] = null; + } + this.currentIndex = INDEX_ILLEGAL; + this.map.size--; + } + } + + static public class Entries extends MapIterator implements Iterable>, Iterator> { + private Entry entry = new Entry(); + + public Entries (IntMap map) { + super(map); + } + + /** Note the same entry instance is returned each time this method is called. */ + @Override + public Entry next () { + if (!this.hasNext) { + throw new NoSuchElementException(); + } + if (!this.valid) { + throw new RuntimeException("#iterator() cannot be used nested."); + } + int[] keyTable = this.map.keyTable; + if (this.nextIndex == INDEX_ZERO) { + this.entry.key = 0; + this.entry.value = this.map.zeroValue; + } else { + this.entry.key = keyTable[this.nextIndex]; + this.entry.value = this.map.valueTable[this.nextIndex]; + } + this.currentIndex = this.nextIndex; + findNextIndex(); + return this.entry; + } + + @Override + public boolean hasNext () { + return this.hasNext; + } + + @Override + public Iterator> iterator () { + return this; + } + } + + static public class Values extends MapIterator implements Iterable, Iterator { + public Values (IntMap map) { + super(map); + } + + @Override + public boolean hasNext () { + return this.hasNext; + } + + @Override + public V next () { + if (!this.hasNext) { + throw new NoSuchElementException(); + } + if (!this.valid) { + throw new RuntimeException("#iterator() cannot be used nested."); + } + V value; + if (this.nextIndex == INDEX_ZERO) { + value = this.map.zeroValue; + } else { + value = this.map.valueTable[this.nextIndex]; + } + this.currentIndex = this.nextIndex; + findNextIndex(); + return value; + } + + @Override + public Iterator iterator () { + return this; + } + + /** Returns a new array containing the remaining values. */ +// public Array toArray () { +// Array array = new Array(true, map.size); +// while (hasNext) { +// array.add(next()); +// } +// return array; +// } + } + + static public class Keys extends MapIterator { + public Keys (IntMap map) { + super(map); + } + + public int next () { + if (!this.hasNext) { + throw new NoSuchElementException(); + } + if (!this.valid) { + throw new RuntimeException("#iterator() cannot be used nested."); + } + int key = this.nextIndex == INDEX_ZERO ? 0 : this.map.keyTable[this.nextIndex]; + this.currentIndex = this.nextIndex; + findNextIndex(); + return key; + } + + /** Returns a new array containing the remaining keys. */ + public IntArray toArray () { + IntArray array = new IntArray(true, this.map.size); + while (this.hasNext) { + array.add(next()); + } + return array; + } + } +} \ No newline at end of file diff --git a/Dorkbox-Util/src/dorkbox/util/primativeCollections/ObjectIntMap.java b/Dorkbox-Util/src/dorkbox/util/primativeCollections/ObjectIntMap.java new file mode 100644 index 0000000..5665bcf --- /dev/null +++ b/Dorkbox-Util/src/dorkbox/util/primativeCollections/ObjectIntMap.java @@ -0,0 +1,557 @@ + +package dorkbox.util.primativeCollections; + +import com.esotericsoftware.kryo.util.ObjectMap; + +import dorkbox.util.MathUtils; + +/** An unordered map where the values are ints. This implementation is a cuckoo hash map using 3 hashes, random walking, and a + * small stash for problematic keys. Null keys are not allowed. No allocation is done except when growing the table size.
+ *
+ * This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower, + * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the + * next higher POT size. + * @author Nathan Sweet */ +public class ObjectIntMap { + @SuppressWarnings("unused") + private static final int PRIME1 = 0xbe1f14b1; + private static final int PRIME2 = 0xb4b82e39; + private static final int PRIME3 = 0xced1c241; + + public int size; + + K[] keyTable; + int[] valueTable; + int capacity, stashSize; + + private float loadFactor; + private int hashShift, mask, threshold; + private int stashCapacity; + private int pushIterations; + + /** Creates a new map with an initial capacity of 32 and a load factor of 0.8. This map will hold 25 items before growing the + * backing table. */ + public ObjectIntMap () { + this(32, 0.8f); + } + + /** Creates a new map with a load factor of 0.8. This map will hold initialCapacity * 0.8 items before growing the backing + * table. */ + public ObjectIntMap (int initialCapacity) { + this(initialCapacity, 0.8f); + } + + /** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity * loadFactor items + * before growing the backing table. */ + @SuppressWarnings("unchecked") + public ObjectIntMap (int initialCapacity, float loadFactor) { + if (initialCapacity < 0) { + throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity); + } + if (initialCapacity > 1 << 30) { + throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity); + } + this.capacity = ObjectMap.nextPowerOfTwo(initialCapacity); + + if (loadFactor <= 0) { + throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor); + } + this.loadFactor = loadFactor; + + this.threshold = (int)(this.capacity * loadFactor); + this.mask = this.capacity - 1; + this.hashShift = 31 - Integer.numberOfTrailingZeros(this.capacity); + this.stashCapacity = Math.max(3, (int)Math.ceil(Math.log(this.capacity)) * 2); + this.pushIterations = Math.max(Math.min(this.capacity, 8), (int)Math.sqrt(this.capacity) / 8); + + this.keyTable = (K[])new Object[this.capacity + this.stashCapacity]; + this.valueTable = new int[this.keyTable.length]; + } + + /** Creates a new map identical to the specified map. */ + public ObjectIntMap (ObjectIntMap map) { + this(map.capacity, map.loadFactor); + this.stashSize = map.stashSize; + System.arraycopy(map.keyTable, 0, this.keyTable, 0, map.keyTable.length); + System.arraycopy(map.valueTable, 0, this.valueTable, 0, map.valueTable.length); + this.size = map.size; + } + + public void put (K key, int value) { + if (key == null) { + throw new IllegalArgumentException("key cannot be null."); + } + K[] keyTable = this.keyTable; + + // Check for existing keys. + int hashCode = key.hashCode(); + int index1 = hashCode & this.mask; + K key1 = keyTable[index1]; + if (key.equals(key1)) { + this.valueTable[index1] = value; + return; + } + + int index2 = hash2(hashCode); + K key2 = keyTable[index2]; + if (key.equals(key2)) { + this.valueTable[index2] = value; + return; + } + + int index3 = hash3(hashCode); + K key3 = keyTable[index3]; + if (key.equals(key3)) { + this.valueTable[index3] = value; + return; + } + + // Update key in the stash. + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (key.equals(keyTable[i])) { + this.valueTable[i] = value; + return; + } + } + + // Check for empty buckets. + if (key1 == null) { + keyTable[index1] = key; + this.valueTable[index1] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + if (key2 == null) { + keyTable[index2] = key; + this.valueTable[index2] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + if (key3 == null) { + keyTable[index3] = key; + this.valueTable[index3] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + push(key, value, index1, key1, index2, key2, index3, key3); + } + + /** Skips checks for existing keys. */ + private void putResize (K key, int value) { + // Check for empty buckets. + int hashCode = key.hashCode(); + int index1 = hashCode & this.mask; + K key1 = this.keyTable[index1]; + if (key1 == null) { + this.keyTable[index1] = key; + this.valueTable[index1] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + int index2 = hash2(hashCode); + K key2 = this.keyTable[index2]; + if (key2 == null) { + this.keyTable[index2] = key; + this.valueTable[index2] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + int index3 = hash3(hashCode); + K key3 = this.keyTable[index3]; + if (key3 == null) { + this.keyTable[index3] = key; + this.valueTable[index3] = value; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + push(key, value, index1, key1, index2, key2, index3, key3); + } + + private void push (K insertKey, int insertValue, int index1, K key1, int index2, K key2, int index3, K key3) { + K[] keyTable = this.keyTable; + int[] valueTable = this.valueTable; + int mask = this.mask; + + // Push keys until an empty bucket is found. + K evictedKey; + int evictedValue; + int i = 0, pushIterations = this.pushIterations; + do { + // Replace the key and value for one of the hashes. + switch (MathUtils.randomInt(2)) { + case 0: + evictedKey = key1; + evictedValue = valueTable[index1]; + keyTable[index1] = insertKey; + valueTable[index1] = insertValue; + break; + case 1: + evictedKey = key2; + evictedValue = valueTable[index2]; + keyTable[index2] = insertKey; + valueTable[index2] = insertValue; + break; + default: + evictedKey = key3; + evictedValue = valueTable[index3]; + keyTable[index3] = insertKey; + valueTable[index3] = insertValue; + break; + } + + // If the evicted key hashes to an empty bucket, put it there and stop. + int hashCode = evictedKey.hashCode(); + index1 = hashCode & mask; + key1 = keyTable[index1]; + if (key1 == null) { + keyTable[index1] = evictedKey; + valueTable[index1] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + index2 = hash2(hashCode); + key2 = keyTable[index2]; + if (key2 == null) { + keyTable[index2] = evictedKey; + valueTable[index2] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + index3 = hash3(hashCode); + key3 = keyTable[index3]; + if (key3 == null) { + keyTable[index3] = evictedKey; + valueTable[index3] = evictedValue; + if (this.size++ >= this.threshold) { + resize(this.capacity << 1); + } + return; + } + + if (++i == pushIterations) { + break; + } + + insertKey = evictedKey; + insertValue = evictedValue; + } while (true); + + putStash(evictedKey, evictedValue); + } + + private void putStash (K key, int value) { + if (this.stashSize == this.stashCapacity) { + // Too many pushes occurred and the stash is full, increase the table size. + resize(this.capacity << 1); + put(key, value); + return; + } + // Store key in the stash. + int index = this.capacity + this.stashSize; + this.keyTable[index] = key; + this.valueTable[index] = value; + this.stashSize++; + this.size++; + } + + /** @param defaultValue Returned if the key was not associated with a value. */ + public int get (K key, int defaultValue) { + int hashCode = key.hashCode(); + int index = hashCode & this.mask; + if (!key.equals(this.keyTable[index])) { + index = hash2(hashCode); + if (!key.equals(this.keyTable[index])) { + index = hash3(hashCode); + if (!key.equals(this.keyTable[index])) { + return getStash(key, defaultValue); + } + } + } + return this.valueTable[index]; + } + + private int getStash (K key, int defaultValue) { + K[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (key.equals(keyTable[i])) { + return this.valueTable[i]; + } + } + return defaultValue; + } + + /** Returns the key's current value and increments the stored value. If the key is not in the map, defaultValue + increment is + * put into the map. */ + public int getAndIncrement (K key, int defaultValue, int increment) { + int hashCode = key.hashCode(); + int index = hashCode & this.mask; + if (!key.equals(this.keyTable[index])) { + index = hash2(hashCode); + if (!key.equals(this.keyTable[index])) { + index = hash3(hashCode); + if (!key.equals(this.keyTable[index])) { + return getAndIncrementStash(key, defaultValue, increment); + } + } + } + int value = this.valueTable[index]; + this.valueTable[index] = value + increment; + return value; + } + + private int getAndIncrementStash (K key, int defaultValue, int increment) { + K[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (key.equals(keyTable[i])) { + int value = this.valueTable[i]; + this.valueTable[i] = value + increment; + return value; + } + } + put(key, defaultValue + increment); + return defaultValue; + } + + public int remove (K key, int defaultValue) { + int hashCode = key.hashCode(); + int index = hashCode & this.mask; + if (key.equals(this.keyTable[index])) { + this.keyTable[index] = null; + int oldValue = this.valueTable[index]; + this.size--; + return oldValue; + } + + index = hash2(hashCode); + if (key.equals(this.keyTable[index])) { + this.keyTable[index] = null; + int oldValue = this.valueTable[index]; + this.size--; + return oldValue; + } + + index = hash3(hashCode); + if (key.equals(this.keyTable[index])) { + this.keyTable[index] = null; + int oldValue = this.valueTable[index]; + this.size--; + return oldValue; + } + + return removeStash(key, defaultValue); + } + + int removeStash (K key, int defaultValue) { + K[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (key.equals(keyTable[i])) { + int oldValue = this.valueTable[i]; + removeStashIndex(i); + this.size--; + return oldValue; + } + } + return defaultValue; + } + + void removeStashIndex (int index) { + // If the removed location was not last, move the last tuple to the removed location. + this.stashSize--; + int lastIndex = this.capacity + this.stashSize; + if (index < lastIndex) { + this.keyTable[index] = this.keyTable[lastIndex]; + this.valueTable[index] = this.valueTable[lastIndex]; + } + } + + /** Reduces the size of the backing arrays to be the specified capacity or less. If the capacity is already less, nothing is + * done. If the map contains more items than the specified capacity, the next highest power of two capacity is used instead. */ + public void shrink (int maximumCapacity) { + if (maximumCapacity < 0) { + throw new IllegalArgumentException("maximumCapacity must be >= 0: " + maximumCapacity); + } + if (this.size > maximumCapacity) { + maximumCapacity = this.size; + } + if (this.capacity <= maximumCapacity) { + return; + } + maximumCapacity = ObjectMap.nextPowerOfTwo(maximumCapacity); + resize(maximumCapacity); + } + + /** Clears the map and reduces the size of the backing arrays to be the specified capacity if they are larger. */ + public void clear (int maximumCapacity) { + if (this.capacity <= maximumCapacity) { + clear(); + return; + } + this.size = 0; + resize(maximumCapacity); + } + + public void clear () { + K[] keyTable = this.keyTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + keyTable[i] = null; + } + this.size = 0; + this.stashSize = 0; + } + + /** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may be + * an expensive operation. */ + public boolean containsValue (int value) { + int[] valueTable = this.valueTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (valueTable[i] == value) { + return true; + } + } + return false; + } + + public boolean containsKey (K key) { + int hashCode = key.hashCode(); + int index = hashCode & this.mask; + if (!key.equals(this.keyTable[index])) { + index = hash2(hashCode); + if (!key.equals(this.keyTable[index])) { + index = hash3(hashCode); + if (!key.equals(this.keyTable[index])) { + return containsKeyStash(key); + } + } + } + return true; + } + + private boolean containsKeyStash (K key) { + K[] keyTable = this.keyTable; + for (int i = this.capacity, n = i + this.stashSize; i < n; i++) { + if (key.equals(keyTable[i])) { + return true; + } + } + return false; + } + + /** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares + * every value, which may be an expensive operation. */ + public K findKey (int value) { + int[] valueTable = this.valueTable; + for (int i = this.capacity + this.stashSize; i-- > 0;) { + if (valueTable[i] == value) { + return this.keyTable[i]; + } + } + return null; + } + + /** Increases the size of the backing array to acommodate the specified number of additional items. Useful before adding many + * items to avoid multiple backing array resizes. */ + public void ensureCapacity (int additionalCapacity) { + int sizeNeeded = this.size + additionalCapacity; + if (sizeNeeded >= this.threshold) { + resize(ObjectMap.nextPowerOfTwo((int)(sizeNeeded / this.loadFactor))); + } + } + + @SuppressWarnings("unchecked") + private void resize (int newSize) { + int oldEndIndex = this.capacity + this.stashSize; + + this.capacity = newSize; + this.threshold = (int)(newSize * this.loadFactor); + this.mask = newSize - 1; + this.hashShift = 31 - Integer.numberOfTrailingZeros(newSize); + this.stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2); + this.pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8); + + K[] oldKeyTable = this.keyTable; + int[] oldValueTable = this.valueTable; + + this.keyTable = (K[])new Object[newSize + this.stashCapacity]; + this.valueTable = new int[newSize + this.stashCapacity]; + + int oldSize = this.size; + this.size = 0; + this.stashSize = 0; + if (oldSize > 0) { + for (int i = 0; i < oldEndIndex; i++) { + K key = oldKeyTable[i]; + if (key != null) { + putResize(key, oldValueTable[i]); + } + } + } + } + + private int hash2 (int h) { + h *= PRIME2; + return (h ^ h >>> this.hashShift) & this.mask; + } + + private int hash3 (int h) { + h *= PRIME3; + return (h ^ h >>> this.hashShift) & this.mask; + } + + @Override + public String toString () { + if (this.size == 0) { + return "{}"; + } + StringBuilder buffer = new StringBuilder(32); + buffer.append('{'); + K[] keyTable = this.keyTable; + int[] valueTable = this.valueTable; + int i = keyTable.length; + while (i-- > 0) { + K key = keyTable[i]; + if (key == null) { + continue; + } + buffer.append(key); + buffer.append('='); + buffer.append(valueTable[i]); + break; + } + while (i-- > 0) { + K key = keyTable[i]; + if (key == null) { + continue; + } + buffer.append(", "); + buffer.append(key); + buffer.append('='); + buffer.append(valueTable[i]); + } + buffer.append('}'); + return buffer.toString(); + } +} diff --git a/Dorkbox-Util/test/dorkbox/util/MersenneTwisterFastTest.java b/Dorkbox-Util/test/dorkbox/util/MersenneTwisterFastTest.java new file mode 100644 index 0000000..7ce065f --- /dev/null +++ b/Dorkbox-Util/test/dorkbox/util/MersenneTwisterFastTest.java @@ -0,0 +1,239 @@ +package dorkbox.util; + +import java.io.IOException; +import java.util.Random; + +import org.junit.Test; + +public class MersenneTwisterFastTest { + + @Test + public void mersenneTwisterTest() throws IOException { + int j; + + MersenneTwisterFast r; + + // CORRECTNESS TEST + // COMPARE WITH + // http://www.math.keio.ac.jp/matumoto/CODES/MT2002/mt19937ar.out + + r = new MersenneTwisterFast(new int[] {0x123,0x234,0x345,0x456}); + System.out.println("Output of MersenneTwisterFast with new (2002/1/26) seeding mechanism"); + for (j = 0; j < 1000; j++) { + // first, convert the int from signed to "unsigned" + long l = r.nextInt(); + if (l < 0) { + l += 4294967296L; // max int value + } + String s = String.valueOf(l); + while (s.length() < 10) { + s = " " + s; // buffer + } + System.out.print(s + " "); + if (j % 5 == 4) { + System.out.println(); + } + } + + // SPEED TEST + + final long SEED = 4357; + + int xx; + long ms; + System.out.println("\nTime to test grabbing 100000000 ints"); + + Random rr = new Random(SEED); + xx = 0; + ms = System.currentTimeMillis(); + for (j = 0; j < 100000000; j++) { + xx += rr.nextInt(); + } + System.out.println("java.util.Random: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx); + + r = new MersenneTwisterFast(SEED); + ms = System.currentTimeMillis(); + xx = 0; + for (j = 0; j < 100000000; j++) { + xx += r.nextInt(); + } + System.out.println("Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: " + + xx); + + // TEST TO COMPARE TYPE CONVERSION BETWEEN + // MersenneTwisterFast.java AND MersenneTwister.java + + System.out.println("\nGrab the first 1000 booleans"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextBoolean() + " "); + if (j % 8 == 7) { + System.out.println(); + } + } + if (!(j % 8 == 7)) { + System.out.println(); + } + + System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(double)"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextBoolean(j / 999.0) + " "); + if (j % 8 == 7) { + System.out.println(); + } + } + if (!(j % 8 == 7)) { + System.out.println(); + } + + System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(float)"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextBoolean(j / 999.0f) + " "); + if (j % 8 == 7) { + System.out.println(); + } + } + if (!(j % 8 == 7)) { + System.out.println(); + } + + byte[] bytes = new byte[1000]; + System.out.println("\nGrab the first 1000 bytes using nextBytes"); + r = new MersenneTwisterFast(SEED); + r.nextBytes(bytes); + for (j = 0; j < 1000; j++) { + System.out.print(bytes[j] + " "); + if (j % 16 == 15) { + System.out.println(); + } + } + if (!(j % 16 == 15)) { + System.out.println(); + } + + byte b; + System.out.println("\nGrab the first 1000 bytes -- must be same as nextBytes"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print((b = r.nextByte()) + " "); + if (b != bytes[j]) { + System.out.print("BAD "); + } + if (j % 16 == 15) { + System.out.println(); + } + } + if (!(j % 16 == 15)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 shorts"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextShort() + " "); + if (j % 8 == 7) { + System.out.println(); + } + } + if (!(j % 8 == 7)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 ints"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextInt() + " "); + if (j % 4 == 3) { + System.out.println(); + } + } + if (!(j % 4 == 3)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 ints of different sizes"); + r = new MersenneTwisterFast(SEED); + int max = 1; + for (j = 0; j < 1000; j++) { + System.out.print(r.nextInt(max) + " "); + max *= 2; + if (max <= 0) { + max = 1; + } + if (j % 4 == 3) { + System.out.println(); + } + } + if (!(j % 4 == 3)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 longs"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextLong() + " "); + if (j % 3 == 2) { + System.out.println(); + } + } + if (!(j % 3 == 2)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 longs of different sizes"); + r = new MersenneTwisterFast(SEED); + long max2 = 1; + for (j = 0; j < 1000; j++) { + System.out.print(r.nextLong(max2) + " "); + max2 *= 2; + if (max2 <= 0) { + max2 = 1; + } + if (j % 4 == 3) { + System.out.println(); + } + } + if (!(j % 4 == 3)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 floats"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextFloat() + " "); + if (j % 4 == 3) { + System.out.println(); + } + } + if (!(j % 4 == 3)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 doubles"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextDouble() + " "); + if (j % 3 == 2) { + System.out.println(); + } + } + if (!(j % 3 == 2)) { + System.out.println(); + } + + System.out.println("\nGrab the first 1000 gaussian doubles"); + r = new MersenneTwisterFast(SEED); + for (j = 0; j < 1000; j++) { + System.out.print(r.nextGaussian() + " "); + if (j % 3 == 2) { + System.out.println(); + } + } + if (!(j % 3 == 2)) { + System.out.println(); + } + + } +} \ No newline at end of file