-
-
Notifications
You must be signed in to change notification settings - Fork 294
/
XORShiftRNG.java
136 lines (125 loc) · 4.65 KB
/
XORShiftRNG.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// ============================================================================
// Copyright 2006-2012 Daniel W. Dyer
//
// 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.
// ============================================================================
package net.citizensnpcs.util;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
/**
* <p>
* Very fast pseudo random number generator. See
* <a href= "http://school.anhb.uwa.edu.au/personalpages/kwessen/shared/Marsaglia03.html" >this page</a> for a
* description. This RNG has a period of about 2^160, which is not as long as the MersenneTwisterRNG but it is
* faster.
* </p>
*
* <p>
* <em>NOTE: Because instances of this class require 160-bit seeds, it is not
* possible to seed this RNG using the {@link #setSeed(long)} method inherited
* from {@link Random}. Calls to this method will have no effect.
* Instead the seed must be set by a constructor.</em>
* </p>
*
* @author Daniel Dyer
* @since 1.2
*/
public class XORShiftRNG extends Random {
// Lock to prevent concurrent modification of the RNG's internal state.
private final ReentrantLock lock = new ReentrantLock();
private final byte[] seed;
// Previously used an array for state but using separate fields proved to be
// faster.
private int state1;
private int state2;
private int state3;
private int state4;
private int state5;
/**
* Creates an RNG and seeds it with the specified seed data.
*/
public XORShiftRNG() {
this.seed = new byte[SEED_SIZE_BYTES];
SEED_GENERATOR.nextBytes(seed);
int[] state = convertBytesToInts(seed);
this.state1 = state[0];
this.state2 = state[1];
this.state3 = state[2];
this.state4 = state[3];
this.state5 = state[4];
}
/**
* {@inheritDoc}
*/
public byte[] getSeed() {
return seed.clone();
}
/**
* {@inheritDoc}
*/
@Override
protected int next(int bits) {
try {
lock.lock();
int t = (state1 ^ (state1 >> 7));
state1 = state2;
state2 = state3;
state3 = state4;
state4 = state5;
state5 = (state5 ^ (state5 << 6)) ^ (t ^ (t << 13));
int value = (state2 + state2 + 1) * state5;
return value >>> (32 - bits);
} finally {
lock.unlock();
}
}
// Mask for casting a byte to an int, bit-by-bit (with
// bitwise AND) with no special consideration for the sign bit.
private static final int BITWISE_BYTE_TO_INT = 0x000000FF;
private static Random SEED_GENERATOR = new Random();
private static final int SEED_SIZE_BYTES = 20; // Needs 5 32-bit integers.
private static final long serialVersionUID = -1843001897066722618L;
/**
* Take four bytes from the specified position in the specified block and convert them into a 32-bit int, using the
* big-endian convention.
*
* @param bytes
* The data to read from.
* @param offset
* The position to start reading the 4-byte int from.
* @return The 32-bit integer represented by the four bytes.
*/
public static int convertBytesToInt(byte[] bytes, int offset) {
return (BITWISE_BYTE_TO_INT & bytes[offset + 3]) | ((BITWISE_BYTE_TO_INT & bytes[offset + 2]) << 8)
| ((BITWISE_BYTE_TO_INT & bytes[offset + 1]) << 16) | ((BITWISE_BYTE_TO_INT & bytes[offset]) << 24);
}
/**
* Convert an array of bytes into an array of ints. 4 bytes from the input data map to a single int in the output
* data.
*
* @param bytes
* The data to read from.
* @return An array of 32-bit integers constructed from the data.
* @since 1.1
*/
public static int[] convertBytesToInts(byte[] bytes) {
if (bytes.length % 4 != 0) {
throw new IllegalArgumentException("Number of input bytes must be a multiple of 4.");
}
int[] ints = new int[bytes.length / 4];
for (int i = 0; i < ints.length; i++) {
ints[i] = convertBytesToInt(bytes, i * 4);
}
return ints;
}
}