Skip to content

Commit bfaa9cc

Browse files
committed
Added Drand48Mix generator
1 parent ebadfd2 commit bfaa9cc

8 files changed

Lines changed: 235 additions & 11 deletions

File tree

SimSystem/src/main/java/mathtools/distribution/tools/Drand48.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* Notes about Java types: C uses unsigned short for xsubi; Java shorts are signed,
3030
* so we mask with 0xFFFF when converting to/from the short[].
3131
*/
32-
public final class Drand48 {
32+
public final class Drand48 implements Drand48Interface {
3333
/** LCG parameter (48-bit) MULTIPLIER */
3434
private static final long MULTIPLIER = 0x5DEECE66DL;
3535
/** LCG parameter (48-bit) ADDEND */
@@ -44,16 +44,24 @@ public final class Drand48 {
4444
* Create and seed the generator with given seedval (like srand48).
4545
* @param seedval seed value (any long; only low bits used similarly to C)
4646
*/
47-
public Drand48(long seedval) {
47+
public Drand48(final long seedval) {
4848
srand48(seedval);
4949
}
5050

51+
/**
52+
* Create and seed the generator with given seedval (like srand48).
53+
*/
54+
public Drand48() {
55+
srand48(0);
56+
}
57+
5158
/**
5259
* Seed the generator (like C's srand48).
5360
* state := ((seedval << 16) + 0x330E) & ((1<<48)-1)
5461
* @param seedval seed value
5562
*/
56-
public void srand48(long seedval) {
63+
@Override
64+
public void srand48(final long seedval) {
5765
state = ((seedval << 16) + 0x330EL) & MASK48;
5866
}
5967

@@ -62,6 +70,7 @@ public void srand48(long seedval) {
6270
* Equivalent to C's drand48().
6371
* @return uniformly distributed double in [0,1)
6472
*/
73+
@Override
6574
public double drand48() {
6675
state = (state * MULTIPLIER + ADDEND) & MASK48;
6776
return (state / (double) (1L << 48));
@@ -71,6 +80,7 @@ public double drand48() {
7180
* Return a 31-bit non-negative integer like C's lrand48 (optional helper).
7281
* @return int in [0, 2^31)
7382
*/
83+
@Override
7484
public int lrand48() {
7585
// advance state and return high-order 31 bits
7686
state = (state * MULTIPLIER + ADDEND) & MASK48;
@@ -93,7 +103,7 @@ public int lrand48() {
93103
* @return double in [0,1)
94104
* @throws IllegalArgumentException if xsubi.length &lt; 3
95105
*/
96-
public static double erand48(short[] xsubi) {
106+
public static double erand48(final short[] xsubi) {
97107
if (xsubi == null || xsubi.length < 3) {
98108
throw new IllegalArgumentException("xsubi must be a short[3] (or longer)");
99109
}
@@ -118,7 +128,7 @@ public static double erand48(short[] xsubi) {
118128
* @return double in [0,1)
119129
* @throws IllegalArgumentException if xsubi.length &lt; 3
120130
*/
121-
public static double erand48FromInts(int[] xsubi) {
131+
public static double erand48FromInts(final int[] xsubi) {
122132
if (xsubi == null || xsubi.length < 3) {
123133
throw new IllegalArgumentException("xsubi must be an int[3] (or longer)");
124134
}

SimSystem/src/main/java/mathtools/distribution/tools/Drand48BitsStreamGenerator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,14 @@ public class Drand48BitsStreamGenerator extends BitsStreamGenerator {
3333
/**
3434
* Internes Generatorobjekt
3535
*/
36-
private Drand48 generator;
36+
private final Drand48Interface generator;
3737

3838
/**
3939
* Konstruktor
40+
* @param generator internes Generatorobjekt
4041
*/
41-
public Drand48BitsStreamGenerator() {
42+
public Drand48BitsStreamGenerator(final Drand48Interface generator) {
43+
this.generator=generator;
4244
setSeed(System.currentTimeMillis());
4345
}
4446

@@ -58,7 +60,7 @@ public void setSeed(int[] seed) {
5860

5961
@Override
6062
public void setSeed(long seed) {
61-
generator=new Drand48(seed);
63+
generator.srand48(seed);
6264
}
6365

6466
@Override
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* Copyright 2026 Alexander Herzog
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package mathtools.distribution.tools;
17+
18+
/**
19+
* Interface zu einem Drand48-artigen Generator.<br>
20+
* Wird von {@link Drand48BitsStreamGenerator} verwendet.
21+
*/
22+
public interface Drand48Interface {
23+
24+
/**
25+
* Seed the generator (like C's srand48).
26+
* @param seedval seed value
27+
*/
28+
void srand48(long seedval);
29+
30+
/**
31+
* Advance the internal state and return a double in [0.0, 1.0).
32+
* Equivalent to C's drand48().
33+
* @return uniformly distributed double in [0,1)
34+
*/
35+
double drand48();
36+
37+
/**
38+
* Return a 31-bit non-negative integer like C's lrand48 (optional helper).
39+
* @return int in [0, 2^31)
40+
*/
41+
int lrand48();
42+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/**
2+
* Copyright 2026 Alexander Herzog
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package mathtools.distribution.tools;
17+
18+
/**
19+
* Minimal, compatible implementation of C's drand48/srand48 and erand48 in Java.
20+
*
21+
* Uses the POSIX/standard 48-bit LCG:
22+
* X_{n+1} = (a * X_n + c) mod 2^48
23+
* with a = 0x5DEECE66DL, c = 0xB.
24+
*
25+
* - srand48(seedval) sets the internal 48-bit state to ((seedval &lt;&lt; 16) + 0x330E) &amp; mask
26+
* - drand48() advances state and returns state / 2^48 as a double in [0,1)
27+
* - erand48(xsubi) advances the state stored in the provided 3-element short[] and returns the double
28+
*
29+
* Notes about Java types: C uses unsigned short for xsubi; Java shorts are signed,
30+
* so we mask with 0xFFFF when converting to/from the short[].
31+
*/
32+
public final class Drand48Mix implements Drand48Interface {
33+
/** LCG parameter (48-bit) MULTIPLIER */
34+
private static final long MULTIPLIER = 0x5DEECE66DL;
35+
/** LCG parameter (48-bit) ADDEND */
36+
private static final long ADDEND = 0xBL;
37+
/** LCG parameter (48-bit) MASK48 */
38+
private static final long MASK48 = (1L << 48) - 1;
39+
40+
/** internal 48-bit state (only low 48 bits used) */
41+
private long state;
42+
43+
/**
44+
* Create and seed the generator with given seedval (like srand48).
45+
* @param seedval seed value (any long; only low bits used similarly to C)
46+
*/
47+
public Drand48Mix(final long seedval) {
48+
srand48(seedval);
49+
}
50+
51+
/**
52+
* Create and seed the generator with given seedval (like srand48).
53+
*/
54+
public Drand48Mix() {
55+
srand48(0);
56+
}
57+
58+
/**
59+
* 128-bittige Mix-Funktion
60+
* @param x Eingangswert
61+
* @return Ausgangswert nach Mix
62+
* @see L32X64Mix#lea32(int)
63+
*/
64+
public static long mixLong(final long x) {
65+
int low = (int) (x & 0xFFFFFFFFL); /* untere 32 Bit */
66+
int high = (int) ((x >>> 32) & 0xFFFFFFFFL); /* obere 32 Bit */
67+
68+
/* Schritt 2: Mix-Funktion auf beide Teile anwenden */
69+
int mixedLow = L32X64Mix.lea32(low);
70+
int mixedHigh = L32X64Mix.lea32(high);
71+
72+
/* Schritt 3: Wieder zusammenführen zu einem long */
73+
/* Beachte: mixedHigh ist jetzt 32 Bit, aber als int (signed).
74+
* Wir müssen sicherstellen, dass es als unsigned 32-Bit interpretiert wird. */
75+
return ((long) mixedHigh << 32) | (mixedLow & 0xFFFFFFFFL);
76+
}
77+
78+
/**
79+
* Seed the generator (like C's srand48).
80+
* state := ((seedval &lt;&lt; 16) + 0x330E) &amp; ((1&lt;&lt;48)-1)
81+
* @param seedval seed value
82+
*/
83+
@Override
84+
public void srand48(final long seedval) {
85+
state = ((seedval << 16) + 0x330EL) & MASK48;
86+
}
87+
88+
/**
89+
* Advance the internal state and return a double in [0.0, 1.0).
90+
* Equivalent to C's drand48().
91+
* @return uniformly distributed double in [0,1)
92+
*/
93+
@Override
94+
public double drand48() {
95+
state = (state * MULTIPLIER + ADDEND) & MASK48;
96+
return (state / (double) (1L << 48));
97+
}
98+
99+
/**
100+
* Return a 31-bit non-negative integer like C's lrand48 (optional helper).
101+
* @return int in [0, 2^31)
102+
*/
103+
@Override
104+
public int lrand48() {
105+
// advance state and return high-order 31 bits
106+
state = (state * MULTIPLIER + ADDEND) & MASK48;
107+
return (int) (mixLong(state) >>> (48 - 31));
108+
}
109+
110+
/**
111+
* erand48: updates the provided seed array in-place and returns a double in [0,1).
112+
* The input array must have at least 3 elements. Each element represents an
113+
* unsigned 16-bit word; Java's short is signed, so mask with 0xFFFF when reading.
114+
*
115+
* Array layout:
116+
* xsubi[0] = low 16 bits
117+
* xsubi[1] = middle 16 bits
118+
* xsubi[2] = high 16 bits (most significant)
119+
*
120+
* This mirrors the C POSIX definition.
121+
*
122+
* @param xsubi short[3] (modified in-place)
123+
* @return double in [0,1)
124+
* @throws IllegalArgumentException if xsubi.length &lt; 3
125+
*/
126+
public static double erand48(final short[] xsubi) {
127+
if (xsubi == null || xsubi.length < 3) {
128+
throw new IllegalArgumentException("xsubi must be a short[3] (or longer)");
129+
}
130+
// assemble 48-bit state from 3 unsigned 16-bit words (xsubi[0] = least significant)
131+
long s0 = xsubi[0] & 0xFFFFL;
132+
long s1 = xsubi[1] & 0xFFFFL;
133+
long s2 = xsubi[2] & 0xFFFFL;
134+
long state = (s2 << 32) | (s1 << 16) | s0;
135+
// update
136+
state = (state * MULTIPLIER + ADDEND) & MASK48;
137+
// write back into xsubi (low..high)
138+
xsubi[0] = (short) (state & 0xFFFFL);
139+
xsubi[1] = (short) ((state >>> 16) & 0xFFFFL);
140+
xsubi[2] = (short) ((state >>> 32) & 0xFFFFL);
141+
return mixLong(state) / (double) (1L << 48);
142+
}
143+
144+
/**
145+
* Convenience overload that accepts int[] - each int must be in 0..65535.
146+
* Useful because Java's short is signed.
147+
* @param xsubi int[3] (modified in-place)
148+
* @return double in [0,1)
149+
* @throws IllegalArgumentException if xsubi.length &lt; 3
150+
*/
151+
public static double erand48FromInts(final int[] xsubi) {
152+
if (xsubi == null || xsubi.length < 3) {
153+
throw new IllegalArgumentException("xsubi must be an int[3] (or longer)");
154+
}
155+
long s0 = xsubi[0] & 0xFFFFL;
156+
long s1 = xsubi[1] & 0xFFFFL;
157+
long s2 = xsubi[2] & 0xFFFFL;
158+
long state = (s2 << 32) | (s1 << 16) | s0;
159+
state = (state * MULTIPLIER + ADDEND) & MASK48;
160+
xsubi[0] = (int) (state & 0xFFFFL);
161+
xsubi[1] = (int) ((state >>> 16) & 0xFFFFL);
162+
xsubi[2] = (int) ((state >>> 32) & 0xFFFFL);
163+
return mixLong(state) / (double) (1L << 48);
164+
}
165+
}
166+

SimSystem/src/main/java/mathtools/distribution/tools/L32X64Mix.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public L32X64Mix() {
4343
* @param x the input value
4444
* @return the output value
4545
*/
46-
private static int lea32(int x) {
46+
public static int lea32(int x) {
4747
x = (x ^ (x >>> 16)) * 0xd36d884b;
4848
x = (x ^ (x >>> 16)) * 0xd36d884b;
4949
return x ^ (x >>> 16);

SimSystem/src/main/java/mathtools/distribution/tools/RandomGeneratorMode.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ public enum RandomGeneratorMode {
9797
L128X1024Mix
9898
*/
9999

100-
/** Pro Thread gekapselte Version von {@link Drand48BitsStreamGenerator} verwenden */
101-
DRAND48("Drand48",useSeed->new Drand48BitsStreamGenerator(),false);
100+
/** Pro Thread gekapselte Version von {@link Drand48BitsStreamGenerator} mit innerem {@link Drand48} verwenden */
101+
DRAND48("Drand48",useSeed->new Drand48BitsStreamGenerator(new Drand48()),false),
102+
/** Pro Thread gekapselte Version von {@link Drand48BitsStreamGenerator} mit innerem {@link Drand48Mix} verwenden */
103+
DRAND48MIX("Drand48Mix",useSeed->new Drand48BitsStreamGenerator(new Drand48Mix()),false);
102104

103105
/**
104106
* Standardmäßig zu verwendender Modus

Simulator/src/main/java/ui/help/pages_de/ChangeLog.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ <h1>Versionsgeschichte</h1>
1212
<h2>Version 6.1.0 (??.11.2026)</h2>
1313
<lul>
1414
<li>Wird der Simulator unter Java 17 oder höher ausgeführt, stehen 8 zusätzliche Pseudo-Zufallszahlengeneratoren aus dem JDK zur Verfügung.</li>
15+
<li>Drand48Mix als weiteren optionalen Pseudo-Zufasllszahlengenerator hinzugefügt.</li>
1516
<li>Bei der Aufzeichnung der Autokorrelation kann nun die Schrittweite eingestellt werden.</li>
1617
<li>[Fix] Der Überschreib-Warnungsdialog beim Speichern wird doppelt angezeigt.</li>
1718
</lul>

Simulator/src/main/java/ui/help/pages_en/ChangeLog.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ <h1>Version history</h1>
1212
<h2>Version 6.1.0 (2026-11-??)</h2>
1313
<ul>
1414
<li>If the simulator is running on Java 17 or later, 8 additional pseudo-random number generators from the JDK are available.</li>
15+
<li>Added Drand48Mix as additional optional pseudo-random number generator.</li>
1516
<li>When recording the autocorrelation, you can now set the step size.</li>
1617
<li>[Fix] Overwrite warning dialog is shown twice.</li>
1718
</ul>

0 commit comments

Comments
 (0)