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}