fr.arakne.utils.value.helper.RandomUtil   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 203
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 44
c 1
b 0
f 0
dl 0
loc 203
ccs 30
cts 30
cp 1
rs 10
wmc 19

14 Methods

Rating   Name   Duplication   Size   Complexity  
A rand(Interval) 0 2 1
A reverseBool(int) 0 2 1
A enableTestingMode() 0 5 1
A createShared() 0 6 1
A rand(int,int) 0 11 3
A bool() 0 2 1
A of(List) 0 2 1
A shuffle(List) 0 6 1
A of(T[]) 0 2 1
A of(char[]) 0 2 1
A rand(int[]) 0 6 3
A RandomUtil() 0 3 2
A decimal(double) 0 2 1
A bool(int) 0 2 1
1
/*
2
 * This file is part of ArakneUtils.
3
 *
4
 * ArakneUtils is free software: you can redistribute it and/or modify
5
 * it under the terms of the GNU Lesser General Public License as published by
6
 * the Free Software Foundation, either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * ArakneUtils is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15
 * along with ArakneUtils.  If not, see <https://www.gnu.org/licenses/>.
16
 *
17
 * Copyright (c) 2017-2020 Vincent Quatrevieux
18
 */
19
20
package fr.arakne.utils.value.helper;
21
22
import fr.arakne.utils.value.Interval;
23
import org.checkerframework.checker.index.qual.LessThan;
24
import org.checkerframework.checker.index.qual.NonNegative;
25
import org.checkerframework.checker.index.qual.Positive;
26
import org.checkerframework.common.value.qual.IntRange;
27
import org.checkerframework.common.value.qual.MinLen;
28
29
import java.util.ArrayList;
30
import java.util.Collection;
31
import java.util.Collections;
32
import java.util.List;
33
import java.util.Random;
34
35
/**
36
 * Utility for random numbers
37
 *
38
 * This utility class must be used instead of the native {@link Random} class,
39
 * for create predictable random values on testing environment
40
 *
41
 * It's not recommended to share the RandomUtil instance between objects instances (i.e. store into a static field)
42
 * Each objects instance should have its own RandomUtil instance
43
 * But in case of short-life objects static instance of RandomUtil can be used when created using {@link RandomUtil#createShared()}
44
 */
