View Javadoc
1   /*
2    * License : The MIT License
3    * Copyright(c) 2022 Olyutorskii
4    */
5   
6   package io.github.olyutorskii.aletojio.emergence;
7   
8   import java.util.Arrays;
9   
10  /**
11   * Ehrenfeucht-Mycielski random bits sequence.
12   *
13   * <p>Continue to generate random bits sequences acyclic and emergently
14   * until the memory capacity and indexing mechanism reach their limits.
15   *
16   * <p>This is Automatic sequence. This means infinite sequence from finite automaton.
17   *
18   * <p>It is only possible to add bit to the end of sequence.
19   *
20   * <p>TODO: use suffix tree.
21   *
22   * @see <a href="https://en.wikipedia.org/wiki/Ehrenfeucht%E2%80%93Mycielski_sequence">
23   * Ehrenfeucht Mycielski sequence.
24   * </a>
25   * @see <a href="https://oeis.org/A038219">A038219 sample list</a>
26   */
27  public class EfMsSeq {
28  
29      private static final int PRIMLEN = Long.SIZE;
30      private static final int INIT_VECLEN = 2;
31      private static final int GROWTH_RATE = 2;
32  
33  
34      private long[] vec;
35      private int capacity;
36  
37      private int bitLength;
38  
39  
40      /**
41       * Constructor.
42       *
43       * <p>Bit sequence is empty.
44       */
45      public EfMsSeq() {
46          super();
47  
48          this.vec = new long[INIT_VECLEN];
49          this.capacity = PRIMLEN * this.vec.length;
50          this.bitLength = 0;
51  
52          return;
53      }
54  
55      /**
56       * Return length of bit sequence.
57       *
58       * @return length of bit sequence
59       */
60      public int length() {
61          return this.bitLength;
62      }
63  
64      /**
65       * Ensure min capacity of bit length.
66       *
67       * @param minCapacity min capacity of bit length
68       */
69      private void ensureCapacity(int minCapacity) {
70          if (minCapacity <= this.capacity) {
71              return;
72          }
73  
74          int newArraySize = this.vec.length * GROWTH_RATE;
75          while (PRIMLEN * newArraySize < minCapacity) {
76              newArraySize *= GROWTH_RATE;
77          }
78  
79          this.vec = Arrays.copyOf(this.vec, newArraySize);
80  
81          this.capacity = PRIMLEN * this.vec.length;
82  
83          return;
84      }
85  
86      /**
87       * Append new bit to tail of bit sequence.
88       *
89       * @param iVal bit. 0 means 0b0, not 0 means 0b1.
90       */
91      public void append(int iVal) {
92          int newLength = this.bitLength + 1;
93          ensureCapacity(newLength);
94  
95          if (iVal != 0) {
96              int vecIdx = this.bitLength / PRIMLEN;
97              int bitPos = this.bitLength % PRIMLEN;
98              this.vec[vecIdx] |= 1L << bitPos;
99          }
100 
101         this.bitLength = newLength;
102 
103         return;
104     }
105 
106     /**
107      * Append new bits to tail of bit sequence.
108      *
109      * @param iVals bits. 0 means 0b0, not 0 means 0b1.
110      */
111     public void append(int... iVals) {
112         ensureCapacity(this.bitLength + iVals.length);
113 
114         for (int iVal : iVals) {
115             append(iVal);
116         }
117 
118         return;
119     }
120 
121     /**
122      * Get bit from bit sequence with index.
123      *
124      * <p>Index starts with 0 (oldest bit).
125      * Index ends with bit sequence length - 1 (recently added bit).
126      *
127      * @param idx bit position. 0 means oldest bit.
128      * @return bit. 0 means 0b0. 1 means 0b1.
129      * @throws IndexOutOfBoundsException illegal index
130      */
131     public int get(int idx) throws IndexOutOfBoundsException {
132         if (idx >= this.bitLength || idx < 0) {
133             throw new IndexOutOfBoundsException();
134         }
135 
136         int vecIdx = idx / PRIMLEN;
137         int bitPos = idx % PRIMLEN;
138 
139         long lVal = this.vec[vecIdx];
140         lVal = (lVal >> bitPos) & 1L;
141 
142         return (int) lVal;
143     }
144 
145     /**
146      * Return last index of suffix pattern appeared in bit sequence.
147      *
148      * <p>Suffix never unmatch it self.
149      *
150      * <p>Suffix of "01010010101" with index 8 is "101".
151      *
152      * <p>"101" pattern appear 3-times in "01010010101".
153      * 3rd one is ignored because last one is suffix it self.
154      *
155      * <p>Therefore, last index of "01010010101" with suffix index 8 is position 6
156      *
157      * @param suffixIdx index of suffix starting
158      * @return last index of suffix pattern appeared in bit sequence. Negative if unmatched.
159      * @throws IndexOutOfBoundsException invalid index
160      */
161     public int lastIdxSuffix(int suffixIdx) throws IndexOutOfBoundsException {
162         if (suffixIdx <= 0) throw new IndexOutOfBoundsException();
163         if (suffixIdx >= this.bitLength) throw new IndexOutOfBoundsException();
164 
165         int suffixLen = this.bitLength - suffixIdx;
166         int revStart = this.bitLength - suffixLen - 1;
167 
168         assert suffixLen > 0;
169         assert revStart >= 0;
170         assert revStart < this.bitLength - 1;
171 
172         int lastIdx = -1;
173 
174         for (int seqRevIdx = revStart; seqRevIdx >= 0; seqRevIdx--) {
175             boolean suffixMatched = true;
176 
177             for (int offset = 0; offset < suffixLen; offset++) {
178                 int bitSeq = get(seqRevIdx + offset);
179                 int bitSfx = get(suffixIdx + offset);
180                 if (bitSeq != bitSfx) {
181                     suffixMatched = false;
182                     break;
183                 }
184             }
185 
186             if (suffixMatched) {
187                 lastIdx = seqRevIdx;
188                 break;
189             }
190         }
191 
192         return lastIdx;
193     }
194 
195     /**
196      * Determin next random bit from past bits.
197      *
198      * @return next random bit. (0 or 1)
199      * @throws NullPointerException argument is null
200      */
201     public int nextBool()
202             throws NullPointerException {
203         int targetLen = length();
204         if (targetLen <= 0) return 0;
205 
206         int suffixNext = -1;
207 
208         for (int suffixIdx = 1; suffixIdx < targetLen; suffixIdx++) {
209             int matchedPos = lastIdxSuffix(suffixIdx);
210             if (matchedPos >= 0) {
211                 int suffixLen = targetLen - suffixIdx;
212                 suffixNext = matchedPos + suffixLen;
213                 break;
214             }
215         }
216 
217         int resultPos;
218         if (suffixNext >= 0) resultPos = suffixNext;
219         else                 resultPos = length() - 1;
220 
221         int resultNeg;
222         if (get(resultPos) == 0) resultNeg = 1;
223         else                     resultNeg = 0;
224 
225         return resultNeg;
226     }
227 
228     /**
229      * {@inheritDoc}
230      *
231      * @return {@inheritDoc}
232      */
233     @Override
234     public int hashCode() {
235         long lVal1st  = 0xdeadbeefaa554321L;
236         long lValLast = 0x1234aa55cafebabeL;
237         if (this.bitLength >= 1) {
238             int vecs = (this.bitLength - 1) / PRIMLEN + 1;
239             lVal1st  ^= this.vec[0]        * 23;
240             lValLast ^= this.vec[vecs - 1] * 29;
241         }
242         long lVal = lVal1st ^ lValLast;
243 
244         int result = 0;
245         result ^= (int)  lVal;
246         result ^= (int) (lVal >>> 32);
247 
248         return result;
249     }
250 
251     /**
252      * {@inheritDoc}
253      *
254      * @param obj {@inheritDoc}
255      * @return {@inheritDoc}
256      */
257     @Override
258     public boolean equals(Object obj) {
259         if (this == obj) {
260             return true;
261         }
262 
263         if (!(obj instanceof EfMsSeq)) {
264             return false;
265         }
266         EfMsSeq other = (EfMsSeq) obj;
267 
268         if (this.bitLength != other.bitLength) {
269             return false;
270         }
271 
272         boolean result = Arrays.equals(this.vec, other.vec);
273         return result;
274     }
275 
276     /**
277      * {@inheritDoc}
278      *
279      * <p>Output 0/1 sequence in historical order.
280      *
281      * @return bit sequence text. "nil" if empty.
282      */
283     @Override
284     public String toString() {
285         if (this.bitLength <= 0) return "nil";
286 
287         StringBuilder result = new StringBuilder();
288         result.ensureCapacity(this.bitLength);
289 
290         for (int bitIdx = 0; bitIdx < this.bitLength; bitIdx++) {
291             char ch;
292             if (get(bitIdx) == 0) ch = '0';
293             else                  ch = '1';
294             result.append(ch);
295         }
296 
297         return result.toString();
298     }
299 
300 }