This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-rng.git
commit c10923949f78855c1bb567e05de034f43ef2cbb9 Author: Alex Herbert <[email protected]> AuthorDate: Thu Mar 17 21:48:56 2022 +0000 RNG-169: Move all conversions to the Conversions class Remove use of instances of SeedConverter as the conversion is stateless. --- .../rng/simple/internal/ByteArray2IntArray.java | 29 +-- .../rng/simple/internal/ByteArray2LongArray.java | 29 +-- .../commons/rng/simple/internal/Conversions.java | 187 +++++++++++++++-- .../commons/rng/simple/internal/Int2Long.java | 2 +- .../commons/rng/simple/internal/IntArray2Int.java | 7 +- .../rng/simple/internal/IntArray2LongArray.java | 29 +-- .../commons/rng/simple/internal/Long2IntArray.java | 4 +- .../rng/simple/internal/Long2LongArray.java | 4 +- .../rng/simple/internal/LongArray2IntArray.java | 30 +-- .../rng/simple/internal/LongArray2Long.java | 7 +- .../rng/simple/internal/NativeSeedType.java | 40 ++-- .../rng/simple/internal/ConversionsTest.java | 225 +++++++++++++++++---- .../rng/simple/internal/NativeSeedTypeTest.java | 53 +++-- 13 files changed, 417 insertions(+), 229 deletions(-) diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2IntArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2IntArray.java index 832c08c..68f1bdf 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2IntArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2IntArray.java @@ -26,7 +26,7 @@ public class ByteArray2IntArray implements Seed2ArrayConverter<byte[], int[]> { @Override public int[] convert(byte[] seed) { // Full length conversion - return convertSeed(seed, SeedUtils.intSizeFromByteSize(seed.length)); + return Conversions.byteArray2IntArray(seed, SeedUtils.intSizeFromByteSize(seed.length)); } /** @@ -36,31 +36,6 @@ public class ByteArray2IntArray implements Seed2ArrayConverter<byte[], int[]> { */ @Override public int[] convert(byte[] seed, int outputSize) { - return convertSeed(seed, outputSize); - } - - /** - * Creates an array of {@code int} values from a sequence of bytes. The integers are - * filled in little-endian order (least significant byte first). - * - * @param input Input bytes - * @param length Output length - * @return an array of {@code int}. - */ - private static int[] convertSeed(byte[] input, int length) { - final int[] output = new int[length]; - - // Overflow-safe minimum using long - final int n = (int) Math.min(input.length, length * (long) Integer.BYTES); - // Little-endian fill - for (int i = 0; i < n; i++) { - // i = byte index - // i >> 2 = integer index - // i & 0x3 = byte number in the integer [0, 3] - // (i & 0x3) << 3 = little-endian byte shift to the integer {0, 8, 16, 24} - output[i >> 2] |= (input[i] & 0xff) << ((i & 0x3) << 3); - } - - return output; + return Conversions.byteArray2IntArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2LongArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2LongArray.java index 6369f00..4f2957d 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2LongArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/ByteArray2LongArray.java @@ -26,7 +26,7 @@ public class ByteArray2LongArray implements Seed2ArrayConverter<byte[], long[]> @Override public long[] convert(byte[] seed) { // Full length conversion - return convertSeed(seed, SeedUtils.longSizeFromByteSize(seed.length)); + return Conversions.byteArray2LongArray(seed, SeedUtils.longSizeFromByteSize(seed.length)); } /** @@ -36,31 +36,6 @@ public class ByteArray2LongArray implements Seed2ArrayConverter<byte[], long[]> */ @Override public long[] convert(byte[] seed, int outputSize) { - return convertSeed(seed, outputSize); - } - - /** - * Creates an array of {@code long} values from a sequence of bytes. The longs are - * filled in little-endian order (least significant byte first). - * - * @param input Input bytes - * @param length Output length - * @return an array of {@code long}. - */ - private static long[] convertSeed(byte[] input, int length) { - final long[] output = new long[length]; - - // Overflow-safe minimum using long - final int n = (int) Math.min(input.length, length * (long) Long.BYTES); - // Little-endian fill - for (int i = 0; i < n; i++) { - // i = byte index - // i >> 3 = long index - // i & 0x7 = byte number in the long [0, 7] - // (i & 0x7) << 3 = little-endian byte shift to the long {0, 8, 16, 24, 32, 36, 40, 48, 56} - output[i >> 3] |= (input[i] & 0xffL) << ((i & 0x7) << 3); - } - - return output; + return Conversions.byteArray2LongArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Conversions.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Conversions.java index 140f59d..6a82627 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Conversions.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Conversions.java @@ -63,7 +63,7 @@ final class Conversions { * @param input Input * @return a {@code long}. */ - static long int2long(int input) { + static long int2Long(int input) { return stafford13(input + GOLDEN_RATIO); } @@ -77,8 +77,8 @@ final class Conversions { * @param length Array length * @return an {@code int[]}. */ - static int[] int2intArray(int input, int length) { - return long2intArray(input, length); + static int[] int2IntArray(int input, int length) { + return long2IntArray(input, length); } /** @@ -90,8 +90,8 @@ final class Conversions { * @param length Array length * @return a {@code long[]}. */ - static long[] int2longArray(int input, int length) { - return long2longArray(input, length); + static long[] int2LongArray(int input, int length) { + return long2LongArray(input, length); } /** @@ -101,7 +101,7 @@ final class Conversions { * @param input Input * @return an {@code int}. */ - static int long2int(long input) { + static int long2Int(long input) { return (int) input ^ (int) (input >>> 32); } @@ -115,7 +115,7 @@ final class Conversions { * @param length Array length * @return an {@code int[]}. */ - static int[] long2intArray(long input, int length) { + static int[] long2IntArray(long input, int length) { long v = input; final int[] output = new int[length]; // Process pairs @@ -141,7 +141,7 @@ final class Conversions { * @param length Array length * @return a {@code long}. */ - static long[] long2longArray(long input, int length) { + static long[] long2LongArray(long input, int length) { long v = input; final long[] output = new long[length]; for (int i = 0; i < length; i++) { @@ -151,6 +151,124 @@ final class Conversions { } /** + * Creates an {@code int} value from a sequence of ints. The conversion + * is made by combining all the longs with a xor operation. + * + * @param input Input bytes + * @return an {@code int}. + */ + static int intArray2Int(int[] input) { + int output = 0; + for (final int i : input) { + output ^= i; + } + return output; + } + + /** + * Creates a {@code long} value from a sequence of ints. The conversion + * is made as if converting to a {@code long[]} array by filling the longs + * in little-endian order (least significant byte first), then combining + * all the longs with a xor operation. + * + * @param input Input bytes + * @return a {@code long}. + */ + static long intArray2Long(int[] input) { + long output = 0; + + final int n = input.length; + // xor in the bits to a long in little-endian order + for (int i = 0; i < n; i++) { + // i = int index + // i >> 1 = long index + // i & 0x1 = int number in the long [0, 1] + // (i & 0x1) << 5 = little-endian byte shift to the long {0, 32} + output ^= (input[i] & 0xffffffffL) << ((i & 0x1) << 5); + } + + return output; + } + + /** + * Creates a {@code long[]} value from a sequence of ints. The longs are + * filled in little-endian order (least significant byte first). + * + * @param input Input ints + * @param length Output array length + * @return a {@code long[]}. + */ + static long[] intArray2LongArray(int[] input, int length) { + final long[] output = new long[length]; + + // Overflow-safe minimum using long + final int n = (int) Math.min(input.length, length * 2L); + // Little-endian fill + for (int i = 0; i < n; i++) { + // i = int index + // i >> 1 = long index + // i & 0x1 = int number in the long [0, 1] + // (i & 0x1) << 5 = little-endian byte shift to the long {0, 32} + output[i >> 1] |= (input[i] & 0xffffffffL) << ((i & 0x1) << 5); + } + + return output; + } + + /** + * Creates an {@code int} value from a sequence of longs. The conversion + * is made as if combining all the longs with a xor operation, then folding + * the long high and low parts using a xor operation. + * + * @param input Input longs + * @return an {@code int}. + */ + static int longArray2Int(long[] input) { + return Conversions.long2Int(longArray2Long(input)); + } + + /** + * Creates a {@code long} value from a sequence of longs. The conversion + * is made by combining all the longs with a xor operation. + * + * @param input Input longs + * @return a {@code long}. + */ + static long longArray2Long(long[] input) { + long output = 0; + for (final long i : input) { + output ^= i; + } + return output; + } + + /** + * Creates a {@code int[]} value from a sequence of longs. The ints are + * filled in little-endian order (least significant byte first). + * + * @param input Input longs + * @param length Output array length + * @return an {@code int[]}. + */ + static int[] longArray2IntArray(long[] input, int length) { + final int[] output = new int[length]; + + // Overflow-safe minimum using long + final int n = (int) Math.min(input.length * 2L, length); + // Little-endian fill + // Alternate low/high 32-bits from each long + for (int i = 0; i < n; i++) { + // i = int index + // i >> 1 = long index + // i & 0x1 = int number in the long [0, 1] + // (i & 0x1) << 5 = little-endian long shift to the int {0, 32} + output[i] = (int)((input[i >> 1]) >>> ((i & 0x1) << 5)); + } + + return output; + } + + /** * Creates an {@code int} value from a sequence of bytes. The conversion * is made as if converting to a {@code int[]} array by filling the ints * in little-endian order (least significant byte first), then combining @@ -176,6 +294,31 @@ final class Conversions { } /** + * Creates an {@code int[]} value from a sequence of bytes. The ints are + * filled in little-endian order (least significant byte first). + * + * @param input Input bytes + * @param length Output array length + * @return a {@code int[]}. + */ + static int[] byteArray2IntArray(byte[] input, int length) { + final int[] output = new int[length]; + + // Overflow-safe minimum using long + final int n = (int) Math.min(input.length, length * (long) Integer.BYTES); + // Little-endian fill + for (int i = 0; i < n; i++) { + // i = byte index + // i >> 2 = integer index + // i & 0x3 = byte number in the integer [0, 3] + // (i & 0x3) << 3 = little-endian byte shift to the integer {0, 8, 16, 24} + output[i >> 2] |= (input[i] & 0xff) << ((i & 0x3) << 3); + } + + return output; + } + + /** * Creates a {@code long} value from a sequence of bytes. The conversion * is made as if converting to a {@code long[]} array by filling the longs * in little-endian order (least significant byte first), then combining @@ -201,25 +344,25 @@ final class Conversions { } /** - * Creates a {@code long} value from a sequence of ints. The conversion - * is made as if converting to a {@code long[]} array by filling the longs - * in little-endian order (least significant byte first), then combining - * all the longs with a xor operation. + * Creates a {@code long[]} value from a sequence of bytes. The longs are + * filled in little-endian order (least significant byte first). * * @param input Input bytes - * @return a {@code long}. + * @param length Output array length + * @return a {@code long[]}. */ - static long intArray2Long(int[] input) { - long output = 0; + static long[] byteArray2LongArray(byte[] input, int length) { + final long[] output = new long[length]; - final int n = input.length; - // xor in the bits to a long in little-endian order + // Overflow-safe minimum using long + final int n = (int) Math.min(input.length, length * (long) Long.BYTES); + // Little-endian fill for (int i = 0; i < n; i++) { - // i = int index - // i >> 1 = long index - // i & 0x1 = int number in the long [0, 1] - // (i & 0x1) << 5 = little-endian byte shift to the long {0, 32} - output ^= (input[i] & 0xffffffffL) << ((i & 0x1) << 5); + // i = byte index + // i >> 3 = long index + // i & 0x7 = byte number in the long [0, 7] + // (i & 0x7) << 3 = little-endian byte shift to the long {0, 8, 16, 24, 32, 36, 40, 48, 56} + output[i >> 3] |= (input[i] & 0xffL) << ((i & 0x7) << 3); } return output; diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Int2Long.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Int2Long.java index cd1c207..9f23a64 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Int2Long.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Int2Long.java @@ -25,6 +25,6 @@ public class Int2Long implements SeedConverter<Integer, Long> { /** {@inheritDoc} */ @Override public Long convert(Integer seed) { - return Conversions.int2long(seed); + return Conversions.int2Long(seed); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2Int.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2Int.java index 4899258..af742d0 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2Int.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2Int.java @@ -25,11 +25,6 @@ public class IntArray2Int implements SeedConverter<int[], Integer> { /** {@inheritDoc} */ @Override public Integer convert(int[] seed) { - int out = 0; - for (final int s : seed) { - out ^= s; - } - - return out; + return Conversions.intArray2Int(seed); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2LongArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2LongArray.java index c557235..46fbe6e 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2LongArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/IntArray2LongArray.java @@ -30,7 +30,7 @@ public class IntArray2LongArray implements Seed2ArrayConverter<int[], long[]> { @Override public long[] convert(int[] seed) { // Full length conversion - return convertSeed(seed, SeedUtils.longSizeFromIntSize(seed.length)); + return Conversions.intArray2LongArray(seed, SeedUtils.longSizeFromIntSize(seed.length)); } /** @@ -40,31 +40,6 @@ public class IntArray2LongArray implements Seed2ArrayConverter<int[], long[]> { */ @Override public long[] convert(int[] seed, int outputSize) { - return convertSeed(seed, outputSize); - } - - /** - * Creates an array of {@code long} values from a sequence of ints. The longs are - * filled in little-endian order (least significant byte first). - * - * @param input Input bytes - * @param length Output length - * @return an array of {@code long}. - */ - private static long[] convertSeed(int[] input, int length) { - final long[] output = new long[length]; - - // Overflow-safe minimum using long - final int n = (int) Math.min(input.length, length * 2L); - // Little-endian fill - for (int i = 0; i < n; i++) { - // i = int index - // i >> 1 = long index - // i & 0x1 = int number in the long [0, 1] - // (i & 0x1) << 5 = little-endian byte shift to the long {0, 32} - output[i >> 1] |= (input[i] & 0xffffffffL) << ((i & 0x1) << 5); - } - - return output; + return Conversions.intArray2LongArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2IntArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2IntArray.java index 352ca50..4b78632 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2IntArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2IntArray.java @@ -38,7 +38,7 @@ public class Long2IntArray implements Seed2ArrayConverter<Long, int[]> { /** {@inheritDoc} */ @Override public int[] convert(Long seed) { - return Conversions.long2intArray(seed, size); + return Conversions.long2IntArray(seed, size); } /** @@ -48,6 +48,6 @@ public class Long2IntArray implements Seed2ArrayConverter<Long, int[]> { */ @Override public int[] convert(Long seed, int outputSize) { - return Conversions.long2intArray(seed, outputSize); + return Conversions.long2IntArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2LongArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2LongArray.java index 1806ca9..652e08c 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2LongArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/Long2LongArray.java @@ -39,7 +39,7 @@ public class Long2LongArray implements Seed2ArrayConverter<Long, long[]> { /** {@inheritDoc} */ @Override public long[] convert(Long seed) { - return Conversions.long2longArray(seed, size); + return Conversions.long2LongArray(seed, size); } /** @@ -49,6 +49,6 @@ public class Long2LongArray implements Seed2ArrayConverter<Long, long[]> { */ @Override public long[] convert(Long seed, int outputSize) { - return Conversions.long2longArray(seed, outputSize); + return Conversions.long2LongArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2IntArray.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2IntArray.java index d33f4ea..c35e10b 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2IntArray.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2IntArray.java @@ -29,7 +29,7 @@ public class LongArray2IntArray implements Seed2ArrayConverter<long[], int[]> { @Override public int[] convert(long[] seed) { // Full length conversion - return convertSeed(seed, SeedUtils.intSizeFromLongSize(seed.length)); + return Conversions.longArray2IntArray(seed, SeedUtils.intSizeFromLongSize(seed.length)); } /** @@ -39,32 +39,6 @@ public class LongArray2IntArray implements Seed2ArrayConverter<long[], int[]> { */ @Override public int[] convert(long[] seed, int outputSize) { - return convertSeed(seed, outputSize); - } - - /** - * Creates an array of {@code int} values from a sequence of bytes. The integers are - * filled in little-endian order (least significant byte first). - * - * @param input Input bytes - * @param length Output length - * @return an array of {@code int}. - */ - private static int[] convertSeed(long[] input, int length) { - final int[] output = new int[length]; - - // Overflow-safe minimum using long - final int n = (int) Math.min(input.length * 2L, length); - // Little-endian fill - // Alternate low/high 32-bits from each long - for (int i = 0; i < n; i++) { - // i = int index - // i >> 1 = long index - // i & 0x1 = int number in the long [0, 1] - // (i & 0x1) << 5 = little-endian long shift to the int {0, 32} - output[i] = (int)((input[i >> 1]) >>> ((i & 0x1) << 5)); - } - - return output; + return Conversions.longArray2IntArray(seed, outputSize); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2Long.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2Long.java index 67abb1a..39257a3 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2Long.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/LongArray2Long.java @@ -25,11 +25,6 @@ public class LongArray2Long implements SeedConverter<long[], Long> { /** {@inheritDoc} */ @Override public Long convert(long[] seed) { - long out = 0; - for (final long s : seed) { - out ^= s; - } - - return out; + return Conversions.longArray2Long(seed); } } diff --git a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java index 6ccc436..494ff1c 100644 --- a/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java +++ b/commons-rng-simple/src/main/java/org/apache/commons/rng/simple/internal/NativeSeedType.java @@ -54,15 +54,15 @@ public enum NativeSeedType { } @Override protected Integer convert(Long seed, int size) { - return Conversions.long2int(seed); + return Conversions.long2Int(seed); } @Override protected Integer convert(int[] seed, int size) { - return INT_ARRAY_TO_INT.convert(seed); + return Conversions.intArray2Int(seed); } @Override protected Integer convert(long[] seed, int size) { - return LONG_TO_INT.convert(LONG_ARRAY_TO_LONG.convert(seed)); + return Conversions.longArray2Int(seed); } @Override protected Integer convert(byte[] seed, int size) { @@ -77,7 +77,7 @@ public enum NativeSeedType { } @Override protected Long convert(Integer seed, int size) { - return Conversions.int2long(seed); + return Conversions.int2Long(seed); } @Override protected Long convert(Long seed, int size) { @@ -89,7 +89,7 @@ public enum NativeSeedType { } @Override protected Long convert(long[] seed, int size) { - return LONG_ARRAY_TO_LONG.convert(seed); + return Conversions.longArray2Long(seed); } @Override protected Long convert(byte[] seed, int size) { @@ -106,11 +106,11 @@ public enum NativeSeedType { } @Override protected int[] convert(Integer seed, int size) { - return Conversions.int2intArray(seed, size); + return Conversions.int2IntArray(seed, size); } @Override protected int[] convert(Long seed, int size) { - return Conversions.long2intArray(seed, size); + return Conversions.long2IntArray(seed, size); } @Override protected int[] convert(int[] seed, int size) { @@ -119,13 +119,13 @@ public enum NativeSeedType { @Override protected int[] convert(long[] seed, int size) { // Avoid zero filling seeds that are too short - return LONG_ARRAY_TO_INT_ARRAY.convert(seed, + return Conversions.longArray2IntArray(seed, Math.min(size, SeedUtils.intSizeFromLongSize(seed.length))); } @Override protected int[] convert(byte[] seed, int size) { // Avoid zero filling seeds that are too short - return BYTE_ARRAY_TO_INT_ARRAY.convert(seed, + return Conversions.byteArray2IntArray(seed, Math.min(size, SeedUtils.intSizeFromByteSize(seed.length))); } }, @@ -139,16 +139,16 @@ public enum NativeSeedType { } @Override protected long[] convert(Integer seed, int size) { - return Conversions.int2longArray(seed, size); + return Conversions.int2LongArray(seed, size); } @Override protected long[] convert(Long seed, int size) { - return Conversions.long2longArray(seed, size); + return Conversions.long2LongArray(seed, size); } @Override protected long[] convert(int[] seed, int size) { // Avoid zero filling seeds that are too short - return INT_ARRAY_TO_LONG_ARRAY.convert(seed, + return Conversions.intArray2LongArray(seed, Math.min(size, SeedUtils.longSizeFromIntSize(seed.length))); } @Override @@ -158,7 +158,7 @@ public enum NativeSeedType { @Override protected long[] convert(byte[] seed, int size) { // Avoid zero filling seeds that are too short - return BYTE_ARRAY_TO_LONG_ARRAY.convert(seed, + return Conversions.byteArray2LongArray(seed, Math.min(size, SeedUtils.longSizeFromByteSize(seed.length))); } }; @@ -167,20 +167,6 @@ public enum NativeSeedType { private static final String UNRECOGNISED_SEED = "Unrecognized seed type: "; /** Maximum length of the seed array (for creating array seeds). */ private static final int RANDOM_SEED_ARRAY_SIZE = 128; - /** Convert {@code Long} to {@code Integer}. */ - private static final Long2Int LONG_TO_INT = new Long2Int(); - /** Convert {@code long[]} to {@code Long}. */ - private static final LongArray2Long LONG_ARRAY_TO_LONG = new LongArray2Long(); - /** Convert {@code int[]} to {@code Integer}. */ - private static final IntArray2Int INT_ARRAY_TO_INT = new IntArray2Int(); - /** Convert {@code long[]} to {@code int[]}. */ - private static final LongArray2IntArray LONG_ARRAY_TO_INT_ARRAY = new LongArray2IntArray(); - /** Convert {@code Long} to {@code long[]}. */ - private static final IntArray2LongArray INT_ARRAY_TO_LONG_ARRAY = new IntArray2LongArray(); - /** Convert {@code byte[]} to {@code int[]}. */ - private static final ByteArray2IntArray BYTE_ARRAY_TO_INT_ARRAY = new ByteArray2IntArray(); - /** Convert {@code byte[]} to {@code long[]}. */ - private static final ByteArray2LongArray BYTE_ARRAY_TO_LONG_ARRAY = new ByteArray2LongArray(); /** Define the class type of the native seed. */ private final Class<?> type; diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/ConversionsTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/ConversionsTest.java index 840eefd..b2eef2c 100644 --- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/ConversionsTest.java +++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/ConversionsTest.java @@ -52,18 +52,27 @@ class ConversionsTest { return IntStream.rangeClosed(0, (Long.BYTES / Integer.BYTES) * 2); } + /** + * Gets the lengths for the long[] seeds to convert. + * + * @return the lengths + */ + static IntStream getLongLengths() { + return IntStream.rangeClosed(0, 2); + } + @RepeatedTest(value = 5) void testInt2Long() { final int v = ThreadLocalRandom.current().nextInt(); - Assertions.assertEquals(new SplitMix64(v).nextLong(), Conversions.int2long(v)); + Assertions.assertEquals(new SplitMix64(v).nextLong(), Conversions.int2Long(v)); } @RepeatedTest(value = 5) void testInt2IntArray() { final int v = ThreadLocalRandom.current().nextInt(); getIntLengths().forEach(len -> { - Assertions.assertArrayEquals(Conversions.long2intArray(v, len), - Conversions.int2intArray(v, len)); + Assertions.assertArrayEquals(Conversions.long2IntArray(v, len), + Conversions.int2IntArray(v, len)); }); } @@ -71,12 +80,12 @@ class ConversionsTest { void testInt2LongArray() { final int v = ThreadLocalRandom.current().nextInt(); getIntLengths().forEach(len -> { - final long[] a = Conversions.int2longArray(v, len); - Assertions.assertArrayEquals(Conversions.long2longArray(v, len), a); + final long[] a = Conversions.int2LongArray(v, len); + Assertions.assertArrayEquals(Conversions.long2LongArray(v, len), a); if (len != 0) { // Special case of expansion to length 1 // Expandion is done by mixing - Assertions.assertEquals(Conversions.int2long(v), a[0]); + Assertions.assertEquals(Conversions.int2Long(v), a[0]); } }); } @@ -84,7 +93,7 @@ class ConversionsTest { @RepeatedTest(value = 5) void testLong2Int() { final long v = ThreadLocalRandom.current().nextLong(); - Assertions.assertEquals(NumberFactory.makeInt(v), Conversions.long2int(v)); + Assertions.assertEquals(NumberFactory.makeInt(v), Conversions.long2Int(v)); } @RepeatedTest(value = 5) @@ -101,7 +110,7 @@ class ConversionsTest { expected[i] = bb.getInt(); } Assertions.assertArrayEquals(expected, - Conversions.long2intArray(v, len)); + Conversions.long2IntArray(v, len)); // Note: // long -> int[] position[0] != long -> int @@ -114,12 +123,131 @@ class ConversionsTest { final long v = ThreadLocalRandom.current().nextLong(); getIntLengths().forEach(len -> { Assertions.assertArrayEquals(LongStream.generate(new SplitMix64(v)::nextLong).limit(len).toArray(), - Conversions.long2longArray(v, len)); + Conversions.long2LongArray(v, len)); }); } @ParameterizedTest @MethodSource(value = {"getIntLengths"}) + void testIntArray2Int(int ints) { + final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); + // xor all the bytes + int expected = 0; + for (final int i : seed) { + expected ^= i; + } + Assertions.assertEquals(expected, Conversions.intArray2Int(seed)); + } + + @ParameterizedTest + @MethodSource(value = {"getIntLengths"}) + void testIntArray2Long(int ints) { + final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); + + // int[] -> long[] -> long + // Concatenate all ints in little-endian order to bytes + final int outLength = SeedUtils.longSizeFromIntSize(ints); + final int[] filledSeed = Arrays.copyOf(seed, outLength * 2); + final ByteBuffer bb = ByteBuffer.allocate(filledSeed.length * Integer.BYTES) + .order(ByteOrder.LITTLE_ENDIAN); + Arrays.stream(filledSeed).forEach(bb::putInt); + // xor all the bytes read as longs + long expected = 0; + bb.flip(); + for (int i = outLength; i-- != 0;) { + final long l = bb.getLong(); + expected ^= l; + } + + Assertions.assertEquals(expected, Conversions.intArray2Long(seed)); + } + + @ParameterizedTest + @MethodSource(value = {"getIntLengths"}) + void testIntArray2LongComposed(int ints) { + final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); + final long expected = new LongArray2Long().convert(new IntArray2LongArray().convert(seed)); + Assertions.assertEquals(expected, Conversions.intArray2Long(seed)); + } + + @ParameterizedTest + @MethodSource(value = {"getIntLengths"}) + void testIntArray2LongArray(int ints) { + final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); + + // Concatenate all bytes in little-endian order to bytes + final int outLength = SeedUtils.longSizeFromIntSize(ints); + final ByteBuffer bb = ByteBuffer.allocate(outLength * Long.BYTES) + .order(ByteOrder.LITTLE_ENDIAN); + Arrays.stream(seed).forEach(bb::putInt); + bb.clear(); + final long[] expected = new long[outLength]; + for (int i = 0; i < outLength; i++) { + expected[i] = bb.getLong(); + } + + Assertions.assertArrayEquals(expected, Conversions.intArray2LongArray(seed, outLength)); + // Zero fill + Assertions.assertArrayEquals(Arrays.copyOf(expected, outLength * 2), + Conversions.intArray2LongArray(seed, outLength * 2)); + // Truncation + for (int i = 0; i < outLength; i++) { + Assertions.assertArrayEquals(Arrays.copyOf(expected, i), Conversions.intArray2LongArray(seed, i)); + } + } + + @ParameterizedTest + @MethodSource(value = {"getLongLengths"}) + void testLongArray2Int(long longs) { + final long[] seed = ThreadLocalRandom.current().longs(longs).toArray(); + // xor all the bytes + long expected = 0; + for (final long i : seed) { + expected ^= i; + } + Assertions.assertEquals((int) (expected ^ expected >>> 32), Conversions.longArray2Int(seed)); + } + + @ParameterizedTest + @MethodSource(value = {"getLongLengths"}) + void testLongArray2Long(long longs) { + final long[] seed = ThreadLocalRandom.current().longs(longs).toArray(); + // xor all the bytes + long expected = 0; + for (final long i : seed) { + expected ^= i; + } + Assertions.assertEquals(expected, Conversions.longArray2Long(seed)); + } + + @ParameterizedTest + @MethodSource(value = {"getLongLengths"}) + void testLongArray2IntArray(int longs) { + final long[] seed = ThreadLocalRandom.current().longs(longs).toArray(); + + // Concatenate all bytes in little-endian order to bytes + final int outLength = SeedUtils.intSizeFromLongSize(longs); + final ByteBuffer bb = ByteBuffer.allocate(longs * Long.BYTES) + .order(ByteOrder.LITTLE_ENDIAN); + Arrays.stream(seed).forEach(bb::putLong); + bb.clear(); + final int[] expected = new int[outLength]; + for (int i = 0; i < outLength; i++) { + expected[i] = bb.getInt(); + } + + Assertions.assertArrayEquals(expected, Conversions.longArray2IntArray(seed, outLength)); + // Zero fill + Assertions.assertArrayEquals(Arrays.copyOf(expected, outLength * 2), + Conversions.longArray2IntArray(seed, outLength * 2)); + // Truncation + for (int i = 0; i < outLength; i++) { + Assertions.assertArrayEquals(Arrays.copyOf(expected, i), Conversions.longArray2IntArray(seed, i)); + } + } + + @ParameterizedTest + @MethodSource(value = {"getByteLengths"}) void testByteArray2Int(int bytes) { final byte[] seed = new byte[bytes]; ThreadLocalRandom.current().nextBytes(seed); @@ -133,7 +261,7 @@ class ConversionsTest { // xor all the bytes read as ints int expected = 0; for (int i = outLength; i-- != 0;) { - long l = bb.getInt(); + final long l = bb.getInt(); expected ^= l; } @@ -141,7 +269,7 @@ class ConversionsTest { } @ParameterizedTest - @MethodSource(value = {"getIntLengths"}) + @MethodSource(value = {"getByteLengths"}) void testByteArray2IntComposed(int bytes) { final byte[] seed = new byte[bytes]; ThreadLocalRandom.current().nextBytes(seed); @@ -150,7 +278,33 @@ class ConversionsTest { } @ParameterizedTest - @MethodSource(value = {"getIntLengths"}) + @MethodSource(value = {"getByteLengths"}) + void testByteArray2IntArray(int bytes) { + final byte[] seed = new byte[bytes]; + ThreadLocalRandom.current().nextBytes(seed); + + // Concatenate all bytes in little-endian order to bytes + final int outLength = SeedUtils.intSizeFromByteSize(bytes); + final byte[] filledSeed = Arrays.copyOf(seed, outLength * Integer.BYTES); + final ByteBuffer bb = ByteBuffer.wrap(filledSeed) + .order(ByteOrder.LITTLE_ENDIAN); + final int[] expected = new int[outLength]; + for (int i = 0; i < outLength; i++) { + expected[i] = bb.getInt(); + } + + Assertions.assertArrayEquals(expected, Conversions.byteArray2IntArray(seed, outLength)); + // Zero fill + Assertions.assertArrayEquals(Arrays.copyOf(expected, outLength * 2), + Conversions.byteArray2IntArray(seed, outLength * 2)); + // Truncation + for (int i = 0; i < outLength; i++) { + Assertions.assertArrayEquals(Arrays.copyOf(expected, i), Conversions.byteArray2IntArray(seed, i)); + } + } + + @ParameterizedTest + @MethodSource(value = {"getByteLengths"}) void testByteArray2Long(int bytes) { final byte[] seed = new byte[bytes]; ThreadLocalRandom.current().nextBytes(seed); @@ -164,7 +318,7 @@ class ConversionsTest { // xor all the bytes read as longs long expected = 0; for (int i = outLength; i-- != 0;) { - long l = bb.getLong(); + final long l = bb.getLong(); expected ^= l; } @@ -172,7 +326,7 @@ class ConversionsTest { } @ParameterizedTest - @MethodSource(value = {"getIntLengths"}) + @MethodSource(value = {"getByteLengths"}) void testByteArray2LongComposed(int bytes) { final byte[] seed = new byte[bytes]; ThreadLocalRandom.current().nextBytes(seed); @@ -181,33 +335,28 @@ class ConversionsTest { } @ParameterizedTest - @MethodSource(value = {"getIntLengths"}) - void testIntArray2Long(int ints) { - final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); + @MethodSource(value = {"getByteLengths"}) + void testByteArray2LongArray(int bytes) { + final byte[] seed = new byte[bytes]; + ThreadLocalRandom.current().nextBytes(seed); - // int[] -> long[] -> long - // Concatenate all ints in little-endian order to bytes - final int outLength = SeedUtils.longSizeFromIntSize(ints); - final int[] filledSeed = Arrays.copyOf(seed, outLength * 2); - final ByteBuffer bb = ByteBuffer.allocate(filledSeed.length * Integer.BYTES) + // Concatenate all bytes in little-endian order to bytes + final int outLength = SeedUtils.longSizeFromByteSize(bytes); + final byte[] filledSeed = Arrays.copyOf(seed, outLength * Long.BYTES); + final ByteBuffer bb = ByteBuffer.wrap(filledSeed) .order(ByteOrder.LITTLE_ENDIAN); - Arrays.stream(filledSeed).forEach(bb::putInt); - // xor all the bytes read as longs - long expected = 0; - bb.flip(); - for (int i = outLength; i-- != 0;) { - long l = bb.getLong(); - expected ^= l; + final long[] expected = new long[outLength]; + for (int i = 0; i < outLength; i++) { + expected[i] = bb.getLong(); } - Assertions.assertEquals(expected, Conversions.intArray2Long(seed)); - } - - @ParameterizedTest - @MethodSource(value = {"getIntLengths"}) - void testIntArray2LongComposed(int ints) { - final int[] seed = ThreadLocalRandom.current().ints(ints).toArray(); - final long expected = new LongArray2Long().convert(new IntArray2LongArray().convert(seed)); - Assertions.assertEquals(expected, Conversions.intArray2Long(seed)); + Assertions.assertArrayEquals(expected, Conversions.byteArray2LongArray(seed, outLength)); + // Zero fill + Assertions.assertArrayEquals(Arrays.copyOf(expected, outLength * 2), + Conversions.byteArray2LongArray(seed, outLength * 2)); + // Truncation + for (int i = 0; i < outLength; i++) { + Assertions.assertArrayEquals(Arrays.copyOf(expected, i), Conversions.byteArray2LongArray(seed, i)); + } } } diff --git a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java index c81b51e..41ae3dc 100644 --- a/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java +++ b/commons-rng-simple/src/test/java/org/apache/commons/rng/simple/internal/NativeSeedTypeTest.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.IntStream; import java.util.stream.LongStream; +import org.apache.commons.rng.core.source64.SplitMix64; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -35,49 +36,69 @@ import org.junit.jupiter.params.provider.MethodSource; * <p>Note: All supported types are tested in the {@link NativeSeedTypeParametricTest}. */ class NativeSeedTypeTest { - /** Convert {@code Integer} to {@code Long}. */ - private static final Int2Long INT_TO_LONG = new Int2Long(); - /** Convert {@code Long} to {@code int[]}. */ - private static final Long2IntArray LONG_TO_INT_ARRAY = new Long2IntArray(0); - /** Convert {@code Long} to {@code long[]}. */ - private static final Long2LongArray LONG_TO_LONG_ARRAY = new Long2LongArray(0); - /** * Perform the reference int to long conversion. * This may change between release versions. - * The reference implementation is in the Int2Long converter. + * The reference implementation is to create a long using a SplitMix64 generator. * * @param v Value * @return the result */ private static long int2long(int v) { - return INT_TO_LONG.convert(v); + return new SplitMix64(v).nextLong(); } /** * Perform the reference long to int[] conversion. * This may change between release versions. - * The reference implementation is in the Long2IntArray converter. + * The reference implementation is to create a long[] using a SplitMix64 generator + * and split each into an int, least significant bytes first. * * @param v Value * @param length Array length * @return the result */ private static int[] long2intArray(long v, int length) { - return LONG_TO_INT_ARRAY.convert(v, length); + class LoHiSplitMix64 extends SplitMix64 { + /** Cache part of the most recently generated long value. + * Store the upper 32-bits from nextLong() in the lower half + * and all zero bits in the upper half when cached. + * Set to -1 when empty and requires a refill. */ + private long next = -1; + + LoHiSplitMix64(long seed) { + super(seed); + } + + @Override + public int nextInt() { + long l = next; + if (l < 0) { + l = nextLong(); + // Reserve the upper 32-bits + next = l >>> 32; + // Return the lower 32-bits + return (int) l; + } + // Clear cache and return the previous upper 32-bits + next = -1; + return (int) l; + } + } + return IntStream.generate(new LoHiSplitMix64(v)::nextInt).limit(length).toArray(); } /** * Perform the reference long to long[] conversion. * This may change between release versions. - * The reference implementation is in the Long2LongArray converter. + * The reference implementation is to create a long[] using a SplitMix64 generator. * * @param v Value * @param length Array length * @return the result */ private static long[] long2longArray(long v, int length) { - return LONG_TO_LONG_ARRAY.convert(v, length); + return LongStream.generate(new SplitMix64(v)::nextLong).limit(length).toArray(); } /** @@ -273,11 +294,11 @@ class NativeSeedTypeTest { // - Native seed types are passed through with no change // - long to int conversion uses hi ^ lo // - int to long conversion expands the bits. - // The Int2Long converter is the reference implementation. + // Creating a long using a SplitMix64 is the reference implementation. // - long to long[] conversion seeds a RNG then expands. - // The Long2LongArray converter is the reference implementation. + // Filling using a SplitMix64 is the reference implementation. // - long to int[] conversion seeds a RNG then expands. - // The Long2IntArray converter is the reference implementation. + // Filling using a SplitMix64 is the reference implementation. // - Primitive expansion should produce equivalent output bits // for all larger output seed types, // i.e. int -> long == int -> int[0]+int[1] == int -> long[0]
