### Pseudorandom number generator

I use this pseudorandom number generator in many of my turtles, but I never tested its accuracy. At least it has a mean of ~0.5 and a good-looking histogram.

If you know the origin of this code, please let me know.

```// Pseudorandom number generator. Created by Reinder Nijhoff 2024 - @reindernijhoff
//
// https://turtletoy.net/turtle/a2274fd1fe
//

const turtle = new Turtle();

let seed = 1000; // min=1, max=1000000, step=1
const bits = 20; // min=1, max=30, step=1
const samples = 150000; // min=1, max=500000, step=1

const mod = 1<<bits;
const bins = Math.min(1<<8, 1<<bits);
const ksAlpha = 0.05; // 95% confidence level
const acLag = 1; // min=1, max=128, step=1

const values = [];

////////////////////////////////////////////////////////////////
// Pseudorandom number generator. Created by Reinder Nijhoff 2024
// https://turtletoy.net/turtle/a2274fd1fe
////////////////////////////////////////////////////////////////
function random() { // returns a number [0, 1[
let r = 1103515245 * (((seed+=12345) >> 1) ^ seed);
r = 1103515245 * (r ^ (r >> 3));
r = r ^ (r >> 16);
return (r % mod) / mod;
}

const oseed = seed;

function walk(i) {
const r = random();
// const r = (random() + random()) / 2;
// const r = (random() + random() + random()) / 3;

values.push(r);

drawPoint( i/samples, r, transform(10, -80, 70, 70) );

if (i >= samples) {
const text = new Text(HersheySans1);
turtle.jump(-80,-5);
text.print(turtle, 'Histogram', .35);
turtle.jump(10,-5);
text.print(turtle, 'Noise', .35);

const histogram = createHistogram(values, bins);
drawHistogram(histogram, transform(-80, -80, 70, 70));

const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
const corr = autocorrelation(values, acLag);

const criticalValue = ksCriticalValue(values, ksAlpha);
const ksStatistic = ksTest(values);

turtle.jump(-80, 10);
text.print(turtle, `Mean: \${mean.toFixed(4)}

Autocorrelation: \${corr.toFixed(4)} (lag \${acLag})
K-S Statistic: \${ksStatistic.toFixed(4)} (\${ksStatistic < criticalValue ? '<':'!! >='} \${criticalValue.toFixed(4)})

Seed: \${oseed}
Bits: \${bits}
Samples: \${samples}`, .6);
}

return i < samples;
}

function transform(l, t, w, h) {
return (x, y) => [l+w*x, t+h*y];
}

function drawPoint(x, y, t) {
const d = 0.0002;
turtle.jump( t(x-d, y-d) );
turtle.goto( t(x+d, y+d) );
}

//
// Noise Statistics, thanks to chatGPT
//
function createHistogram(a, bins) {
const histogram = new Array(bins).fill(0);
for (let value of a) {
histogram[value * bins | 0]++;
}
return histogram;
}

function drawHistogram(histogram, t) {
const n = histogram.filter(v => v > 0).length;
const mean = histogram.reduce((sum, value) => sum + value, 0) / n;
for (let i=0; i<bins; i++) {
turtle.jump( t(i/bins, 1) );
turtle.goto( t(i/bins, 1 - 0.5 * histogram[i] / mean));
}
}

function autocorrelation(a, lag) {
const n = a.length;
const mean = a.reduce((sum, value) => sum + value, 0) / n;
let numerator = 0;
let denominator = 0;
for (let i = 0; i < n - lag; i++) {
numerator += (a[i] - mean) * (a[i + lag] - mean);
}
for (let i = 0; i < n; i++) {
denominator += (a[i] - mean) ** 2;
}
return numerator / denominator;
}

function ksCriticalValue(a, alpha) {
return Math.sqrt(-0.5 * Math.log(alpha / 2) / a.length);
}

function ksTest(a) {
a.sort((x, y) => x - y); // Sort the array in ascending order
const n = a.length;
let d = 0;

for (let i = 0; i < n; i++) {
const empiricalCDF = (i + 1) / n; // Empirical CDF
const uniformCDF = a[i]; // Uniform CDF (assuming values in [0, 1])
d = Math.max(d, Math.abs(empiricalCDF - uniformCDF)); // Update K-S statistic
}

return d;
}

////////////////////////////////////////////////////////////////
// Text utility code. Created by Reinder Nijhoff 2024
// https://turtletoy.net/turtle/0f84fd3ae4
////////////////////////////////////////////////////////////////

function Text(fontData) {
const decode = (data) => {
const b = atob(data), a = new Int16Array(b.length / 2), g = {};
for (let i = 0; i < b.length; i+=2) {
a[i / 2 | 0] = b.charCodeAt(i) | (b.charCodeAt(i + 1) << 8);
}
for (let i = 0;i < a.length; i++) {
let u = String.fromCharCode(a[i++]), x = a[i++], d = [], p = [];
for (;a[i] !== -16384; i++) a[i] === 16384 ? (d.push(p), p = []) : p.push(a[i]);
g[u] = { x, d };
}
return g;
}
const rotAdd = (a, b, h) => [Math.cos(h)*a[0] - Math.sin(h)*a[1] + b[0],
Math.cos(h)*a[1] + Math.sin(h)*a[0] + b[1]];

const data = Object.fromEntries(Object.entries(fontData).map(([key, value]) =>
[key, key === 'glyphs' ? decode(value) : value]));

class Text {
print (t, str, scale = 1) {
let pos = t ? [t.x(), t.y()] : [0,0], h = t ? t.h() * Math.PI * 2 / t.fullCircle() : 0,
o = pos, s = scale / data.unitsPerEm;
str.split('').map(c => {
if (c == '\n') {
pos = o = rotAdd([0, 14 * scale], o, h);
return;
}
const glyph = (data.glyphs[c] || data.glyphs[' ']), d = glyph.d;
d.forEach( (p, k) => {
t && t.up();
for (let i=0; i<p.length; i+=2) {
t && t.goto(rotAdd([p[i]*s, p[i+1]*s], pos, h));
t && t.down();
}
});
pos = rotAdd([glyph.x*s, 0], pos, h);
});