001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 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                record Limits() {
046                        private static final int LEFT = '0';
047                        private static final int RIGHT = 'z' + 1;
048                }
049
050                char c = '\0';
051                do {
052                        c = (char)random.nextInt(Limits.LEFT, Limits.RIGHT);
053                } while (!((c <= 57 || c >= 65) && (c <= 90 || c >= 97)));
054
055                return c;
056        }
057
058        public static short nextShort(final RandomGenerator random) {
059                return (short)random.nextInt(Short.MIN_VALUE, Short.MAX_VALUE + 1);
060        }
061
062        public static String nextASCIIString(
063                final int length,
064                final RandomGenerator random
065        ) {
066                final char[] chars = new char[length];
067                for (int i = 0; i < length; ++i) {
068                        chars[i] = (char)random.nextInt(32, 127);
069                }
070
071                return new String(chars);
072        }
073
074        public static String nextASCIIString(final RandomGenerator random) {
075                return nextASCIIString(random.nextInt(5, 20), random);
076        }
077
078        /**
079         * Create an {@code IntStream} which creates random indexes within the
080         * given range and the index probability.
081         *
082         * @since 3.0
083         *
084         * @param random the random engine used for calculating the random
085         *        indexes
086         * @param start the start index (inclusively)
087         * @param end the end index (exclusively)
088         * @param p the index selection probability
089         * @return a new random index stream
090         * @throws IllegalArgumentException if {@code p} is not a
091         *         valid probability.
092         */
093        public static IntStream indexes(
094                final RandomGenerator random,
095                final int start,
096                final int end,
097                final double p
098        ) {
099                requireNonNull(random);
100                probability(p);
101
102                if (isZero(p)) {
103                        return IntStream.empty();
104                } else if (isOne(p)) {
105                        return IntStream.range(start, end);
106                } else {
107                        final int P = Probabilities.toInt(p);
108                        return IntStream.range(start, end)
109                                .filter(i -> random.nextInt() < P);
110                }
111        }
112
113
114        /**
115         * Create an {@code IntStream} which creates random indexes within the
116         * given range and the index probability.
117         *
118         * @since 3.0
119         *
120         * @param random the random engine used for calculating the random
121         *        indexes
122         * @param n the end index (exclusively). The start index is zero.
123         * @param p the index selection probability
124         * @return a new random index stream
125         * @throws IllegalArgumentException if {@code p} is not a
126         *         valid probability.
127         * @throws NullPointerException if the given {@code random}
128         *         engine is {@code null}.
129         */
130        public static IntStream indexes(
131                final RandomGenerator random,
132                final int n,
133                final double p
134        ) {
135                return indexes(random, 0, n, p);
136        }
137
138        /**
139         * Create a new <em>seed</em> byte array of the given length.
140         *
141         * @see #seed(byte[])
142         * @see #seed()
143         *
144         * @param length the length of the returned byte array.
145         * @return a new <em>seed</em> byte array of the given length
146         * @throws NegativeArraySizeException if the given length is smaller
147         *         than zero.
148         */
149        public static byte[] seedBytes(final int length) {
150                return seed(new byte[length]);
151        }
152
153        /**
154         * Fills the given byte array with random bytes, created by successive
155         * calls of the {@link #seed()} method.
156         *
157         * @see #seed()
158         *
159         * @param seed the byte array seed to fill with random bytes.
160         * @return the given byte array, for method chaining.
161         * @throws NullPointerException if the {@code seed} array is
162         *         {@code null}.
163         */
164        public static byte[] seed(final byte[] seed) {
165                for (int i = 0, len = seed.length; i < len;) {
166                        int n = Math.min(len - i, Long.SIZE/Byte.SIZE);
167
168                        for (long x = seed(); n-- > 0; x >>= Byte.SIZE) {
169                                seed[i++] = (byte)x;
170                        }
171                }
172
173                return seed;
174        }
175
176        /**
177         * Calculating a 64-bit seed value which can be used for initializing
178         * PRNGs. This method uses a combination of {@code System.nanoTime()}
179         * and {@code new Object().hashCode()} calls to create a reasonable safe
180         * seed value:
181         *
182         * {@snippet lang="java":
183         * public static long seed() {
184         *     return seed(System.nanoTime());
185         * }
186         * }
187         *
188         * This method passes all the statistical tests of the
189         * <a href="http://www.phy.duke.edu/~rgb/General/dieharder.php">
190         * dieharder</a> test suite&mdash;executed on a linux machine with
191         * JDK version 1.7. <em>Since there is no prove that this will the case
192         * for every Java version and OS, it is recommended to only use this
193         * method for seeding other PRNGs.</em>
194         *
195         * @see #seed(long)
196         *
197         * @return the random seed value.
198         */
199        public static long seed() {
200                return seed(System.nanoTime());
201        }
202
203        /**
204         * Uses the given {@code base} value to create a reasonable safe seed
205         * value. This is done by combining it with values of
206         * {@code new Object().hashCode()}:
207         *
208         * {@snippet lang="java":
209         * public static long seed(final long base) {
210         *     final long objectHashSeed = ((long)(new Object().hashCode()) << 32) |
211         *                                         new Object().hashCode();
212         *     long seed = base^objectHashSeed;
213         *     seed ^= seed << 17;
214         *     seed ^= seed >>> 31;
215         *     seed ^= seed << 8;
216         *     return seed;
217         * }
218         * }
219         *
220         * @param base the base value of the seed to create
221         * @return the created seed value.
222         */
223        public static long seed(final long base) {
224                return mix(base, ObjectSeed.seed());
225        }
226
227        private static long mix(final long a, final long b) {
228                long c = a^b;
229                c ^= c << 17;
230                c ^= c >>> 31;
231                c ^= c << 8;
232                return c;
233        }
234
235        private static final class ObjectSeed {
236                private static long seed() {
237                        return (long)new ObjectSeed().hashCode() << 32 |
238                                new ObjectSeed().hashCode();
239                }
240        }
241
242}