001/*  Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org)
002
003To the extent possible under law, the author has dedicated all copyright
004and related and neighboring rights to this software to the public domain
005worldwide. This software is distributed without any warranty.
006
007See <http://creativecommons.org/publicdomain/zero/1.0/>. */
008package squidpony.squidmath;
009
010import squidpony.StringKit;
011
012import java.io.Serializable;
013
014/**
015 * A Non-Linear Feedback Shift Register that may be used like a StatefulRandomness but is not truly random. This is
016 * based on the {@link LFSR} class, and is less predictable but is otherwise less than optimal in some ways. It has a
017 * period of (2 to the 27) minus 1, and uses data from
018 * http://people.kth.se/~dubrova/nlfsr.html and https://eprint.iacr.org/2012/314.pdf . You would normally only prefer
019 * NLFSR over LFSR if you expect players to scrutinize your randomly-generated data, or if you want to use it as part of
020 * a more complex process such as encoding a saved file in a more robust way. Since 2 to the 27 numbers can be produced
021 * and analyzed in a matter of seconds, you'd need a lot of independent steps like this to actually improve encoding.
022 * It is important to note that an NLFSR or LFSR will produce each number from 1 until its maximum exactly once before
023 * repeating, so this may be useful as a way of generating test data in an unpredictable order.
024 * @author Tommy Ettinger
025 */
026public class NLFSR implements StatefulRandomness, Serializable {
027
028        private static final long DOUBLE_MASK = (1L << 53) - 1;
029    private static final double NORM_53 = 1. / (1L << 53);
030    private static final long FLOAT_MASK = (1L << 24) - 1;
031    private static final double NORM_24 = 1. / (1L << 24);
032
033        private static final long serialVersionUID = -2373549048478690398L;
034
035    public int state;
036
037    /**
038     * Creates a new generator seeded using Math.random.
039     */
040    public NLFSR() {
041        this((int) (Math.random() * Long.MAX_VALUE));
042    }
043
044    public NLFSR(final int seed) {
045        if(seed <= 0 || seed > 134217727)
046            state = 134217727;
047        else
048            state = seed;
049    }
050
051    public NLFSR(final CharSequence seed)
052    {
053        this(CrossHash.hash(seed));
054    }
055
056
057    @Override
058    public int next(int bits) {
059        return (int) (nextLong() >>> (64 - bits));
060    }
061
062    @Override
063    public long nextLong() {
064        return nextInt() * 0x2000000000L ^ nextInt() * 0x40000L ^ nextInt();
065    }
066
067    /**
068     * Produces up to 27 bits of random int, with a minimum result of 1 and a max of 134217727 (both inclusive).
069     * @return a random int between 1 and 134217727, both inclusive
070     */
071    public int nextInt() {
072        return state = (state >>> 1 | (0x4000000 & (
073                (state << 26) //0
074                        ^ (state << 22) //4
075                        ^ (state << 18) //8
076                        ^ (state << 17) //9
077                        ^ (state << 15) //11
078                        ^ (state << 14) //12
079                        ^ (state << 11) //15
080                        ^ (state << 10) //16
081                        ^ (state << 3)  //23
082                        ^ ((state << 14) & (state << 4)) //12 22
083                        ^ ((state << 13) & (state << 3)) //13 23
084                        ^ ((state << 13) & (state << 1)) //13 25
085                        ^ ((state << 4) & (state << 3))  //22 23
086                        ^ ((state << 19) & (state << 18) & (state << 2))  //7 8 24
087                        ^ ((state << 14) & (state << 12) & (state))       //12 14 26
088                        ^ ((state << 20) & (state << 15) & (state << 7) & (state << 4))       //6 11 19 22
089
090        )));
091    }
092
093    /**
094     * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
095     * copy, both will generate the same sequence of random numbers from the point copy() was called. This just needs to
096     * copy the state so it isn't shared, usually, and produce a new value with the same exact state.
097     *
098     * @return a copy of this RandomnessSource
099     */
100    @Override
101    public NLFSR copy() {
102        return new NLFSR(state);
103    }
104
105    /**
106     * Exclusive on the upper bound.  The lower bound is 0.
107     * @param bound the upper bound; should be positive
108     * @return a random int less than n and at least equal to 0
109     */
110    public int nextInt( final int bound ) {
111        return (int)((bound * (nextLong() & 0x7FFFFFFFL)) >> 31);
112    }
113    /**
114     * Inclusive lower, exclusive upper.
115     * @param lower the lower bound, inclusive, can be positive or negative
116     * @param upper the upper bound, exclusive, should be positive, must be greater than lower
117     * @return a random int at least equal to lower and less than upper
118     */
119    public int nextInt( final int lower, final int upper ) {
120        if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
121        return lower + nextInt(upper - lower);
122    }
123
124    /**
125     * Exclusive on the upper bound. The lower bound is 0.
126     * @param bound the upper bound; should be positive
127     * @return a random long less than n
128     */
129    public long nextLong( long bound ) {
130        long rand = nextLong();
131        if (bound <= 0) return 0;
132        final long randLow = rand & 0xFFFFFFFFL;
133        final long boundLow = bound & 0xFFFFFFFFL;
134        rand >>>= 32;
135        bound >>>= 32;
136        final long a = rand * bound;
137        final long b = randLow * boundLow;
138        return (((b >>> 32) + (rand + randLow) * (bound + boundLow) - a - b) >>> 32) + a;
139    }
140
141    public double nextDouble() {
142        return (nextLong() & DOUBLE_MASK) * NORM_53;
143    }
144
145    public float nextFloat() {
146        return (float) ((nextLong() & FLOAT_MASK) * NORM_24);
147    }
148
149    public boolean nextBoolean() {
150        return (nextInt() & 1) == 0;
151    }
152
153    public void nextBytes(final byte[] bytes) {
154        int i = bytes.length, n;
155        while (i != 0) {
156            n = Math.min(i, 8);
157            for (long bits = nextLong(); n-- != 0; bits >>>= 8) {
158                bytes[--i] = (byte) bits;
159            }
160        }
161    }
162
163    /**
164     * Get the current internal state of the StatefulRandomness as a long.
165     *
166     * @return the current internal state of this object.
167     */
168    @Override
169    public long getState() {
170        return state;
171    }
172
173    /**
174     * Sets the seed of this generator using one long, running that through LightRNG's algorithm twice to get the state.
175     * @param seed the number to use as the seed
176     */
177    @Override
178    public void setState(final long seed) {
179         state = (int) ((seed & 0x7FFFFFFFFFFFFFFFL) % 134217727) + 1;
180    }
181
182    @Override
183    public String toString() {
184        return "NLFSR with state 0x" + StringKit.hex(state);
185    }
186
187    @Override
188    public boolean equals(Object o) {
189        if (this == o) return true;
190        if (o == null || getClass() != o.getClass()) return false;
191
192        NLFSR nlfsr = (NLFSR) o;
193
194        return (state == nlfsr.state);
195    }
196
197    @Override
198    public int hashCode() {
199        return state;
200    }
201}