001/*
002 * Java Genetic Algorithm Library (jenetics-7.1.0).
003 * Copyright (c) 2007-2022 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.internal.math;
021
022import static java.lang.Math.abs;
023import static io.jenetics.internal.util.Requires.probability;
024
025import java.util.random.RandomGenerator;
026import java.util.stream.IntStream;
027
028/**
029 * Some random helper functions.
030 *
031 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
032 * @since 1.4
033 * @version 7.0
034 */
035public final class Randoms {
036        private Randoms() {}
037
038        public static byte nextByte(final RandomGenerator random) {
039                return (byte)random.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE + 1);
040        }
041
042        public static char nextChar(final RandomGenerator random) {
043                char c = '\0';
044                do {
045                        c = (char)random.nextInt(
046                                Character.MIN_VALUE,
047                                Character.MAX_VALUE + 1
048                        );
049                } while (!Character.isLetterOrDigit(c));
050
051                return c;
052        }
053
054        public static short nextShort(final RandomGenerator random) {
055                return (short)random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE + 1);
056        }
057
058        public static String nextASCIIString(
059                final int length,
060                final RandomGenerator random
061        ) {
062                final char[] chars = new char[length];
063                for (int i = 0; i < length; ++i) {
064                        chars[i] = (char)random.nextInt(32, 127);
065                }
066
067                return new String(chars);
068        }
069
070        public static String nextASCIIString(final RandomGenerator random) {
071                return nextASCIIString(random.nextInt(5, 20), random);
072        }
073
074        /*
075         * Conversion methods used by the 'RandomGenerator' engine from the JDK.
076         */
077
078        public static float toFloat(final int a) {
079                return (a >>> 8)/(float)(1 << 24);
080        }
081
082        public static float toFloat(final long a) {
083                return (int)(a >>> 40)/(float)(1 << 24);
084        }
085
086        public static double toDouble(final long a) {
087                return (((a >>> 38) << 27) + ((int)a >>> 5))/(double)(1L << 53);
088        }
089
090        public static double toDouble(final int a, final int b) {
091                return (((long)(a >>> 6) << 27) + (b >>> 5))/(double)(1L << 53);
092        }
093
094
095        /*
096         * Conversion methods used by the Apache Commons BitStreamGenerator.
097         */
098
099        public static float toFloat2(final int a) {
100                return (a >>> 9)*0x1.0p-23f;
101        }
102
103        public static float toFloat2(final long a) {
104                return (int)(a >>> 41)*0x1.0p-23f;
105        }
106
107        public static double toDouble2(final long a) {
108                return (a & 0xFFFFFFFFFFFFFL)*0x1.0p-52d;
109        }
110
111        public static double toDouble2(final int a, final int b) {
112                return (((long)(a >>> 6) << 26) | (b >>> 6))*0x1.0p-52d;
113        }
114
115        /**
116         * Create an {@code IntStream} which creates random indexes within the
117         * given range and the index probability.
118         *
119         * @since 3.0
120         *
121         * @param random the random engine used for calculating the random
122         *        indexes
123         * @param start the start index (inclusively)
124         * @param end the end index (exclusively)
125         * @param p the index selection probability
126         * @return an new random index stream
127         * @throws IllegalArgumentException if {@code p} is not a
128         *         valid probability.
129         */
130        public static IntStream indexes(
131                final RandomGenerator random,
132                final int start,
133                final int end,
134                final double p
135        ) {
136                probability(p);
137                final int P = Probabilities.toInt(p);
138
139                return equals(p, 0, 1E-20)
140                        ? IntStream.empty()
141                        : equals(p, 1, 1E-20)
142                                ? IntStream.range(start, end)
143                                : IntStream.range(start, end)
144                                        .filter(i -> random.nextInt() < P);
145        }
146
147        private static boolean
148        equals(final double a, final double b, final double delta) {
149                return abs(a - b) <= delta;
150        }
151
152        /**
153         * Create an {@code IntStream} which creates random indexes within the
154         * given range and the index probability.
155         *
156         * @since 3.0
157         *
158         * @param random the random engine used for calculating the random
159         *        indexes
160         * @param n the end index (exclusively). The start index is zero.
161         * @param p the index selection probability
162         * @return an new random index stream
163         * @throws IllegalArgumentException if {@code p} is not a
164         *         valid probability.
165         * @throws NullPointerException if the given {@code random}
166         *         engine is {@code null}.
167         */
168        public static IntStream indexes(
169                final RandomGenerator random,
170                final int n,
171                final double p
172        ) {
173                return indexes(random, 0, n, p);
174        }
175
176        /**
177         * Create a new <em>seed</em> byte array of the given length.
178         *
179         * @see #seed(byte[])
180         * @see #seed()
181         *
182         * @param length the length of the returned byte array.
183         * @return a new <em>seed</em> byte array of the given length
184         * @throws NegativeArraySizeException if the given length is smaller
185         *         than zero.
186         */
187        public static byte[] seedBytes(final int length) {
188                return seed(new byte[length]);
189        }
190
191        /**
192         * Fills the given byte array with random bytes, created by successive
193         * calls of the {@link #seed()} method.
194         *
195         * @see #seed()
196         *
197         * @param seed the byte array seed to fill with random bytes.
198         * @return the given byte array, for method chaining.
199         * @throws NullPointerException if the {@code seed} array is
200         *         {@code null}.
201         */
202        public static byte[] seed(final byte[] seed) {
203                for (int i = 0, len = seed.length; i < len;) {
204                        int n = Math.min(len - i, Long.SIZE/Byte.SIZE);
205
206                        for (long x = seed(); n-- > 0; x >>= Byte.SIZE) {
207                                seed[i++] = (byte)x;
208                        }
209                }
210
211                return seed;
212        }
213
214        /**
215         * Calculating a 64 bit seed value which can be used for initializing
216         * PRNGs. This method uses a combination of {@code System.nanoTime()}
217         * and {@code new Object().hashCode()} calls to create a reasonable safe
218         * seed value:
219         *
220         * <pre>{@code
221         * public static long seed() {
222         *     return seed(System.nanoTime());
223         * }
224         * }</pre>
225         *
226         * This method passes all the statistical tests of the
227         * <a href="http://www.phy.duke.edu/~rgb/General/dieharder.php">
228         * dieharder</a> test suite&mdash;executed on a linux machine with
229         * JDK version 1.7. <em>Since there is no prove that this will the case
230         * for every Java version and OS, it is recommended to only use this
231         * method for seeding other PRNGs.</em>
232         *
233         * @see #seed(long)
234         *
235         * @return the random seed value.
236         */
237        public static long seed() {
238                return seed(System.nanoTime());
239        }
240
241        /**
242         * Uses the given {@code base} value to create a reasonable safe seed
243         * value. This is done by combining it with values of
244         * {@code new Object().hashCode()}:
245         *
246         * <pre>{@code
247         * public static long seed(final long base) {
248         *     final long objectHashSeed = ((long)(new Object().hashCode()) << 32) |
249         *                                         new Object().hashCode();
250         *     long seed = base^objectHashSeed;
251         *     seed ^= seed << 17;
252         *     seed ^= seed >>> 31;
253         *     seed ^= seed << 8;
254         *     return seed;
255         * }
256         * }</pre>
257         *
258         * @param base the base value of the seed to create
259         * @return the created seed value.
260         */
261        public static long seed(final long base) {
262                return mix(base, ObjectSeed.seed());
263        }
264
265        private static long mix(final long a, final long b) {
266                long c = a^b;
267                c ^= c << 17;
268                c ^= c >>> 31;
269                c ^= c << 8;
270                return c;
271        }
272
273        private static final class ObjectSeed {
274                private static long seed() {
275                        return (long)new ObjectSeed().hashCode() << 32 |
276                                new ObjectSeed().hashCode();
277                }
278        }
279
280}