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