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 }