zncb.

code:dsp:infosec:sounds | ams:txl:nrt:yul

MT19937 Output Space Visualization

Stumbled upon an old snippet of code implementing a 32bit-word Mersenne Twister and visualizing the output space distribution using ANSI escape sequences in the terminal.

Mersenne Twister MT19937 Output Space Visualization - log10 scale

This is not very useful, but by running it with various parameters and seed one can notice some dead zones where very few values are produced (especially visible when using a 64x64 space division). Of course it is common knowledge that this PRNG is not cryptographically secure, and has otherwise very good properties for other uses. Nevertheless a bit of an intriguing curiosity.

This was originally posted in the previous version of this blog, so here it is again.

mt19937.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <signal.h>

#define MT_SIZE 624
#define GEN_OFST 397
#define INIT_K 0x6c078965

#define A 0x9908b0df
#define U 11
#define S 7
#define B 0x9d2c55680
#define T 15
#define C 0xefc60000
#define L 18

#define ESC 27
#define CLS() printf("%c[2J",ESC)
#define POS(row,col) printf("%c[%d;%dH",ESC,row,col)

uint32_t gMT[MT_SIZE];
uint16_t gIdx;

uint32_t gCount4096[65636];

typedef char Grid64[64][64][9];

static inline
void init_gen(uint32_t seed)
{
    uint32_t * __restrict mt = &gMT[0];
    const uint32_t *mte = mt + MT_SIZE;

    *(mt++) = seed;

    for (; mt!=mte; ++mt) {
        *mt = (INIT_K * (*(mt-1) ^ (*(mt-1)>>30))) & 0xffffffff;
    }
}

static inline
void gen_num()
{
    uint32_t * __restrict mt = &gMT[0];

    for (uint16_t i=0;i<MT_SIZE;++i) {
        const uint32_t y = (mt[i] & 0x80000000) + (mt[(i+1)%MT_SIZE] & 0x7ffffffff);
        mt[i] = mt[(i+GEN_OFST)%MT_SIZE] ^ (y<<1);
        if (y%2) {
            mt[i] = mt[i] ^ A;
        }
    }
}

static inline
uint32_t xtrct_num()
{
    if (!gIdx) {
        gen_num();
    }

    uint32_t y = gMT[gIdx];
    y ^= y>>U;
    y ^= y<<S & B;
    y ^= y<<T & C;
    y ^= y>>L;

    gIdx = (gIdx+1)%MT_SIZE;

    return y;
}

static inline
char *bin32(uint32_t val, char *buf)
{
    char *pbuf = buf;
    memset(pbuf,'0',33);
    pbuf += 33;
    *pbuf-- = '\0';
    while (val) {
        *pbuf-- = (val & 0x1)?'1':'0';
        val >>= 1;
    }
    return buf;
}


static inline
void cgrid6464_init(Grid64 buf)
{
    const char d[9] = "\e[2;30mo";

    for(int8_t i=31;i>=0;--i)
        for(int8_t j=63;j>=0;--j)
            for (int8_t k=8;k>=0;--k)
                buf[i][j][k] = d[k];
}

static inline
void cgrid6464_upd(uint32_t val, Grid64 buf)
{
    const uint16_t map =  val & 0xffff;
    const uint8_t row = ((map & 0x1f00) >> 8);
    const uint8_t col = (map & 0x3f);
    const uint16_t index = col*32+row;

    gCount4096[index] += 1;

    POS(row+1,col+1);

    const char d = buf[row][col][5] = '0'+((int)(log10f((float)gCount4096[index]))&0x7);

    printf("%c[1;3%cm%c",ESC,d,'0'+(int)(log10f((float)gCount4096[index])));
}

void cleanup()
{
    //FIXME: Resetting the cursor and clearing doesn't work. Probably due to interrupt inside printf.
    fflush(stdout);
    printf("%c[0m",ESC);
    CLS();
}

int main (int argc, char *argv[])
{
    uint32_t period = 0, num=0;
    char buf[33];
    Grid64 grid;
    int i = 0;
    gIdx = 0;

    memset(gMT,0x0,MT_SIZE*sizeof(uint32_t));
    memset(gCount4096,0x0,4096*sizeof(uint32_t));

    init_gen(0x2f9a109b);
    //init_gen(0x10000); // Very bad seed but if curious to see the process of recovery

    atexit(cleanup);

    CLS(); // Clear screen
    cgrid6464_init(grid);
    do {
        cgrid6464_upd(xtrct_num(),grid);
    } while (++period != 0);

    return 1;
}