// Path: prngine/src/main/java/io/jenetics/prngine/Bytes.java
// static long readLong(final byte[] bytes, final int index) {
// 	final int offset = index*Long.BYTES;
// 	if (offset + Long.BYTES > bytes.length) {
// 		throw new IndexOutOfBoundsException(format(
// 			"Not enough data to read long value (index=%d, bytes=%d).",
// 			index, bytes.length
// 		));
// 	}
// 
// 	return
// 		((long)(bytes[offset    ] & 255) << 56) +
// 		((long)(bytes[offset + 1] & 255) << 48) +
// 		((long)(bytes[offset + 2] & 255) << 40) +
// 		((long)(bytes[offset + 3] & 255) << 32) +
// 		((long)(bytes[offset + 4] & 255) << 24) +
// 		((bytes[offset + 5] & 255) << 16) +
// 		((bytes[offset + 6] & 255) <<  8) +
// 		(bytes[offset + 7] & 255);
// }
// 
// Path: prngine/src/main/java/io/jenetics/prngine/IntMath.java
// static long log2Floor(final long x) {
// 	return Long.SIZE - 1 - Long.numberOfLeadingZeros(x);
// }


import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static io.jenetics.prngine.Bytes.readLong;
import static io.jenetics.prngine.IntMath.log2Floor;
import java.util.random.RandomGenerator;
/*
 * PRNGine - Java PRNG Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed 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.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
 */
package io.jenetics.prngine;



/**
 * This class implements a linear congruential PRNG with additional bit-shift
 * transition.
 *
 * <em>
 * This is a port of the
 * <a href="https://github.com/rabauke/trng4/blob/master/src/lcg64_shift.hpp">
 * trng::lcg64_shift</a> PRNG class of the
 * <a href="http://numbercrunch.de/trng/">TRNG</a> library created by Heiko
 * Bauke.</em>
 *
 * <p>
 * The following listing shows the actual PRNG implementation.
 * <pre>{@code
 * final long a, b = <params>
 * long r = <seed>
 *
 * long nextLong() {
 *     r = q*r + b;
 *
 *     long t = r;
 *     t ^= t >>> 17;
 *     t ^= t << 31;
 *     t ^= t >>> 8;
 *     return t;
 * }
 * }</pre>
 *
 * <p>
 * <strong>Not that the base implementation of the {@code LCG64ShiftRandom}
 * class is not thread-safe.</strong> If multiple threads requests random
 * numbers from this class, it <i>must</i> be synchronized externally.
 *
 * @see <a href="https://github.com/rabauke/trng4/blob/master/doc/trng.pdf">TRNG</a>
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
 * @since 1.0
 * @version 2.0.0
 */
public class LCG64ShiftRandom implements RandomGenerator.ArbitrarilyJumpableGenerator {

	/* *************************************************************************
	 * Parameter classes.
	 * ************************************************************************/

	/**
	 * Parameter class for the {@code LCG64ShiftRandom} generator, for the
	 * parameters <i>a</i> and <i>b</i> of the LC recursion
	 * <i>r<sub>i+1</sub> = a · r<sub>i</sub> + b</i> mod <i>2<sup>64</sup></i>.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
	 * @since 1.1
	 * @version 2.0.0
	 *
	 * @param a the LEcuyer parameter a
	 * @param b the LEcuyer parameter b
	 */
	public static final record Param(long a, long b) {

		/**
		 * The default PRNG parameters: a = 0xFBD19FBBC5C07FF5L; b = 1
		 */
		public static final Param DEFAULT = new Param(0xFBD19FBBC5C07FF5L, 1L);

		/**
		 * LEcuyer 1 parameters: a = 0x27BB2EE687B0B0FDL; b = 1
		 */
		public static final Param LECUYER1 = new Param(0x27BB2EE687B0B0FDL, 1L);

		/**
		 * LEcuyer 2 parameters: a = 0x2C6FE96EE78B6955L; b = 1
		 */
		public static final Param LECUYER2 = new Param(0x2C6FE96EE78B6955L, 1L);

		/**
		 * LEcuyer 3 parameters: a = 0x369DEA0F31A53F85L; b = 1
		 */
		public static final Param LECUYER3 = new Param(0x369DEA0F31A53F85L, 1L);

	}


	/**
	 * Represents the state of this random engine
	 */
	private final static class State {
		static final int SEED_BYTES = 8;

		long r;

		private State(final State state) {
			this.r = state.r;
		}

