001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  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 */
017package org.apache.commons.rng.core.source32;
018
019import org.apache.commons.rng.core.util.NumberFactory;
020
021/**
022 * Port from Marsaglia's <a href="http://www.cse.yorku.ca/~oz/marsaglia-rng.html">
023 * "KISS" algorithm</a>.
024 * This version contains the correction referred to
025 * <a href="https://programmingpraxis.com/2010/10/05/george-marsaglias-random-number-generators/">here</a>
026 * in a reply to the original post.
027 *
028 * @see <a href="https://en.wikipedia.org/wiki/KISS_(algorithm)">KISS (Wikipedia)</a>
029 * @since 1.0
030 */
031public class KISSRandom extends IntProvider {
032    /** Size of the seed. */
033    private static final int SEED_SIZE = 4;
034    /** State variable. */
035    private int z;
036    /** State variable. */
037    private int w;
038    /** State variable. */
039    private int jsr;
040    /** State variable. */
041    private int jcong;
042
043    /**
044     * Creates a new instance.
045     *
046     * @param seed Seed.
047     * If the length is larger than 4, only the first 4 elements will
048     * be used; if smaller, the remaining elements will be automatically
049     * set.
050     */
051    public KISSRandom(int[] seed) {
052        setSeedInternal(seed);
053    }
054
055    /** {@inheritDoc} */
056    @Override
057    protected byte[] getStateInternal() {
058        return composeStateInternal(NumberFactory.makeByteArray(new int[] {z, w, jsr, jcong}),
059                                    super.getStateInternal());
060    }
061
062    /** {@inheritDoc} */
063    @Override
064    protected void setStateInternal(byte[] s) {
065        final byte[][] c = splitStateInternal(s, SEED_SIZE * 4);
066
067        final int[] tmp = NumberFactory.makeIntArray(c[0]);
068        z = tmp[0];
069        w = tmp[1];
070        jsr = tmp[2];
071        jcong = tmp[3];
072
073        super.setStateInternal(c[1]);
074    }
075
076    /**
077     * Seeds the RNG.
078     *
079     * @param seed Seed.
080     */
081    private void setSeedInternal(int[] seed) {
082        // Reset the whole state of this RNG (i.e. the 4 state variables).
083        // Filling procedure is not part of the reference code.
084        final int[] tmp = new int[SEED_SIZE];
085        fillState(tmp, seed);
086
087        z = tmp[0];
088        w = tmp[1];
089        jsr = tmp[2];
090        jcong = tmp[3];
091    }
092
093    /** {@inheritDoc} */
094    @Override
095    public int next() {
096        z = computeNew(36969, z);
097        w = computeNew(18000, w);
098        final int mwc = (z << 16) + w;
099
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}