From a89bab386371b49c9af21e47cc9fdc37c18e6f43 Mon Sep 17 00:00:00 2001 From: Fabio Lima Date: Sun, 23 Feb 2020 14:03:58 -0300 Subject: [PATCH] Added XorshiftRandom --- .../com/github/f4b6a3/ulid/UlidCreator.java | 2 +- .../f4b6a3/ulid/random/XorshiftRandom.java | 82 +++++++++++++++++++ .../github/f4b6a3/ulid/util/Base32Util.java | 8 +- .../com/github/f4b6a3/ulid/util/UlidUtil.java | 8 +- .../f4b6a3/ulid/random/NaiveRandomTest.java | 17 ++++ 5 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/github/f4b6a3/ulid/random/XorshiftRandom.java diff --git a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java index bf06d28..9cbe579 100644 --- a/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java +++ b/src/main/java/com/github/f4b6a3/ulid/UlidCreator.java @@ -32,7 +32,7 @@ import com.github.f4b6a3.ulid.guid.GuidCreator; /** * A factory for Universally Unique Lexicographically Sortable Identifiers. * - * @see The ULID spec; https://github.com/ulid/spec + * See the ULID spec: https://github.com/ulid/spec */ public class UlidCreator { diff --git a/src/main/java/com/github/f4b6a3/ulid/random/XorshiftRandom.java b/src/main/java/com/github/f4b6a3/ulid/random/XorshiftRandom.java new file mode 100644 index 0000000..7a89cb1 --- /dev/null +++ b/src/main/java/com/github/f4b6a3/ulid/random/XorshiftRandom.java @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2018-2020 Fabio Lima + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.f4b6a3.ulid.random; + +import java.util.Random; + +/** + * A subclass of {@link java.util.Random} that implements the Xorshift random + * number generator. + * + * https://en.wikipedia.org/wiki/Xorshift + * + * Reference: + * + * George Marsaglia. 2003. Xorshift RNGs. Journal of Statistical Software 8, 14 + * (2003), 1–6. https://www.jstatsoft.org/article/view/v008i14 + * + */ +public class XorshiftRandom extends Random { + + private static final long serialVersionUID = 5084310156945573858L; + + private long seed; + private static int count; + + public XorshiftRandom() { + this((int) System.nanoTime()); + } + + /** + * Constructor that receives an integer as 'salt'. This value is combined + * with the current milliseconds to generate the seed. + * + * @param salt + * a number used to generate the seed. + */ + public XorshiftRandom(int salt) { + long time = System.currentTimeMillis() + count++; + this.seed = (((long) salt) << 32) | (time & 0x00000000ffffffffL); + } + + public XorshiftRandom(long seed) { + this.seed = seed; + } + + @Override + protected int next(int bits) { + return (int) (nextLong() >>> (64 - bits)); + } + + @Override + public long nextLong() { + long x = this.seed; + x ^= (x << 13); + x ^= (x >>> 7); + x ^= (x << 17); + this.seed = x; + return x; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/f4b6a3/ulid/util/Base32Util.java b/src/main/java/com/github/f4b6a3/ulid/util/Base32Util.java index 8b075ae..eeec394 100644 --- a/src/main/java/com/github/f4b6a3/ulid/util/Base32Util.java +++ b/src/main/java/com/github/f4b6a3/ulid/util/Base32Util.java @@ -209,8 +209,8 @@ public class Base32Util { /** * Convert a string to an array of bytes using UTF-8. * - * @param string - * @return + * @param string a string + * @return a string */ public static byte[] toBytes(String string) { return string.getBytes(StandardCharsets.UTF_8); @@ -219,8 +219,8 @@ public class Base32Util { /** * Convert an array of bytes to a string using UTF-8. * - * @param bytes - * @return + * @param bytes a byte sequence + * @return a string */ public static String toString(byte[] bytes) { return new String(bytes, StandardCharsets.UTF_8); diff --git a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java index 56ffc8c..00d0e6c 100644 --- a/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java +++ b/src/main/java/com/github/f4b6a3/ulid/util/UlidUtil.java @@ -143,11 +143,10 @@ public class UlidUtil { * * The validation mode is not strict. * - * @see {@link UlidUtil#validate(String, boolean)}. + * See {@link UlidUtil#validate(String, boolean)}. * * @param ulid * a ULID - * @return boolean true if valid */ protected static void validate(String ulid) { validate(ulid, false); @@ -156,11 +155,10 @@ public class UlidUtil { /** * Checks if the ULID string is a valid. * - * @see {@link UlidUtil#validate(String, boolean)}. + * See {@link UlidUtil#validate(String, boolean)}. * * @param ulid * a ULID - * @return boolean true if valid */ protected static void validate(String ulid, boolean strict) { if (!isValid(ulid, strict)) { @@ -173,7 +171,7 @@ public class UlidUtil { * * The validation mode is not strict. * - * @see {@link UlidUtil#validate(String, boolean)}. + * See {@link UlidUtil#validate(String, boolean)}. */ public static boolean isValid(String ulid) { return isValid(ulid, false); diff --git a/src/test/java/com/github/f4b6a3/ulid/random/NaiveRandomTest.java b/src/test/java/com/github/f4b6a3/ulid/random/NaiveRandomTest.java index b5226d5..0041649 100644 --- a/src/test/java/com/github/f4b6a3/ulid/random/NaiveRandomTest.java +++ b/src/test/java/com/github/f4b6a3/ulid/random/NaiveRandomTest.java @@ -9,6 +9,23 @@ public class NaiveRandomTest { private static final int DEFAULT_LOOP_LIMIT = 100_000; private static final String EXPECTED_BIT_COUNT_RANDOM_LONG = "The average bit count expected for random long values is 32"; + @Test + public void testXorshiftNextLongNaiveAverageBitCount() { + + double accumulator = 0; + + XorshiftRandom random = new XorshiftRandom(); + + for(int i = 0; i < DEFAULT_LOOP_LIMIT; i++) { + long value = random.nextLong(); + accumulator += Long.bitCount(value); + } + + double average = Math.round(accumulator / DEFAULT_LOOP_LIMIT); + + assertTrue(EXPECTED_BIT_COUNT_RANDOM_LONG, average == 32); + } + @Test public void testXorshift128PlusNextLongNaiveAverageBitCount() {