Skip to content

Commit 9b5161e

Browse files
committed
Add new test: maj2random
maj2random is a simplified floating-point hash function derived from SHA-2, retaining its high-quality entropy compression function modified to permute entropy from a vec2 (designed for UV coordinates) returning float values between 0.0 and 1.0. Source: https://github.com/michaeljclark/maj2random
1 parent 9ab7a89 commit 9b5161e

File tree

3 files changed

+256
-0
lines changed

3 files changed

+256
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ In `rv32emu` repository, there are some prebuilt ELF files for testing purpose.
149149
* `dhrystone.elf` : See [rv8-bench](https://github.com/michaeljclark/rv8-bench)
150150
* `doom.elf` : See [sysprog21/doom_riscv](https://github.com/sysprog21/doom_riscv) [RV32M]
151151
* `ieee754.elf` : See [tests/ieee754.c](tests/ieee754.c) [RV32F]
152+
* `maj2random.elf` : See [tests/maj2random.c](tests/maj2random.c) [RV32F]
152153
* `mandelbrot.elf` : See [tests/mandelbrot.c](tests/mandelbrot.c)
153154
* `nqueens.elf` : See [tests/nqueens.c](tests/nqueens.c)
154155
* `pi.elf` : See [tests/pi.c](tests/pi.c) [RV32M]

build/maj2random.elf

75.5 KB
Binary file not shown.

tests/maj2random.c

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
/*
2+
* maj2random
3+
*
4+
* maj2random is a simplified floating point hash function derived from SHA-2,
5+
* retaining its high quality entropy compression function modified to permute
6+
* entropy from a vec2 (designed for UV coordinates) returning float values
7+
* between 0.0 and 1.0. since maj2_random is a hash function it will return
8+
* coherent noise. vector argument can be truncated prior to increase grain.
9+
*
10+
* maj2random is named after the maj mixing function called within the SHA-2
11+
* round function and the 2 is becuase maj2random uses 2 rounds instead of 64.
12+
* This seems to be sufficient to create visually diffuse noise with only 3%
13+
* of the overhead of the full SHA-256 hashing function. maj2random also pays
14+
* homage to arc4random due to the similarity of its name form.
15+
*
16+
* Source: https://github.com/michaeljclark/maj2random
17+
*/
18+
19+
#include <math.h>
20+
typedef unsigned uint;
21+
typedef struct {
22+
float a[2];
23+
} vec2;
24+
typedef struct {
25+
uint a[2];
26+
} uvec2;
27+
28+
/* first 8 rounds of the SHA-256 k constant */
29+
static uint sha256_k[8] = {
30+
0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u,
31+
0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
32+
};
33+
34+
static uint ror(uint x, int d)
35+
{
36+
return (x >> d) | (x << (32 - d));
37+
}
38+
static uint sigma0(uint h1)
39+
{
40+
return ror(h1, 2) ^ ror(h1, 13) ^ ror(h1, 22);
41+
}
42+
static uint sigma1(uint h4)
43+
{
44+
return ror(h4, 6) ^ ror(h4, 11) ^ ror(h4, 25);
45+
}
46+
static uint ch(uint x, uint y, uint z)
47+
{
48+
return z ^ (x & (y ^ z));
49+
}
50+
static uint maj(uint x, uint y, uint z)
51+
{
52+
return (x & y) ^ ((x ^ y) & z);
53+
}
54+
static uint gamma0(uint a)
55+
{
56+
return ror(a, 7) ^ ror(a, 18) ^ (a >> 3);
57+
}
58+
static uint gamma1(uint b)
59+
{
60+
return ror(b, 17) ^ ror(b, 19) ^ (b >> 10);
61+
}
62+
63+
static vec2 unorm(uvec2 n)
64+
{
65+
vec2 r = {(float) (n.a[0] & ((1u << 23) - 1u)) / (float) ((1u << 23) - 1u),
66+
(float) (n.a[1] & ((1u << 23) - 1u)) / (float) ((1u << 23) - 1u)};
67+
return r;
68+
}
69+
static uvec2 sign(vec2 v)
70+
{
71+
uvec2 r = {v.a[0] < 0.0f, v.a[1] < 0.0f};
72+
return r;
73+
}
74+
75+
static uvec2 maj_extract(vec2 uv)
76+
{
77+
/*
78+
* extract 48-bits of entropy from mantissas to create truncated
79+
* two word initialization vector 'W' composed using the 48-bits
80+
* of 'uv' entropy rotated and copied to keep the field equalized.
81+
* the exponent is ignored because the inputs are expected to be
82+
* normalized 'uv' values such as texture coordinates. it would be
83+
* beneficial to include the exponent entropy but we can't depend
84+
* on frexp or ilogb and log2 would be inaccurate.
85+
*/
86+
uvec2 s = sign(uv);
87+
uint x = (uint) (fabsf(uv.a[0]) * (float) (1u << 23)) | (s.a[0] << 23);
88+
uint y = (uint) (fabsf(uv.a[1]) * (float) (1u << 23)) | (s.a[1] << 23);
89+
90+
uvec2 r = {(x) | (y << 24), (y >> 8) | (x << 16)};
91+
return r;
92+
}
93+
94+
static vec2 maj_random(vec2 uv, uint NROUNDS)
95+
{
96+
uint H[8] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
97+
uint W[2];
98+
99+
uvec2 st = maj_extract(uv);
100+
101+
W[0] = st.a[0];
102+
W[1] = st.a[1];
103+
104+
for (uint i = 0; i < NROUNDS; i++) {
105+
W[i & 1] = gamma1(W[(i - 2) & 1]) + W[(i - 7) & 1] +
106+
gamma0(W[(i - 15) & 1]) + W[(i - 16) & 1];
107+
}
108+
109+
/* we use N rounds instead of 64 and alternate 2 words of iv in W */
110+
for (uint i = 0; i < NROUNDS; i++) {
111+
uint T0 =
112+
W[i & 1] + H[7] + sigma1(H[4]) + ch(H[4], H[5], H[6]) + sha256_k[i];
113+
uint T1 = maj(H[0], H[1], H[2]) + sigma0(H[0]);
114+
H[7] = H[6];
115+
H[6] = H[5];
116+
H[5] = H[4];
117+
H[4] = H[3] + T0;
118+
H[3] = H[2];
119+
H[2] = H[1];
120+
H[1] = H[0];
121+
H[0] = T0 + T1;
122+
}
123+
uvec2 u = {H[0] ^ H[1] ^ H[2] ^ H[3], H[4] ^ H[5] ^ H[6] ^ H[7]};
124+
return unorm(u);
125+
}
126+
127+
#include <inttypes.h>
128+
#include <stdint.h>
129+
#include <stdio.h>
130+
#include <stdlib.h>
131+
#include <string.h>
132+
133+
typedef unsigned long long ullong;
134+
135+
void sncatprintf(char *buf, size_t buflen, const char *fmt, int64_t n)
136+
{
137+
size_t len = strlen(buf);
138+
snprintf(buf + len, buflen - len, fmt, n);
139+
}
140+
141+
char *format_comma(int64_t n)
142+
{
143+
static char buf[65];
144+
145+
buf[0] = 0;
146+
int n2 = 0;
147+
int scale = 1;
148+
if (n < 0) {
149+
buf[0] = '-';
150+
buf[1] = 0;
151+
n = -n;
152+
}
153+
while (n >= 1000) {
154+
n2 = n2 + scale * (n % 1000);
155+
n /= 1000;
156+
scale *= 1000;
157+
}
158+
sncatprintf(buf, sizeof(buf), "%d", n);
159+
while (scale != 1) {
160+
scale /= 1000;
161+
n = n2 / scale;
162+
n2 = n2 % scale;
163+
sncatprintf(buf, sizeof(buf), ",%03d", n);
164+
}
165+
return buf;
166+
}
167+
168+
char *format_rate(double rate)
169+
{
170+
static char buf[65];
171+
char unit = ' ';
172+
double divisor = 1;
173+
if (rate > 1e9) {
174+
divisor = 1e9;
175+
unit = 'G';
176+
} else if (rate > 1e6) {
177+
divisor = 1e6;
178+
unit = 'M';
179+
} else if (rate > 1e3) {
180+
divisor = 1e3;
181+
unit = 'K';
182+
}
183+
snprintf(buf, sizeof(buf), "%.1f %c", rate / divisor, unit);
184+
return buf;
185+
}
186+
187+
vec2 f1(ullong i, ullong j)
188+
{
189+
return (vec2){0.0f, (float) i / (float) j};
190+
}
191+
vec2 f2(ullong i, ullong j)
192+
{
193+
return (vec2){(float) i / (float) j, 0.0f};
194+
}
195+
vec2 f3(ullong i, ullong j)
196+
{
197+
return (vec2){(float) i / (float) j, (float) i / (float) j};
198+
}
199+
200+
void test_maj(const char *name,
201+
int NROUNDS,
202+
ullong count,
203+
ullong range,
204+
vec2 (*f)(ullong, ullong))
205+
{
206+
vec2 sum = {0.f, 0.f};
207+
vec2 var = {0.f, 0.f};
208+
209+
fflush(stdout);
210+
for (ullong i = 0; i < count; i++) {
211+
vec2 p = f(i, range);
212+
vec2 q = maj_random(p, NROUNDS);
213+
sum.a[0] += q.a[0];
214+
sum.a[1] += q.a[1];
215+
var.a[0] += (q.a[0] - .5f) * (q.a[0] - .5f);
216+
var.a[1] += (q.a[1] - .5f) * (q.a[1] - .5f);
217+
}
218+
219+
printf("%-32s%12s%12.5f%12.5f%12.5f%12.5f%12.5f%12.5f\n", name,
220+
format_comma(count), sum.a[0] / count, sum.a[1] / count,
221+
var.a[0] / count, var.a[1] / count, sqrt(var.a[0] / count),
222+
sqrt(var.a[1] / count));
223+
}
224+
225+
void test_header(const char *name)
226+
{
227+
printf("%-32s%12s%12s%12s%12s%12s%12s%12s\n", name, "count", "mean(x)",
228+
"mean(y)", "variance(x)", "variance(y)", "std-dev(x)", "std-dev(y)");
229+
}
230+
231+
void run_all_tests(int NROUNDS, ullong i, ullong j)
232+
{
233+
char name[32];
234+
snprintf(name, sizeof(name), "maj_random (NROUNDS=%d)", NROUNDS);
235+
printf("\n");
236+
test_header(name);
237+
test_maj("( 0, (0 - 1K)/8K )", NROUNDS, i, j, f1);
238+
test_maj("( 0, (0 - 8K)/8K )", NROUNDS, j, j, f1);
239+
test_maj("( (0 - 1K)/8K ), 0 )", NROUNDS, i, j, f2);
240+
test_maj("( (0 - 8K)/8K ), 0 )", NROUNDS, j, j, f2);
241+
test_maj("( (0 - 1K)/8K ), (0 - 1K)/8K )", NROUNDS, i, j, f3);
242+
test_maj("( (0 - 8K)/8K ), (0 - 8K)/8K )", NROUNDS, j, j, f3);
243+
printf("\n");
244+
}
245+
246+
int main(int argc, char **argv)
247+
{
248+
ullong i = 1000;
249+
ullong j = 8000;
250+
run_all_tests(2, i, j);
251+
run_all_tests(4, i, j);
252+
run_all_tests(6, i, j);
253+
run_all_tests(8, i, j);
254+
return 0;
255+
}

0 commit comments

Comments
 (0)