1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.rng.core.source32;
18
19 import org.apache.commons.rng.core.util.NumberFactory;
20
21 /**
22 * Port from Marsaglia's <a href="http://www.cse.yorku.ca/~oz/marsaglia-rng.html">
23 * "KISS" algorithm</a>.
24 * This version contains the correction referred to
25 * <a href="https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/">here</a>
26 * in a reply to the original post.
27 *
28 * @see <a href="https://en.wikipedia.org/wiki/KISS_(algorithm)">KISS (Wikipedia)</a>
29 * @since 1.0
30 */
31 public class KISSRandom extends IntProvider {
32 /** Size of the seed. */
33 private static final int SEED_SIZE = 4;
34 /** State variable. */
35 private int z;
36 /** State variable. */
37 private int w;
38 /** State variable. */
39 private int jsr;
40 /** State variable. */
41 private int jcong;
42
43 /**
44 * Creates a new instance.
45 *
46 * @param seed Seed.
47 * If the length is larger than 4, only the first 4 elements will
48 * be used; if smaller, the remaining elements will be automatically
49 * set.
50 */
51 public KISSRandom(int[] seed) {
52 setSeedInternal(seed);
53 }
54
55 /** {@inheritDoc} */
56 @Override
57 protected byte[] getStateInternal() {
58 return composeStateInternal(NumberFactory.makeByteArray(new int[] {z, w, jsr, jcong}),
59 super.getStateInternal());
60 }
61
62 /** {@inheritDoc} */
63 @Override
64 protected void setStateInternal(byte[] s) {
65 final byte[][] c = splitStateInternal(s, SEED_SIZE * 4);
66
67 final int[] tmp = NumberFactory.makeIntArray(c[0]);
68 z = tmp[0];
69 w = tmp[1];
70 jsr = tmp[2];
71 jcong = tmp[3];
72
73 super.setStateInternal(c[1]);
74 }
75
76 /**
77 * Seeds the RNG.
78 *
79 * @param seed Seed.
80 */
81 private void setSeedInternal(int[] seed) {
82 // Reset the whole state of this RNG (i.e. the 4 state variables).
83 // Filling procedure is not part of the reference code.
84 final int[] tmp = new int[SEED_SIZE];
85 fillState(tmp, seed);
86
87 z = tmp[0];
88 w = tmp[1];
89 jsr = tmp[2];
90 jcong = tmp[3];
91 }
92
93 /** {@inheritDoc} */
94 @Override
95 public int next() {
96 z = computeNew(36969, z);
97 w = computeNew(18000, w);
98 final int mwc = (z << 16) + w;
99
100 // Cf. correction mentioned in the reply to the original post:
101 // https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/
102 jsr ^= jsr << 13;
103 jsr ^= jsr >>> 17;
104 jsr ^= jsr << 5;
105
106 jcong = 69069 * jcong + 1234567;
107
108 return (mwc ^ jcong) + jsr;
109 }
110
111 /**
112 * Compute new value.
113 *
114 * @param mult Multiplier.
115 * @param previous Previous value.
116 * @return new value.
117 */
118 private static int computeNew(int mult,
119 int previous) {
120 return mult * (previous & 65535) + (previous >>> 16);
121 }
122 }