Skip to content

Commit

Permalink
+ randogram drawing
Browse files Browse the repository at this point in the history
  • Loading branch information
Hawkynt committed Oct 28, 2024
1 parent a57aed8 commit 0910696
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 50 deletions.
10 changes: 4 additions & 6 deletions Randomizer/Randomizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PackageReadmeFile>..\Readme.md</PackageReadmeFile>

<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net8.0-windows</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
Expand All @@ -18,15 +18,13 @@
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/Hawkynt/Randomizer/tree/master/Randomizer</RepositoryUrl>
<PlatformTarget>AnyCPU</PlatformTarget>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>

<ItemGroup>
<None Remove="Benchy.cs~RFe66c2527.TMP" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
<PackageReference Include="FrameworkExtensions.Corlib" Version="1.0.2.*" />
<PackageReference Include="FrameworkExtensions.System.Drawing" Version="1.0.0.35" />
<PackageReference Include="System.Drawing.Common" Version="8.0.10" />
</ItemGroup>

<ItemGroup>
Expand Down
80 changes: 36 additions & 44 deletions Randomizer/Statistics/Randogram.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,11 @@
using System;
using System.Numerics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace Randomizer.Statistics;

/* Randogramm
Those are powerful visualization tools used to detect patterns or flaws in the output of RNGs. They provide a 2D representation of the state space, offering an intuitive way to assess the randomness of an RNG's output. By visualizing the output in this manner, we can quickly identify regularities or anomalies that might not be apparent through numerical analysis alone. Randograms extract distinct bits from different parts of the output value and convert them into (x, y) coordinates, using pixel intensity to represent occurrence frequency.
We can select bits using:
Groups of adjacent bits
Groups of bits from opposite halves of the output
Groups of bits from the start and their reverse from the end
Interleaved groups of bits
Bits from two consecutive outputs of the RNG
Visualizing 64-Bit Output with 256x256 Randograms
For a comprehensive analysis of a 64-bit RNG, we can use four separate 256x256 randograms, each visualizing a 16-bit segment of the output. Each randogram will represent a different 16-bit slice, allowing us to scrutinize the entire 64-bit output in detail.
Pixel Intensity Representation: In a randogram, each pixel represents the frequency of occurrence of a specific 16-bit value pair (x, y). The intensity of the pixel is determined by how often that particular pair appears in the output:
White (100% intensity) indicates that the pair does not appear at all.
50% grey indicates the pair appears once.
Darker shades represent multiple occurrences, with each additional occurrence halving the pixel's intensity.
This method allows us to identify potential patterns or repetitions that could indicate flaws in the RNG.
8-Bit Visualizations
For smaller-scale analysis, especially when working with simpler or reduced versions of RNGs, an 8-bit segment of the output can be visualized using a tiny randogram. This smaller grid is particularly useful for identifying issues in the least significant bits, which are often more prone to predictability or bias.
16x16 Randogram: In this visualization, each pixel corresponds to one of the possible 8-bit output values. The same intensity rules apply, with pixels turning darker as specific value pairs occur more frequently. A well-functioning RNG should produce a 16x16 randogram that appears random, with no discernible patterns.
Interpreting Randograms
"Random-looking": A high-quality RNG should produce randograms, whether 16x16 or 256x256, that appear as a uniform field of noise, without discernible patterns or regular structures. Such a visualization suggests that the RNG’s output is sufficiently random for practical use.
Patterns and Structures: If a randogram exhibits clear patterns, such as diagonal lines, clusters, or grids, it indicates that the RNG might have structural flaws or that its output is not sufficiently random. These patterns suggest that certain pairs of output values are more likely to occur together, which could compromise the statistical quality of the RNG.
*/
internal class Randogram : IValueTracker {

public enum BitSelectionMethod {
Expand All @@ -42,7 +16,7 @@ public enum BitSelectionMethod {
Consecutive
}

private readonly byte _bitCount;
private byte BitCount => (byte)(this._xBits + this._yBits);
private readonly BitSelectionMethod _method;
private readonly byte _yBits;
private readonly byte _xBits;
Expand All @@ -51,7 +25,9 @@ public enum BitSelectionMethod {
private ulong? _lastValue;

public Randogram(byte bitCount, BitSelectionMethod method) {
this._bitCount = bitCount is >= 2 and <= 64 ? bitCount : throw new ArgumentOutOfRangeException(nameof(bitCount), bitCount, $"Bit count has to be 2 <= x <= 64, got {bitCount}");
if (bitCount is < 2 or > 64)
throw new ArgumentOutOfRangeException(nameof(bitCount), bitCount, $"Bit count has to be 2 <= x <= 64, got {bitCount}");

this._method = Enum.IsDefined(method) ? method : throw new ArgumentOutOfRangeException(nameof(method), method, $"Unknown bit selection method:{method}");
this._yBits = (byte)(bitCount >> 1);
this._xBits = (byte)(bitCount - this._yBits);
Expand All @@ -61,26 +37,42 @@ public Randogram(byte bitCount, BitSelectionMethod method) {
public void Feed(ulong value) {
var coordinates = this._SelectBits(value);
if (coordinates!=null)
++this._histogram[coordinates.Item2, coordinates.Item1];
++this._histogram[coordinates.Value.y, coordinates.Value.x];

this._lastValue = value;
}

private Tuple<ulong, ulong>? _SelectBits(ulong value) {
private (ulong x, ulong y)? _SelectBits(ulong value) {
return this._method switch {
BitSelectionMethod.Adjacent => Tuple.Create(value.Bits(0, this._xBits), value.Bits(this._xBits, this._yBits)),
BitSelectionMethod.OppositeHalves => Tuple.Create(value.Bits(0, this._xBits), value.Bits(32, this._yBits)),
BitSelectionMethod.StartAndReverse => Tuple.Create(value.Bits(0, this._xBits), value.ReverseBits().Bits(0, this._yBits)),
BitSelectionMethod.Adjacent => (value.Bits(0, this._xBits), value.Bits(this._xBits, this._yBits)),
BitSelectionMethod.OppositeHalves => (value.Bits(0, this._xBits), value.Bits(32, this._yBits)),
BitSelectionMethod.StartAndReverse => (value.Bits(0, this._xBits), value.ReverseBits().Bits(0, this._yBits)),
BitSelectionMethod.Consecutive when this._lastValue == null => null,
BitSelectionMethod.Consecutive => Tuple.Create(value.Bits(0, this._xBits), this._lastValue.Value.Bits(0, this._yBits)),
BitSelectionMethod.Interleaved when value.DeinterleaveBits() is var (o, e) => Tuple.Create((ulong)o.Bits(0,this._xBits), (ulong)e.Bits(0, this._yBits)),
BitSelectionMethod.Consecutive => (value.Bits(0, this._xBits), this._lastValue.Value.Bits(0, this._yBits)),
BitSelectionMethod.Interleaved when value.DeinterleaveBits() is var (o, e) => (o.Bits(0, this._xBits), e.Bits(0, this._yBits)),
_ => throw new NotSupportedException($"Unknown method: {this._method}")
};
}


public void Print() {
throw new NotImplementedException();

public void Print() => this.SaveToPng(new("randogram.png"));

public void SaveToPng(FileInfo file) {
var width = 1 << this._xBits;
var height = 1 << this._yBits;
using var bitmap = new Bitmap(width, height);

var max = this._histogram.Cast<ulong>().Max();
var maxLog = Math.Log((max == 0 ? 1 : max) + 1);

using (var locker = bitmap.Lock())
for (var y = 0; y < height; ++y)
for (var x = 0; x < width; ++x) {
var intensityFactor = Math.Log((double)this._histogram[y, x] + 1) / maxLog;
var intensity = (int)(255 * (1 - intensityFactor));
locker[x, y] = Color.FromArgb(intensity, intensity, intensity);
}

bitmap.SaveToPng(file);
}

}

0 comments on commit 0910696

Please sign in to comment.