Added MersenneTwisterFast (replaces java.util.Random), added missing licenses. Added field annonation exclusion serilaizer. Code polish
This commit is contained in:
parent
bbea13e42d
commit
142ab8a383
94
Dorkbox-Util/LICENSE.TXT
Normal file
94
Dorkbox-Util/LICENSE.TXT
Normal file
@ -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 <badlogicgames@gmail.com>
|
||||||
|
Nathan Sweet <nathan.sweet@gmail.com>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- 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.
|
89
Dorkbox-Util/src/dorkbox/util/MathUtils.java
Normal file
89
Dorkbox-Util/src/dorkbox/util/MathUtils.java
Normal file
@ -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<MersenneTwisterFast> random = new ThreadLocal<MersenneTwisterFast>();
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
}
|
||||||
|
}
|
1233
Dorkbox-Util/src/dorkbox/util/MersenneTwisterFast.java
Normal file
1233
Dorkbox-Util/src/dorkbox/util/MersenneTwisterFast.java
Normal file
File diff suppressed because it is too large
Load Diff
384
Dorkbox-Util/src/dorkbox/util/primativeCollections/IntArray.java
Normal file
384
Dorkbox-Util/src/dorkbox/util/primativeCollections/IntArray.java
Normal file
@ -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<Integer>. 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();
|
||||||
|
}
|
||||||
|
}
|
870
Dorkbox-Util/src/dorkbox/util/primativeCollections/IntMap.java
Normal file
870
Dorkbox-Util/src/dorkbox/util/primativeCollections/IntMap.java
Normal file
@ -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. <br>
|
||||||
|
* <br>
|
||||||
|
* 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<V> {
|
||||||
|
@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<V> map) {
|
||||||
|
for (Entry<V> 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 <tt>notFound</tt> 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<V> 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<V> 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<V> {
|
||||||
|
public int key;
|
||||||
|
public V value;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString () {
|
||||||
|
return this.key + "=" + this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static private class MapIterator<V> {
|
||||||
|
static final int INDEX_ILLEGAL = -2;
|
||||||
|
static final int INDEX_ZERO = -1;
|
||||||
|
|
||||||
|
public boolean hasNext;
|
||||||
|
|
||||||
|
final IntMap<V> map;
|
||||||
|
int nextIndex, currentIndex;
|
||||||
|
boolean valid = true;
|
||||||
|
|
||||||
|
public MapIterator (IntMap<V> 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<V> extends MapIterator<V> implements Iterable<Entry<V>>, Iterator<Entry<V>> {
|
||||||
|
private Entry<V> 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<V> 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<Entry<V>> iterator () {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static public class Values<V> extends MapIterator<V> implements Iterable<V>, Iterator<V> {
|
||||||
|
public Values (IntMap<V> 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<V> iterator () {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a new array containing the remaining values. */
|
||||||
|
// public Array<V> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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. <br>
|
||||||
|
* <br>
|
||||||
|
* 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<K> {
|
||||||
|
@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<? extends K> 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();
|
||||||
|
}
|
||||||
|
}
|
239
Dorkbox-Util/test/dorkbox/util/MersenneTwisterFastTest.java
Normal file
239
Dorkbox-Util/test/dorkbox/util/MersenneTwisterFastTest.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user