// Случайные числа: PRNG vs CSPRNG
// Компиляция: zig build-exe random_demo.zig && ./random_demo

const std = @import("std");

/// Xorshift64 — простой, быстрый, предсказуемый PRNG
const Xorshift64 = struct {
    state: u64,

    fn init(seed: u64) Xorshift64 {
        return .{ .state = if (seed == 0) 1 else seed };
    }

    fn next(self: *Xorshift64) u64 {
        var x = self.state;
        x ^= x << 13;
        x ^= x >> 7;
        x ^= x << 17;
        self.state = x;
        return x;
    }
};

/// Линейный конгруэнтный генератор (LCG) — классический плохой PRNG
const LCG = struct {
    state: u32,

    fn init(seed: u32) LCG {
        return .{ .state = seed };
    }

    fn next(self: *LCG) u32 {
        // Параметры из Numerical Recipes
        self.state = self.state *% 1664525 +% 1013904223;
        return self.state;
    }
};

pub fn main() !void {
    const stdout = std.fs.File.stdout().deprecatedWriter();

    try stdout.print("=== Zig: случайные числа ===\n\n", .{});

    // 1. Xorshift64 — ручной PRNG
    try stdout.print("--- 1. Xorshift64 (PRNG) ---\n", .{});
    var rng = Xorshift64.init(12345);
    try stdout.print("Первые 10 чисел [0, 100): ", .{});
    for (0..10) |_| {
        try stdout.print("{d} ", .{rng.next() % 100});
    }
    try stdout.print("\n\n", .{});

    // 2. Предсказуемость
    try stdout.print("--- 2. Предсказуемость PRNG ---\n", .{});
    var rng1 = Xorshift64.init(42);
    var rng2 = Xorshift64.init(42);
    var same = true;
    for (0..100) |_| {
        if (rng1.next() != rng2.next()) {
            same = false;
            break;
        }
    }
    try stdout.print("Два xorshift64 с seed=42: {s}\n", .{
        if (same) "идентичны" else "различны",
    });
    try stdout.print("PRNG детерминирован: зная seed, предсказуемы все числа.\n\n", .{});

    // 3. LCG — классический плохой PRNG
    try stdout.print("--- 3. LCG ---\n", .{});
    var lcg = LCG.init(54321);
    try stdout.print("Первые 10 чисел [0, 100): ", .{});
    for (0..10) |_| {
        try stdout.print("{d} ", .{lcg.next() % 100});
    }
    try stdout.print("\n", .{});
    try stdout.print("LCG: короткий период, корреляция младших бит.\n\n", .{});

    // 4. CSPRNG из ОС
    try stdout.print("--- 4. CSPRNG (ОС) ---\n", .{});
    var buf: [8]u8 = undefined;
    std.crypto.random.bytes(&buf);
    try stdout.print("8 байт от ОС: ", .{});
    for (buf) |b| {
        try stdout.print("{x:0>2}", .{b});
    }
    try stdout.print("\n", .{});
    try stdout.print("std.crypto.random: обёртка над getrandom/urandom.\n", .{});
    try stdout.print("Криптографически стойкий, непредсказуемый.\n\n", .{});

    // 5. Распределение
    try stdout.print("--- 5. Проверка равномерности ---\n", .{});
    var rng3 = Xorshift64.init(99999);
    var buckets: [10]u32 = .{0} ** 10;
    for (0..100_000) |_| {
        const val: usize = @intCast(rng3.next() % 10);
        buckets[val] += 1;
    }
    for (buckets, 0..) |count, i| {
        try stdout.print("  {d}: {d:5} ", .{ i, count });
        for (0..count / 200) |_| {
            try stdout.print("#", .{});
        }
        try stdout.print("\n", .{});
    }

    try stdout.print("\n--- Итог ---\n", .{});
    try stdout.print("PRNG: быстр, детерминирован, НЕ для криптографии\n", .{});
    try stdout.print("CSPRNG: непредсказуем, для ключей и токенов\n", .{});
    try stdout.print("Zig: std.crypto.random (CSPRNG), ручные PRNG\n", .{});
}
