Conversions.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.commons.rng.simple.internal;
/**
* Performs seed conversions.
*
* <p>Note: Legacy converters from version 1.0 use instances of
* the {@link SeedConverter} interface. Instances are no longer
* required as no state is used during conversion and converters
* can use static methods.
*
* @since 1.5
*/
final class Conversions {
/**
* The fractional part of the golden ratio, phi, scaled to 64-bits and rounded to odd.
* <pre>
* phi = (sqrt(5) - 1) / 2) * 2^64
* </pre>
* @see <a href="https://en.wikipedia.org/wiki/Golden_ratio">Golden ratio</a>
*/
private static final long GOLDEN_RATIO = MixFunctions.GOLDEN_RATIO_64;
/** No instances. */
private Conversions() {}
/**
* Compute the size of an {@code int} array required to hold the specified number of bytes.
* Allows space for any remaining bytes that do not fit exactly in a 4 byte integer.
* <pre>
* n = ceil(size / 4)
* </pre>
*
* @param size the size in bytes (assumed to be positive)
* @return the size in ints
*/
static int intSizeFromByteSize(int size) {
return (size + 3) >>> 2;
}
/**
* Compute the size of an {@code long} array required to hold the specified number of bytes.
* Allows space for any remaining bytes that do not fit exactly in an 8 byte long.
* <pre>
* n = ceil(size / 8)
* </pre>
*
* @param size the size in bytes (assumed to be positive)
* @return the size in longs
*/
static int longSizeFromByteSize(int size) {
return (size + 7) >>> 3;
}
/**
* Compute the size of an {@code int} array required to hold the specified number of longs.
* Prevents overflow to a negative number when doubling the size.
* <pre>
* n = min(size * 2, 2^31 - 1)
* </pre>
*
* @param size the size in longs (assumed to be positive)
* @return the size in ints
*/
static int intSizeFromLongSize(int size) {
// Avoid overflow when doubling the length.
// If n is negative the signed shift creates a mask with all bits set;
// otherwise it is zero and n is unchanged after the or operation.
// The final mask clears the sign bit in the event n did overflow.
final int n = size << 1;
return (n | (n >> 31)) & Integer.MAX_VALUE;
}
/**
* Compute the size of an {@code long} array required to hold the specified number of ints.
* Allows space for an odd int.
* <pre>
* n = ceil(size / 2)
* </pre>
*
* @param size the size in ints (assumed to be positive)
* @return the size in longs
*/
static int longSizeFromIntSize(int size) {
return (size + 1) >>> 1;
}
/**
* Creates a {@code long} value from an {@code int}. The conversion
* is made as if seeding a SplitMix64 RNG and calling nextLong().
*
* @param input Input
* @return a {@code long}.
*/
static long int2Long(int input) {
return MixFunctions.stafford13(input + GOLDEN_RATIO);
}
/**
* Creates an {@code int[]} value from an {@code int}. The conversion
* is made as if seeding a SplitMix64 RNG and calling nextLong() to
* generate the sequence and filling the ints
* in little-endian order (least significant byte first).
*
* @param input Input
* @param length Array length
* @return an {@code int[]}.
*/
static int[] int2IntArray(int input, int length) {
return long2IntArray(input, length);
}
/**
* Creates a {@code long[]} value from an {@code int}. The conversion
* is made as if seeding a SplitMix64 RNG and calling nextLong() to
* generate the sequence.
*
* @param input Input
* @param length Array length
* @return a {@code long[]}.
*/
static long[] int2LongArray(int input, int length) {
return long2LongArray(input, length);
}
/**
* Creates an {@code int} value from a {@code long}. The conversion
* is made by xoring the upper and lower bits.
*
* @param input Input
* @return an {@code int}.
*/
static int long2Int(long input) {
return (int) input ^ (int) (input >>> 32);
}
/**
* Creates an {@code int[]} value from a {@code long}. The conversion
* is made as if seeding a SplitMix64 RNG and calling nextLong() to
* generate the sequence and filling the ints
* in little-endian order (least significant byte first).
*
* <p>A special case is made to avoid an array filled with zeros for
* the initial 2 positions. It is still possible to create a zero in
* position 0. However any RNG with an int[] native type is expected to
* require at least 2 int values.
*
* @param input Input
* @param length Array length
* @return an {@code int[]}.
*/
static int[] long2IntArray(long input, int length) {
// Special case to avoid creating a zero-filled array of length 2.
long v = input == -GOLDEN_RATIO ? ~input : input;
final int[] output = new int[length];
// Process pairs
final int n = length & ~0x1;
for (int i = 0; i < n; i += 2) {
v += GOLDEN_RATIO;
final long x = MixFunctions.stafford13(v);
output[i] = (int) x;
output[i + 1] = (int) (x >>> 32);
}
// Final value
if (n < length) {
output[n] = (int) MixFunctions.stafford13(v + GOLDEN_RATIO);
}
return output;
}
/**
* Creates a {@code long[]} value from a {@code long}. The conversion
* is made as if seeding a SplitMix64 RNG and calling nextLong() to
* generate the sequence.
*
* @param input Input
* @param length Array length
* @return a {@code long}.
*/
static long[] long2LongArray(long input, int length) {
long v = input;
final long[] output = new long[length];
for (int i = 0; i < length; i++) {
v += GOLDEN_RATIO;
output[i] = MixFunctions.stafford13(v);
}
return output;
}
/**
* 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 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
* all the ints with a xor operation.
*
* @param input Input bytes
* @return an {@code int}.
*/
static int byteArray2Int(byte[] input) {
int output = 0;
final int n = input.length;
// xor in the bits to an int in little-endian order
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 ^= (input[i] & 0xff) << ((i & 0x3) << 3);
}
return output;
}
/**
* 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
* all the longs with a xor operation.
*
* @param input Input bytes
* @return a {@code long}.
*/
static long byteArray2Long(byte[] 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 = 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 ^= (input[i] & 0xffL) << ((i & 0x7) << 3);
}
return output;
}
/**
* 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
* @param length Output array length
* @return a {@code long[]}.
*/
static long[] byteArray2LongArray(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;
}
}