View Javadoc
1   /*
2    * License : The MIT License
3    * Copyright(c) 2022 olyutorskii
4    */
5   
6   package io.github.olyutorskii.aletojio.shrink;
7   
8   import io.github.olyutorskii.aletojio.BitPool;
9   import io.github.olyutorskii.aletojio.rng.RndInt32;
10  import java.util.Objects;
11  
12  /**
13   * Von Neumann randomness extractor (whitening).
14   *
15   * <p>Crunching input data by an average of 50%
16   * and improve output data randomness bias.
17   *
18   * <ul>
19   * <li>...00... → ×
20   * <li>...01... → 0
21   * <li>...10... → 1
22   * <li>...11... → ×
23   * </ul>
24   *
25   * <p>Random number output throughput decreases half.
26   *
27   * @see <a href="https://en.wikipedia.org/wiki/Bernoulli_process#Basic_von_Neumann_extractor">
28   * Basic von Neumann extractor (Wikipedia)</a>
29   * @see <a href="https://en.wikipedia.org/wiki/Self-shrinking_generator">
30   * Self-shrinking generator (Wikipedia)</a>
31   */
32  @SuppressWarnings("serial")
33  public class VnExtractor implements RndInt32 {
34  
35      private static final int STEP = 2;
36      private static final int MASK_LSB2 = 0b11;
37  
38  
39      private final RndInt32 rnd;
40  
41      private final BitPool bitPool = new BitPool();
42  
43  
44      /**
45       * Constructor.
46       *
47       * @param rnd random generator
48       * @throws NullPointerException null argument
49       */
50      public VnExtractor(RndInt32 rnd) throws NullPointerException {
51          super();
52          Objects.requireNonNull(rnd);
53  
54          this.rnd = rnd;
55  
56          return;
57      }
58  
59  
60      /**
61       * fill random entropy to pool.
62       */
63      private void fillPool() {
64          while (this.bitPool.remaining() >= Integer.SIZE) {
65              int iRndVal = this.rnd.nextInt32();
66  
67              // TODO: High speed by 8-bit look-up table
68              for (int rsft = Integer.SIZE - STEP; rsft >= 0; rsft -= STEP) {
69                  int lsb2 = (iRndVal >>> rsft) & MASK_LSB2;
70  
71                  boolean bVal;
72                  switch (lsb2) {
73                  case 0b01:
74                      bVal = false;
75                      break;
76                  case 0b10:
77                      bVal = true;
78                      break;
79                  default:
80                      continue;
81                  }
82  
83                  this.bitPool.pushBoolean(bVal);
84              }
85          }
86  
87          return;
88      }
89  
90      /**
91       * {@inheritDoc}
92       *
93       * @return {@inheritDoc}
94       */
95      @Override
96      public int nextInt32() {
97          int result;
98  
99          if (this.bitPool.size() < Integer.SIZE) {
100             fillPool();
101         }
102         result = this.bitPool.chopInt();
103 
104         return result;
105     }
106 
107 }