blob: b947b52c5ab56bff74f2eb4f2e0493c07e4ee53b [file] [log] [blame]
/*
* 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.core.source32;
import java.util.stream.Stream;
import org.apache.commons.rng.LongJumpableUniformRandomProvider;
import org.apache.commons.rng.SplittableUniformRandomProvider;
import org.apache.commons.rng.UniformRandomProvider;
import org.apache.commons.rng.core.RandomAssert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/**
* Test for {@link L32X64Mix}.
*/
class L32X64MixTest extends AbstractLXMTest {
/**
* Factory to create a composite LXM generator that is equivalent
* to the RNG under test.
*/
private static class Factory implements LXMGeneratorFactory {
static final Factory INSTANCE = new Factory();
@Override
public int lcgSeedSize() {
return 2;
}
@Override
public int xbgSeedSize() {
return 2;
}
@Override
public LXMGenerator create(int[] seed) {
return new LXMGenerator(getMix(),
new LCG32(seed[0], seed[1]),
new XBGXoRoShiRo64(seed[2], seed[3]));
}
}
@Override
LXMGeneratorFactory getFactory() {
return Factory.INSTANCE;
}
@Override
LongJumpableUniformRandomProvider create(int[] seed) {
return new L32X64Mix(seed);
}
@Override
Stream<Arguments> getReferenceData() {
/*
* Reference data from JDK 17:
* java.util.random.RandomGeneratorFactory.of("L32X64MixRandom").create(seed)
*
* Full byte[] seed created using SecureRandom.nextBytes. The seed was converted
* to int[] by filling the long bits sequentially starting at the most
* significant byte matching the method used by JDK 17. A sign extension bug in
* byte[] conversion required all byte indices (i % 4 != 0) to be non-negative.
* See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8282144
*
* Note: Seed order: LCG addition; LCG state; XBG state.
*/
return Stream.of(
Arguments.of(
new int[] {
0x5a16253f, 0xd449657e, 0x5b46012d, 0x1d504d64,
},
new int[] {
0xa6cc8f9b, 0xb9d3f0e3, 0xb8861d42, 0x9f8001a2,
0xaf1eea5b, 0x4e3bc947, 0x1c6378b8, 0x54ccc942,
0x4629da71, 0x0126a7e7, 0x6038f89c, 0xff72cb5c,
0x3853ae58, 0xc86ccf16, 0x02d32147, 0x78afed22,
0x432194a4, 0xf79809aa, 0x8c115507, 0x646b0445,
0xed63cc9c, 0x4e1d7b55, 0x699cf21d, 0x7d2adf74,
0xc2613634, 0x5ac9c96f, 0x1f2e59d2, 0x4c9878fb,
0x49c7a137, 0xd277cdf8, 0x369dd190, 0x1edeb2dc,
0x10bd09bd, 0x86e5140c, 0xe6bcb04c, 0x67f97775,
0x8f979b43, 0x36dfa266, 0x3acf5157, 0xe69aac08,
}),
Arguments.of(
new int[] {
0xa3512c5b, 0x970f3100, 0xfc22313b, 0xa71f183a,
},
new int[] {
0x971dc6d7, 0xd8200b05, 0x0093d467, 0x74f11b96,
0x8945aa8f, 0x88a3d635, 0xb1fcb212, 0x66ed8543,
0xd5970056, 0x7b8b2d89, 0xd2d3957b, 0xc4ecc777,
0xf8e295c3, 0x4ffb4a29, 0x509fc6b0, 0x9c5b1965,
0x5008c757, 0x3a19410a, 0xc3992498, 0x0c4a8f22,
0x3f6abfd8, 0xe943ac45, 0xb632fcd3, 0x3d1f3201,
0xb17bc9f5, 0x902a9b9c, 0x39a76388, 0xd8c856a0,
0x7422cf86, 0x3f776592, 0xef70a122, 0x053b628e,
0x0dec8c4e, 0x5b0d80c9, 0xf58d19fc, 0x741f1361,
0x01b747f7, 0x6b325a32, 0x714ed761, 0xf1facf8e,
}),
Arguments.of(
new int[] {
0x17767167, 0x1b6e286c, 0xe84a2f12, 0x637a5034,
},
new int[] {
0xd69fd4c1, 0x134945f8, 0x34c2fd4e, 0xd152b08f,
0xb5a7e2bd, 0xd8afab0d, 0xed15f9d2, 0x6baba06e,
0x9a1e8bcd, 0x8b392fbd, 0xb1e1cad9, 0x929f06d9,
0xc9467860, 0x3d492690, 0x84fd7d06, 0xb576b0f9,
0x860eac4c, 0xd3b7aa5b, 0x3ff4dd13, 0xfea00d01,
0x025143a0, 0xf1c5885a, 0x3041a8be, 0xbfbfb908,
0x186cd9cc, 0x1771875b, 0x9cc65fe2, 0xc9189702,
0x6063b09d, 0x20f0286b, 0x6094b83d, 0x8c3fe8f5,
0x2bf720eb, 0xe229e207, 0xe33f93db, 0x443b3849,
0x79cc70a8, 0xcbf0de84, 0xe57c6367, 0xe555ee21,
}));
}
@ParameterizedTest
@MethodSource(value = "getReferenceData")
void testElementConstructor(int[] seed, int[] expected) {
final L32X64Mix rng1 = new L32X64Mix(seed);
final L32X64Mix rng2 = new L32X64Mix(seed[0], seed[1], seed[2], seed[3]);
RandomAssert.assertNextLongEquals(seed.length * 2, rng1, rng2);
}
/**
* Test split with zero bits from the source. This should be robust to escape the state
* of all zero bits that will create an invalid state for the xor-based generator (XBG).
*/
@Test
void testSplitWithZeroBits() {
final UniformRandomProvider zeroSource = () -> 0;
final int[] seed = new int[Factory.INSTANCE.seedSize()];
// Here we copy the split which sets the LCG increment to odd
seed[(Factory.INSTANCE.lcgSeedSize() / 2) - 1] = 1;
final SplittableUniformRandomProvider rng1 = new L32X64Mix(seed);
final SplittableUniformRandomProvider rng2 = rng1.split(zeroSource);
RandomAssert.assertNextIntNotEquals(seed.length * 2, rng1, rng2);
// Since we know how the zero seed is amended
int z = 0;
for (int i = Factory.INSTANCE.lcgSeedSize(); i < seed.length; i++) {
seed[i] = LXMSupport.lea32(z);
z += LXMSupport.GOLDEN_RATIO_32;
}
final SplittableUniformRandomProvider rng3 = new L32X64Mix(seed);
final SplittableUniformRandomProvider rng4 = rng1.split(zeroSource);
RandomAssert.assertNextIntEquals(seed.length * 2, rng3, rng4);
}
}