# Случайные числа: PRNG vs CSPRNG
# Запуск: nim r random_demo.nim

import std/random
import std/sysrand
import std/strutils
import std/strformat

proc main() =
  echo "=== Nim: случайные числа ===\n"

  # 1. std/random без инициализации — детерминированная последовательность
  echo "--- 1. Без randomize() ---"
  echo "Без вызова randomize() генератор стартует с захардкоженного состояния."
  echo "Каждый запуск программы даёт одни и те же числа:"
  stdout.write "  "
  for _ in 0 ..< 10:
    stdout.write $rand(99) & " "
  echo "\n"

  # 2. randomize() — посев от системного времени
  echo "--- 2. randomize() от времени ---"
  randomize()
  stdout.write "10 чисел [0, 99]: "
  for _ in 0 ..< 10:
    stdout.write $rand(99) & " "
  echo ""
  echo "Каждый запуск — разный результат.\n"

  # 3. Фиксированный seed — воспроизводимость
  echo "--- 3. Фиксированный seed ---"
  randomize(42)
  stdout.write "seed=42, первый прогон:  "
  for _ in 0 ..< 10:
    stdout.write $rand(99) & " "
  echo ""

  randomize(42)
  stdout.write "seed=42, второй прогон: "
  for _ in 0 ..< 10:
    stdout.write $rand(99) & " "
  echo ""
  echo "Последовательности идентичны. PRNG детерминирован.\n"

  # 4. initRand — независимые генераторы
  echo "--- 4. Независимые генераторы (initRand) ---"
  var rng1 = initRand(12345)
  var rng2 = initRand(12345)
  var rng3 = initRand(99999)
  stdout.write "rng(seed=12345) A: "
  for _ in 0 ..< 5:
    stdout.write $rng1.rand(99) & " "
  echo ""
  stdout.write "rng(seed=12345) B: "
  for _ in 0 ..< 5:
    stdout.write $rng2.rand(99) & " "
  echo ""
  stdout.write "rng(seed=99999):   "
  for _ in 0 ..< 5:
    stdout.write $rng3.rand(99) & " "
  echo ""
  echo "Одинаковый seed → одинаковая последовательность."
  echo "Не зависят от глобального состояния.\n"

  # 5. Типы случайных значений
  echo "--- 5. Разные типы ---"
  randomize()
  echo &"  rand(100)       = {rand(100)}"
  echo &"  rand(1.0)       = {rand(1.0):.6f}"
  echo &"  rand(10..20)    = {rand(10..20)}"
  echo &"  rand(1.0..9.5)  = {rand(1.0..9.5):.4f}"
  echo &"  rand(bool)      = {rand(bool)}"
  echo ""

  # 6. Shuffle и sample
  echo "--- 6. Shuffle и sample ---"
  var cards = @["2♠", "7♥", "K♦", "A♣", "10♠"]
  echo "  До shuffle:  ", cards
  shuffle(cards)
  echo "  После shuffle: ", cards
  echo "  sample:       ", sample(cards)
  echo ""

  # 7. CSPRNG — std/sysrand
  echo "--- 7. CSPRNG (std/sysrand) ---"
  let bytes = urandom(16)
  stdout.write "  16 байт от ОС: "
  for b in bytes:
    stdout.write b.toHex().toLowerAscii()
  echo ""
  echo "  Источник: getrandom (Linux), SecRandomCopyBytes (macOS),"
  echo "            BCryptGenRandom (Windows)."
  echo "  Криптографически стойкий, непредсказуемый.\n"

  # 8. Сравнение: CSPRNG vs PRNG
  echo "--- 8. CSPRNG vs PRNG: два вызова urandom ---"
  let a = urandom(8)
  let b = urandom(8)
  stdout.write "  Первый:  "
  for x in a: stdout.write x.toHex().toLowerAscii()
  echo ""
  stdout.write "  Второй: "
  for x in b: stdout.write x.toHex().toLowerAscii()
  echo ""
  echo "  Всегда различны, даже между запусками.\n"

  # 9. Проверка равномерности
  echo "--- 9. Проверка равномерности (100 000 бросков) ---"
  var rng = initRand(7777)
  var buckets: array[10, int]
  for _ in 0 ..< 100_000:
    buckets[rng.rand(9)] += 1
  for i in 0 ..< 10:
    let bar = "#".repeat(buckets[i] div 200)
    echo &"  {i}: {buckets[i]:5} {bar}"

  echo "\n--- Итог ---"
  echo "std/random:  xoroshiro128+, быстр, НЕ для криптографии"
  echo "std/sysrand: обёртка над ОС, для ключей и токенов"
  echo "randomize(): обязателен, иначе последовательность одинакова"

main()
