# Stochasticke simulacne metody
# Radoslav Harman, KAMS FMFI UK
# Cvicenie 1 (Testy RNG) - strucny zaznam prikazov pre R

# Du: pozrite si ako vyzeraju bezne dostupne hardwarove generatory nahodnych cisel, napr:
# https://www.amazon.com/TrueRNGpro-Hardware-Random-Number-Generator/dp/B01JTJ6D0S/
# https://www.amazon.com/TrueRNG-V3-Hardware-Random-Generator/dp/B01KR2JHTA/
# Vyhladajte si akou rychlostou generuju.

# Du : Pozrite si tiez stranky www.random.org a https://qrng.anu.edu.au/

RNGkind()
.Random.seed
set.seed(123456789)         # Velmi dolezite pre replikaciu a debugging. Nepouzivat v cykle!

# Du: Precitajte si podrobnejsie https://stat.ethz.ch/R-manual/R-patched/library/base/html/Random.html

lcg <- function(a, c, m, x0, n) {
   # Elementarna implementacia LCG prveho radu
   # a, c, m, x0 ... multiplikator, inkrement, modulus, seed
   # n ... dlzka postupnosti pseudonahodnych cisiel
   
   x <- c(x0, rep(0, n - 1))
   for (i in 2:n) x[i] <- (a * x[i - 1] + c) %% m
   
   return(x)
}

x0 <- lcg(13, 0, 32, 17, 33); print(x0)                    # Len na "overenie" teoretickej vety z prednasky
x0 <- lcg(17, 17, 32, 17, 33); print(x0)

x1 <- lcg(17, 123456, 2^31 - 1, 123456, 500*2^12) / (2^31 - 1) # LCG Generator s "nahodne" zvolenymi parametrami
x2 <- lcg(7^5, 0, 2^31 - 1, 123456, 500*2^12) / (2^31 - 1)     # Generator "Minimal standard"
x3 <- runif(500 * 2^12)                                        # Generator Rka


# Vizualna inspekcia histogramu
par(mfrow = c(3, 1))
hist(x1, ylim = c(0.95, 1.05), freq = FALSE); lines(c(0, 1), c(1, 1), col = "red", lty = "dashed")
hist(x2, ylim = c(0.95, 1.05), freq = FALSE); lines(c(0, 1), c(1, 1), col = "red", lty = "dashed")
hist(x3, ylim = c(0.95, 1.05), freq = FALSE); lines(c(0, 1), c(1, 1), col = "red", lty = "dashed")

# Vizualna inspekcia realizacii dvojrozmernych subvektrov 
windows(); plot(x1[1:(250 * 2^8)], x1[(250 * 2^8 + 1):(500 * 2^8)], pch = 19, cex = 0.1)
windows(); plot(x2[1:(250 * 2^8)], x2[(250 * 2^8 + 1):(500 * 2^8)], pch = 19, cex = 0.1)
windows(); plot(x3[1:(250 * 2^8)], x3[(250 * 2^8 + 1):(500 * 2^8)], pch = 19, cex = 0.1)

# Kolmogorov-Smirnov test
ks.test(x1, punif)
ks.test(x2, punif)
ks.test(x3, punif)

# Jednorozmerny test dobrej zhody
chisq.test(table(ceiling(1000 * x1)), p = rep(1/1000, 1000))
chisq.test(table(ceiling(1000 * x2)), p = rep(1/1000, 1000))
chisq.test(table(ceiling(1000 * x3)), p = rep(1/1000, 1000))

poker.test <- function(x) {
   # Takzvany trojrozmerny pokrovy test
   # (Specialny test dobrej zhody s rovnomernym rozdelenim na [0,1]^m, m=3.)
   # x ... vektor testovanych cisel v intervale [0,1]
   
   d <- length(x); d <- d - (d %% 3)
   x <- x[1:d]; n <- d/3
   
   N <- c(0, 0, 0)
   for (l in 1:n) {
      s <- length(unique(floor(3 * x[(3 * l - 2):(3 * l)])))
      N[s] <- N[s] + 1
   }
   
   # V tomto teste mame k=3 oblasti a nasledovne p su p_1,p_2,p_3 z prednasky
   return(chisq.test(N, p = c(3/27, 18/27, 6/27)))
}

# Trojrozmerny pokrovy test
poker.test(x1) 
poker.test(x2)
poker.test(x3)


serial.test <- function(x, k) {
   # Test nahodnosti pomocou serialnej korelacie
   # x ... testovany vektor
   # k ... "posun"
   
   n <- length(x)
   rk <- cor(x[(k + 1):n], x[1:(n - k)])
   print(paste("Koeficient serialnej korelacie:", rk), quote = FALSE)
   pval <- 2 * (1 - pnorm(abs(sqrt(n) * rk)))
   
   print(paste("p-hodnota testu:", pval), quote = FALSE)
}

# Test serialnej korelacie
serial.test(x1, 2) 
serial.test(x2, 2)
serial.test(x3, 2)


extreme.test <- function(x) {
   # Test nezavislosti pomocou extremalnych bodov
   #  x ... testovany vektor cisel
   
   n <- length(x)
   y <- 0
   for (i in 2:(n - 1)) {
      ismax <- (x[i - 1] < x[i]) & (x[i] > x[i + 1])
      ismin <- (x[i - 1] > x[i]) & (x[i] < x[i + 1])
      if (ismax | ismin) y <- y + 1
   }
   print(paste("Pocet extremalnych bodov v postupnosti:", y), quote = FALSE)
   
   # ak je x generovane z idealneho RNG, tak y ma asymptoticky normalne rozdelenie
   # s nasledovnou strednou hodnotou a disperziou
   Ey <- 2 * (n - 2) / 3; Dy <- (16 * n - 29) / 90
   
   pval <- 2 * (1 - pnorm(abs((y - Ey) / sqrt(Dy))))
   print(paste("p-hodnota testu:", pval), quote = FALSE)
}

# Test extremalnych hodnot
extreme.test(x1) 
extreme.test(x2)
extreme.test(x3)


birthdays.test <- function(x) {
   # Specifikacia testu "birthday spacings"
   # x ... testovany vektor cisel z [0,1]
   # x v tejto implementacii musi mat dlzku 500*2^12
   # Pocet "rokov" je N=500, "dni" v roku je d=2^32, "narodenin/ludi" v roku je k=2^12
   # Za kazdy rok sa vypocita vektor odstupov (v dnoch) medzi po sebe nasledujucimi narodeninami
   # Potom sa urci hodnota kolko tychto odstupov sa zopakovalo (viac ako raz)
   # Pre korektny generator je rozdelenie tychto hodnot priblizne Po(k^3/(4*d))=Po(4)
   
   N <- 500; k <- 2^12; d <- 2^32
   res <- rep(0, N)
   for (i in 1:N) {
      z <- sort(ceiling(d * x[((i - 1) * k + 1):(i * k)]))
      sp <- table(z[2:k] - z[1:(k - 1)])
      res[i] <- length(sp[sp > 1])
   }
   
   nn <- rep(0, 8)
   for (j in 0:7) nn[j + 1] <- length(res[res == j])
   nn <- c(nn, length(res[res > 7]))
   pp <- dpois(0:7, lambda = 4); pp <- c(pp, 1 - sum(pp))
   
   return(chisq.test(nn, p = pp))
}

# Birthday spacings test
birthdays.test(x1)
birthdays.test(x2)
birthdays.test(x3)

# Ovela dokladnejsie testovanie je mozno vykonat pomocou balikov:
# https://en.wikipedia.org/wiki/Diehard_tests
# https://en.wikipedia.org/wiki/TestU01
