blob: 24bad789e9bbfab57525efebfd64ad2141f378b4 [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.source64;
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 L128X128Mix}.
*/
class L128X128MixTest 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 4;
}
@Override
public int xbgSeedSize() {
return 2;
}
@Override
public LXMGenerator create(long[] seed) {
return new LXMGenerator(getMix(),
new LCG128(seed[0], seed[1], seed[2], seed[3]),
new XBGXoRoShiRo128(seed[4], seed[5], false));
}
}
@Override
LXMGeneratorFactory getFactory() {
return Factory.INSTANCE;
}
@Override
LongJumpableUniformRandomProvider create(long[] seed) {
return new L128X128Mix(seed);
}
@Override
Stream<Arguments> getReferenceData() {
/*
* Reference data from JDK 17:
* java.util.random.RandomGeneratorFactory.of("L128X128MixRandom").create(seed)
*
* Full byte[] seed created using SecureRandom.nextBytes. The seed was converted
* to long[] 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 % 8 != 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 long[] {
0x3a297b7429761628L, 0x9d21645f201b7021L, 0xba324e3c4d146537L, 0x16364205433a7113L,
0xf60962173e0c5742L, 0x3e7f5f502633755fL,
},
new long[] {
0x1ea7a5960faef34dL, 0xe77f9f6290dc93f2L, 0x74aefd78ce4030fdL, 0xc081b6e4c3595c02L,
0x3b1edc17aa95bcb4L, 0xc14bf25e77010cb2L, 0xe7395dfccaa9dbcaL, 0xbebad53c986c6f58L,
0x8f8a7583d30ec2e4L, 0xe2c541e3be5ea9b0L, 0x3bd9827396ed52fcL, 0xf21e25d719195bd6L,
0x133fdd3ec02e5349L, 0xe4173fa34456f84cL, 0xf749b087f88e3cefL, 0xd078170802484e3cL,
0x05f06c0da0b2fa8bL, 0x42b7c01bcf46c009L, 0xeae2728936c06176L, 0x71b6f995578a341dL,
0x1dc20c1eb7dde50bL, 0x4dfe3a2dc06884f9L, 0x53f8a64ef6620772L, 0x09f05fe07be6c5aaL,
0x019dd2cec64bd224L, 0xc91122977e5795a1L, 0x5ca628e227a9c59cL, 0x1c45e0b0ba328638L,
0x431beb396ed3d853L, 0x270db91f7a43193cL, 0xba22d45ffa952da8L, 0xa03026f48d0a16eaL,
0xd59e56fa28a9fd0bL, 0x5d3e3178bf2d1bddL, 0xd4a01fc739524149L, 0x08d25b8c425dcbedL,
0x6b53b7544bd12866L, 0x4c1ba8e9baacd1d4L, 0x6a2c03ce0e64be9eL, 0xaef1188c7b49e967L,
}),
Arguments.of(
new long[] {
0xab05120b1d68141aL, 0xaa16702f314b0958L, 0x48712e101702612cL, 0x52564c1c0c462c6aL,
0x02442c63271c2e65L, 0xa8256d670f6a0f50L,
},
new long[] {
0xb26e759a441f5966L, 0xc754e012f0e5b695L, 0x6f63edc0e02b9339L, 0xe3b4a2f074e68065L,
0x50e3133fee680fb1L, 0x4806d74486f1047aL, 0x636a4bb9b1588135L, 0xa38a1d12165a1a5eL,
0x1c93a08893b5d460L, 0xe5f12c095908d68bL, 0xc8209614bdf5c3aaL, 0x269514b48092e05cL,
0xf53376fb46c4068fL, 0x1c29c163c83808a1L, 0x4e061b72e2558d90L, 0x764dbe734460cd58L,
0x951eed50f2688936L, 0x90c431c196e7caa2L, 0x546c18e2ae8449cbL, 0xfa53c4db8f104337L,
0x6101109a7906c635L, 0xd60391b908c7689aL, 0x2edba3f7777b0fceL, 0xc58ec62f686cfafeL,
0xb46e6b9a63f7a5c8L, 0x0882126f0efd1c8cL, 0xb0a74862abb8fdceL, 0xeb73ae25e8d0cb13L,
0x6d2cabda24960b3fL, 0xa066055ef4918eb3L, 0x5e41aee4befadce1L, 0x8d0188693ebc3e89L,
0x42b7171e98c7ba4bL, 0x6cf4919164a36dedL, 0xd90883fa3b33a4daL, 0x01ac3a18f4020a05L,
0x1ec9164000c2c5c3L, 0xa90a377959d611baL, 0x4719833abe706dc0L, 0xf9a18b8597be90c8L,
}),
Arguments.of(
new long[] {
0x68034679056f6362L, 0xe666227323110c1cL, 0xaa502d6b5e676c61L, 0xe94d1d266d030b23L,
0x50253d7d19161d66L, 0x5f64221100172276L,
},
new long[] {
0xcf85d748550aaf08L, 0xa1903ba42f5c24a0L, 0x64df004d9a06460aL, 0xfbf4062ca343346cL,
0x5bdf77cdfdac6cf8L, 0x5e16cbd71a4020a3L, 0x3835ab4dde607646L, 0xa1ccb3ec0909470cL,
0x3d6f31f8aedb785fL, 0x8214004c996c8bf6L, 0xc6c775f354564e3cL, 0x6054e091e54a7278L,
0xd7a4b3827266b0fdL, 0x78ef85c8c29cc270L, 0x1e92d5e38336284bL, 0xd2d7939efd300d8aL,
0xa3b4a28536af8e04L, 0x5f1510ef2c9611dbL, 0xacbffd845e921744L, 0xeb46cad5d2c25cd9L,
0xd449bc48ce4c0288L, 0x738064dc34ed21a9L, 0xac85af76c0d4ae3eL, 0xf04885858cce92f8L,
0x3288572bff8677d4L, 0xc58371958117ecd1L, 0x018a1d0d5f6d6bb7L, 0xe29afe1c6f54208fL,
0x724c19235787dfb1L, 0x1d47fde2388156eaL, 0xb6316593e1cc5ac8L, 0x15f8c8c913938180L,
0x0d38390376df87feL, 0x950b50b0b4af2525L, 0x1d2d86b7b52aeabbL, 0x8331694e910a3705L,
0xc962bf20dc7ae05bL, 0x07b911ecb2b6e5f8L, 0x2e814094668c24eaL, 0xc259ec3c9a99750aL,
}));
}
@ParameterizedTest
@MethodSource(value = "getReferenceData")
void testElementConstructor(long[] seed, long[] expected) {
final L128X128Mix rng1 = new L128X128Mix(seed);
final L128X128Mix rng2 = new L128X128Mix(seed[0], seed[1], seed[2], seed[3], seed[4], seed[5]);
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 long[] seed = new long[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 L128X128Mix(seed);
final SplittableUniformRandomProvider rng2 = rng1.split(zeroSource);
RandomAssert.assertNextLongNotEquals(seed.length * 2, rng1, rng2);
// Since we know how the zero seed is amended
long z = 0;
for (int i = Factory.INSTANCE.lcgSeedSize(); i < seed.length; i++) {
seed[i] = LXMSupport.lea64(z);
z += LXMSupport.GOLDEN_RATIO_64;
}
final SplittableUniformRandomProvider rng3 = new L128X128Mix(seed);
final SplittableUniformRandomProvider rng4 = rng1.split(zeroSource);
RandomAssert.assertNextLongEquals(seed.length * 2, rng3, rng4);
}
}