45
public final class RandomUtil extends Random {
46
    /**
47
     * Testing mode ?
48
     * If set to true, the seed value will be fixed and random will be predictable
49
     */
50 1
    private static boolean testing = false;
51
52
    /**
53
     * List of instance which are shared between instances
54
     * This list is used for reset random seeds when enable testing mode
55
     *
56
     * @see RandomUtil#createShared()
57
     */
58 1
    private static final Collection<RandomUtil> sharedInstances = new ArrayList<>();
59
60 1
    public RandomUtil() {
61 1
        if (testing) {
62 1
            setSeed(0);
63
        }
64 1
    }
65
66
    /**
67
     * Get random number into [min, max] interval
68
     * The interval is inclusive
69
     *
70
     * If max if lower than min, min is returned
71
     *
72
     * @param min The minimal value, included
73
     * @param max The maximal value, included
74
     *
75
     * @return A random int
76
     */
77
    @SuppressWarnings("return")
78
    public @NonNegative @LessThan("#2 + 1") int rand(@NonNegative int min, @NonNegative int max) {
79 1
        if (max <= min) {
80 1
            return min;
81
        }
82
83 1
        if (min == 0) {
84 1
            return nextInt(max + 1);
85
        }
86
87 1
        return nextInt((max - min) + 1) + min;
88
    }
89
90
    /**
91
     * Get random number into given interval
92
     * The interval is inclusive
93
     *
94
     * This method is equivalent to `rand(interval.min(), interval.max())`
95
     *
96
     * @param interval The rand interval
97
     * @return A random int
98
     */
99
    public @NonNegative int rand(Interval interval) {
100 1
        return rand(interval.min(), interval.max());
101
    }
102
103
    /**
104
     * Get a random double between interval [0, max[
105
     *
106
     * @param max The maximum value (exclusive)
107
     * @return A random double
108
     */
109
    public double decimal(double max) {
110 1
        return nextDouble() * max;
111
    }
112
113
    /**
114
     * Get a random number from an interval
115
     *
116
     * @param interval An array of size 1 or 2. If the size is 1, return the array element, else return an array between interval[0] and interval[1] included
117
     * @return A random int
118
     */
119
    public @NonNegative int rand(@NonNegative int @MinLen(1) [] interval) {
120 1
        if (interval.length == 1 || interval[0] >= interval[1]) {
121 1
            return interval[0];
122
        }
123
124 1
        return rand(interval[0], interval[1]);
125
    }
126
127
    /**
128
     * Get a random boolean value
129
     *
130
     * @param percent Percent of chance that the returned value is true.
131
     *                If the value is 100, this method will always returns true
132
     *                If the value is 0, the method will always returns false
133
     *
134
     * @return A random boolean
135
     */
136
    public boolean bool(@IntRange(from = 0, to = 100) int percent) {
137 1
        return nextInt(100) < percent;
138
    }
139
140
    /**
141
     * Get a random boolean value
142
     * The returned value has same chance to be true or false
143
     *
144
     * This method is equivalent to `bool(50)`
145
     *
146
     * @return A random boolean
147
     * @see RandomUtil#bool(int)
148
     */
149
    public boolean bool() {
150 1
        return nextBoolean();
151
    }
152
153
    /**
154
     * Get a random boolean value following reverse chance
155
     * The return value if true with the probability of 1 / rate
156
     * Higher the rate value is, lower the probability is
157
     *
158
     * For rate = 2, the probability is 50% and its equivalent to call bool(50) or bool()
159
     * For rate = 100, the probability is 1% and its equivalent to call bool(1)
160
     *
161
     * @param rate The rate
162
     *
163
     * @return The random boolean value
164
     */
165
    public boolean reverseBool(@Positive int rate) {
166 1
        return nextInt(rate) == 0;
167
    }
168
169
    /**
170
     * Get a random value from an array of values
171
     * All elements has the same selection probability
172
     *
173
     * @param values The provided values
174
     * @param <T> The array element type
175
     *
176
     * @return One of the element of the array
177
     */
178
    public <T> T of(T @MinLen(1) [] values) {
179 1
        return values[nextInt(values.length)];
180
    }
181
182
    /**
183
     * Get a random value from an array of characters
184
     *
185
     * @param values The provided values
186
     *
187
     * @return One of the element of the array
188
     *
189
     * @see RandomUtil#of(Object[])
190
     */
191
    public char of(char @MinLen(1) [] values) {
192 1
        return values[nextInt(values.length)];
193
    }
194
195
    /**
196
     * Get a random value from a list of values
197
     * All elements has the same selection probability
198
     *
199
     * @param values The provided values
200
     * @param <T> The list element type
201
     *
202
     * @return One of the element of the list
203
     */
204
    public <T> T of(List<T> values) {
205 1
        return values.get(nextInt(values.size()));
206
    }
207
208
    /**
209
     * Randomize list element positions
210
     * This method will create a copy of the list, without modify the input parameter (unlike {@link Collections#shuffle(List)})
211
     *
212
     * @param list The list to randomize
213
     * @param <T> The element type
214
     *
215
     * @return The randomized list
216
     */
217
    public <T> List<T> shuffle(List<T> list) {
218 1
        final List<T> shuffled = new ArrayList<>(list);
219
220 1
        Collections.shuffle(shuffled, this);
221
222 1
        return shuffled;
223
    }
224
225
    /**
226
     * Create a new RandomUtil instance which is shared between instances (store in a static field)
227
     * This method allows the system to reset the random seed value when enable testing mode, for predictable random
228
     *
229
     * @return A new RandomUtil instance
230
     */
231
    public static RandomUtil createShared() {
232 1
        final RandomUtil random = new RandomUtil();
233
234 1
        sharedInstances.add(random);
235
236 1
        return random;
237
    }
238
239
    /**
240
     * Enable the testing mode for get predictable random values
241
     * This method MUST NOT be used outside tests
242
     */
243
    public static void enableTestingMode() {
244 1
        testing = true;
245
246
        // Reset the random state
247 1
        sharedInstances.forEach(randomUtil -> randomUtil.setSeed(0));
248 1
    }
249
}
250