		State(final byte[] seed) {
			if (seed.length < SEED_BYTES) {
				throw new IllegalArgumentException(format(
					"Required %d seed bytes, but got %d.",
					SEED_BYTES, seed.length
				));
			}

// Path: prngine/src/main/java/io/jenetics/prngine/Bytes.java
// static long readLong(final byte[] bytes, final int index) {
// 	final int offset = index*Long.BYTES;
// 	if (offset + Long.BYTES > bytes.length) {
// 		throw new IndexOutOfBoundsException(format(
// 			"Not enough data to read long value (index=%d, bytes=%d).",
// 			index, bytes.length
// 		));
// 	}
// 
// 	return
// 		((long)(bytes[offset    ] & 255) << 56) +
// 		((long)(bytes[offset + 1] & 255) << 48) +
// 		((long)(bytes[offset + 2] & 255) << 40) +
// 		((long)(bytes[offset + 3] & 255) << 32) +
// 		((long)(bytes[offset + 4] & 255) << 24) +
// 		((bytes[offset + 5] & 255) << 16) +
// 		((bytes[offset + 6] & 255) <<  8) +
// 		(bytes[offset + 7] & 255);
// }
// 
// Path: prngine/src/main/java/io/jenetics/prngine/IntMath.java
// static long log2Floor(final long x) {
// 	return Long.SIZE - 1 - Long.numberOfLeadingZeros(x);
// }


// Path: prngine/src/main/java/io/jenetics/prngine/LCG64ShiftRandom.java
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static io.jenetics.prngine.Bytes.readLong;
import static io.jenetics.prngine.IntMath.log2Floor;
import java.util.random.RandomGenerator;

/*
 * PRNGine - Java PRNG Library (@__identifier__@).
 * Copyright (c) @__year__@ Franz Wilhelmstötter
 *
 * Licensed 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.
 *
 * Author:
 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
 */
package io.jenetics.prngine;



/**
 * This class implements a linear congruential PRNG with additional bit-shift
 * transition.
 *
 * <em>
 * This is a port of the
 * <a href="https://github.com/rabauke/trng4/blob/master/src/lcg64_shift.hpp">
 * trng::lcg64_shift</a> PRNG class of the
 * <a href="http://numbercrunch.de/trng/">TRNG</a> library created by Heiko
 * Bauke.</em>
 *
 * <p>
 * The following listing shows the actual PRNG implementation.
 * <pre>{@code
 * final long a, b = <params>
 * long r = <seed>
 *
 * long nextLong() {
 *     r = q*r + b;
 *
 *     long t = r;
 *     t ^= t >>> 17;
 *     t ^= t << 31;
 *     t ^= t >>> 8;
 *     return t;
 * }
 * }</pre>
 *
 * <p>
 * <strong>Not that the base implementation of the {@code LCG64ShiftRandom}
 * class is not thread-safe.</strong> If multiple threads requests random
 * numbers from this class, it <i>must</i> be synchronized externally.
 *
 * @see <a href="https://github.com/rabauke/trng4/blob/master/doc/trng.pdf">TRNG</a>
 *
 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
 * @since 1.0
 * @version 2.0.0
 */
public class LCG64ShiftRandom implements RandomGenerator.ArbitrarilyJumpableGenerator {

	/* *************************************************************************
	 * Parameter classes.
	 * ************************************************************************/

	/**
	 * Parameter class for the {@code LCG64ShiftRandom} generator, for the
	 * parameters <i>a</i> and <i>b</i> of the LC recursion
	 * <i>r<sub>i+1</sub> = a · r<sub>i</sub> + b</i> mod <i>2<sup>64</sup></i>.
	 *
	 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
	 * @since 1.1
	 * @version 2.0.0
	 *
	 * @param a the LEcuyer parameter a
	 * @param b the LEcuyer parameter b
	 */
	public static final record Param(long a, long b) {

		/**
		 * The default PRNG parameters: a = 0xFBD19FBBC5C07FF5L; b = 1
		 */
		public static final Param DEFAULT = new Param(0xFBD19FBBC5C07FF5L, 1L);

		/**
		 * LEcuyer 1 parameters: a = 0x27BB2EE687B0B0FDL; b = 1
		 */
		public static final Param LECUYER1 = new Param(0x27BB2EE687B0B0FDL, 1L);

		/**
		 * LEcuyer 2 parameters: a = 0x2C6FE96EE78B6955L; b = 1
		 */
		public static final Param LECUYER2 = new Param(0x2C6FE96EE78B6955L, 1L);

		/**
		 * LEcuyer 3 parameters: a = 0x369DEA0F31A53F85L; b = 1
		 */
		public static final Param LECUYER3 = new Param(0x369DEA0F31A53F85L, 1L);

	}


	/**
	 * Represents the state of this random engine
	 */
	private final static class State {
		static final int SEED_BYTES = 8;

		long r;

		private State(final State state) {
			this.r = state.r;
		}

		State(final byte[] seed) {
			if (seed.length < SEED_BYTES) {
				throw new IllegalArgumentException(format(
					"Required %d seed bytes, but got %d.",
					SEED_BYTES, seed.length
				));
			}

			r = readLong(seed, 0);