# =========================================================================
# ETS KOMPUTASI STATISTIKA - SEMESTER GENAP 2025/2026
# Soal 2 - Dataset: airquality
# -------------------------------------------------------------------------
# Petunjuk umum:
#  1. Kerjakan menggunakan bahasa pemrograman R.
#  2. Setiap nomor dikerjakan dalam file R script TERPISAH.
#  3. Format penamaan file: Nomor_NRP_Nama.R
# -------------------------------------------------------------------------
# Nama   :  shella yuastari____________________________________
# NRP    :  5003251193____________________________________
# Kelas  :  statistika D____________________________________
# ========================================================================

# -----------------------------------------------------------------------------
# Persiapan data
# ------------------------------------------------------------------------
data <- na.omit(airquality[, c("Ozone", "Solar.R", "Wind", "Temp")])

# dim(data)
# 111 x 4


# ========================================================================
# (a) mat_mult() - perkalian matriks manual TANPA operator %*%
# ========================================================================
# A (m x n), B (n x p) -> C (m x p)

mat_mult <- function(A, B) {

  # Mengecek apakah matriks bisa dikalikan
  if(ncol(A) != nrow(B)){
    stop("Jumlah kolom A harus sama dengan jumlah baris B")
  }

  # Ukuran matriks
  m <- nrow(A)
  n <- ncol(A)
  p <- ncol(B)

  # Matriks hasil
  C <- matrix(0, nrow = m, ncol = p)

  # Perkalian matriks manual
  for(i in 1:m){

    for(j in 1:p){

      jumlah <- 0

      for(k in 1:n){

        jumlah <- jumlah + A[i,k] * B[k,j]

      }

      C[i,j] <- jumlah

    }

  }

  return(C)

}

# -------------------------------------------------------------------------
# Validasi fungsi
# -------------------------------------------------------------------------

a <- matrix(c(1,2,3,4), nrow=2)
b <- matrix(c(5,6,7,8), nrow=2)

c_std    <- a %*% b
c_manual <- mat_mult(a,b)

{
  cat("Hasil standar (a %*% b):\n")
  print(c_std)

  cat("\nHasil mat_mult (manual):\n")
  print(c_manual)

  cat("\nSelisih:\n")
  print(c_std == c_manual)
}
Hasil standar (a %*% b):
     [,1] [,2]
[1,]   23   31
[2,]   34   46

Hasil mat_mult (manual):
     [,1] [,2]
[1,]   23   31
[2,]   34   46

Selisih:
     [,1] [,2]
[1,] TRUE TRUE
[2,] TRUE TRUE
# ========================================================================
# (b) corr_matrix(X) - matriks korelasi Pearson TANPA fungsi cor()
# ========================================================================
# Gunakan mat_mult untuk SEMUA perkalian matriks

corr_matrix <- function(X) {

  # Mengubah menjadi matriks
  X <- as.matrix(X)

  # Ukuran data
  n <- nrow(X)
  p <- ncol(X)

  # -----------------------------------------------------------------------
  # (i) Hitung rata-rata kolom
  # X_bar = (1/n) * X^T %*% 1_n
  # -----------------------------------------------------------------------

  one_n <- matrix(1, nrow = n, ncol = 1)

  X_bar <- (1/n) * mat_mult(t(X), one_n)

  # ---------------------------------------------------------------------------
  # (ii) Hitung matriks kovarians
  # X_c = X - 1_n %*% X_bar^T
  # S   = (1/(n-1)) * X_c^T %*% X_c
  # -----------------------------------------------------------------------

  mean_matrix <- mat_mult(one_n, t(X_bar))

  X_c <- X - mean_matrix

  S <- (1/(n-1)) * mat_mult(t(X_c), X_c)

  # -----------------------------------------------------------------------
  # (iii) Hitung matriks korelasi
  # R = D^{-1/2} S D^{-1/2}
  # -----------------------------------------------------------------------

  D <- diag(diag(S))

  D_inv_half <- diag(1 / sqrt(diag(D)))

  temp <- mat_mult(D_inv_half, S)

  R <- mat_mult(temp, D_inv_half)

  return(R)

}

# -------------------------------------------------------------------------
# Terapkan ke data airquality
# -------------------------------------------------------------------------

R_hat <- corr_matrix(data)

cat("\nMatriks Korelasi Pearson:\n")

Matriks Korelasi Pearson:
print(round(R_hat, 6))
          [,1]      [,2]      [,3]      [,4]
[1,]  1.000000  0.348342 -0.612497  0.698541
[2,]  0.348342  1.000000 -0.127183  0.294088
[3,] -0.612497 -0.127183  1.000000 -0.497190
[4,]  0.698541  0.294088 -0.497190  1.000000
# -------------------------------------------------------------------------
# Validasi jawaban
# -------------------------------------------------------------------------

cat("\nValidasi dengan fungsi cor():\n")

Validasi dengan fungsi cor():
print(round(R_hat,10) == round(cor(data),10))
        Ozone Solar.R Wind Temp
Ozone    TRUE    TRUE TRUE TRUE
Solar.R  TRUE    TRUE TRUE TRUE
Wind     TRUE    TRUE TRUE TRUE
Temp     TRUE    TRUE TRUE TRUE
# ========================================================================
# (c) var_test_one(x, sigma0_sq, alpha)
# ========================================================================
# H0: sigma^2 = 1000  vs  H1: sigma^2 > 1000,  alpha = 0.05
# TANPA fungsi built-in: mean(), sum(), var(), sd(), var.test()
# (Boleh: dchisq, qchisq, length, dst.)
#
# Langkah:
#  (i)  Statistik: chi^2 = (n-1) * s^2 / sigma0^2,
#        dengan s^2 = sum((x_i - xbar)^2) / (n - 1)
#  (ii) p-value via trapezoidal pada distribusi chi-square (sisi kanan):
#        p-value = 1 - integral_0^{chi2_hit} f_{chi^2_{n-1}}(x) dx
#        Aproksimasi:
#          integral ~ (h/2) * [f(0) + 2*sum_{i=1}^{N-1} f(i*h) + f(chi2_hit)]
#        dengan h = chi2_hit / N, N = 10000, df = n - 1.
#  (iii) Keputusan + interpretasi dalam konteks variasi kadar ozon harian.

var_test_one <- function(x, sigma0_sq, alpha) {
  # --- TODO (i): hitung n, xbar, dan s^2 ---
  n <- length(x)
  
  # Hitung sum(x) secara manual untuk mencari xbar
  total_x <- 0
  for (i in 1:n) {
    total_x <- total_x + x[i]
  }
  xbar <- total_x / n
  
  # Hitung sum((x_i - xbar)^2) secara manual untuk mencari s^2
  total_ss <- 0
  for (i in 1:n) {
    total_ss <- total_ss + (x[i] - xbar)^2
  }
  s_sq <- total_ss / (n - 1)
  
  # --- TODO (i): hitung chi2_hit dan df ---
  df <- n - 1
  chi2_hit <- (n - 1) * s_sq / sigma0_sq
  
  # --- TODO (ii): aproksimasi p-value via trapezoidal (N = 10000) ---
  N <- 10000
  h <- chi2_hit / N
  
  # f(0) dan f(chi2_hit) menggunakan dchisq
  f_0 <- dchisq(0, df)
  f_end <- dchisq(chi2_hit, df)
  
  # Menghitung sum_{i=1}^{N-1} f(i*h) secara manual
  sum_interior <- 0
  for (i in 1:(N - 1)) {
    x_i <- i * h
    sum_interior <- sum_interior + dchisq(x_i, df)
  }
  
  # Menghitung nilai integral dari 0 sampai chi2_hit
  integral <- (h / 2) * (f_0 + 2 * sum_interior + f_end)
  
  # p-value untuk uji pihak kanan (H1: sigma^2 > 1000)
  p_value <- 1 - integral
  
  # --- TODO (iii): hitung chi2_kritis = qchisq(1 - alpha, df) dan keputusan ---
  chi2_kritis <- qchisq(1 - alpha, df)
  
  if (chi2_hit > chi2_kritis) {
    decision <- "Tolak H0"
  } else {
    decision <- "Gagal Tolak H0"
  }
  
  # Kembalikan list: statistik uji, p-value, keputusan
  results <- list(statistic = chi2_hit, p_value = p_value, decision = decision)
  return(results)
}

# Terapkan ke Ozone
x <- data$Ozone
test_results <- var_test_one(x, sigma0_sq = 1000, alpha = 0.05)
print(test_results)
$statistic
[1] 121.8019

$p_value
[1] 0.207976

$decision
[1] "Gagal Tolak H0"
cat("\n=================== INTERPRETASI ===================\n")

=================== INTERPRETASI ===================
cat("Berdasarkan hasil uji hipotesis dengan alpha = 5%:\n")
Berdasarkan hasil uji hipotesis dengan alpha = 5%:
cat("Keputusan      :", test_results$decision, "\n")
Keputusan      : Gagal Tolak H0 
cat("Nilai P-Value  :", round(test_results$p_value, 5), "\n")
Nilai P-Value  : 0.20798 
cat("Kesimpulan     : Karena P-Value < alpha (atau Chi-Square Hitung > Chi-Square Kritis),\n")
Kesimpulan     : Karena P-Value < alpha (atau Chi-Square Hitung > Chi-Square Kritis),
cat("                 maka terdapat cukup bukti untuk menyatakan bahwa variasi (varians)\n")
                 maka terdapat cukup bukti untuk menyatakan bahwa variasi (varians)
cat("                 kadar ozon harian di udara perkotaan tersebut sudah secara signifikan\n")
                 kadar ozon harian di udara perkotaan tersebut sudah secara signifikan
cat("                 lebih besar dari 1000.\n")
                 lebih besar dari 1000.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoNCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgRVRTIEtPTVBVVEFTSSBTVEFUSVNUSUtBIC0gU0VNRVNURVIgR0VOQVAgMjAyNS8yMDI2DQojIFNvYWwgMiAtIERhdGFzZXQ6IGFpcnF1YWxpdHkNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBQZXR1bmp1ayB1bXVtOg0KIyAgMS4gS2VyamFrYW4gbWVuZ2d1bmFrYW4gYmFoYXNhIHBlbXJvZ3JhbWFuIFIuDQojICAyLiBTZXRpYXAgbm9tb3IgZGlrZXJqYWthbiBkYWxhbSBmaWxlIFIgc2NyaXB0IFRFUlBJU0FILg0KIyAgMy4gRm9ybWF0IHBlbmFtYWFuIGZpbGU6IE5vbW9yX05SUF9OYW1hLlINCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBOYW1hICAgOiAgc2hlbGxhIHl1YXN0YXJpX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQojIE5SUCAgICA6ICA1MDAzMjUxMTkzX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fDQojIEtlbGFzICA6ICBzdGF0aXN0aWthIERfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18NCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgUGVyc2lhcGFuIGRhdGENCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpkYXRhIDwtIG5hLm9taXQoYWlycXVhbGl0eVssIGMoIk96b25lIiwgIlNvbGFyLlIiLCAiV2luZCIsICJUZW1wIildKQ0KDQojIGRpbShkYXRhKQ0KIyAxMTEgeCA0DQoNCg0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgKGEpIG1hdF9tdWx0KCkgLSBwZXJrYWxpYW4gbWF0cmlrcyBtYW51YWwgVEFOUEEgb3BlcmF0b3IgJSolDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBBIChtIHggbiksIEIgKG4geCBwKSAtPiBDIChtIHggcCkNCg0KbWF0X211bHQgPC0gZnVuY3Rpb24oQSwgQikgew0KDQogICMgTWVuZ2VjZWsgYXBha2FoIG1hdHJpa3MgYmlzYSBkaWthbGlrYW4NCiAgaWYobmNvbChBKSAhPSBucm93KEIpKXsNCiAgICBzdG9wKCJKdW1sYWgga29sb20gQSBoYXJ1cyBzYW1hIGRlbmdhbiBqdW1sYWggYmFyaXMgQiIpDQogIH0NCg0KICAjIFVrdXJhbiBtYXRyaWtzDQogIG0gPC0gbnJvdyhBKQ0KICBuIDwtIG5jb2woQSkNCiAgcCA8LSBuY29sKEIpDQoNCiAgIyBNYXRyaWtzIGhhc2lsDQogIEMgPC0gbWF0cml4KDAsIG5yb3cgPSBtLCBuY29sID0gcCkNCg0KICAjIFBlcmthbGlhbiBtYXRyaWtzIG1hbnVhbA0KICBmb3IoaSBpbiAxOm0pew0KDQogICAgZm9yKGogaW4gMTpwKXsNCg0KICAgICAganVtbGFoIDwtIDANCg0KICAgICAgZm9yKGsgaW4gMTpuKXsNCg0KICAgICAgICBqdW1sYWggPC0ganVtbGFoICsgQVtpLGtdICogQltrLGpdDQoNCiAgICAgIH0NCg0KICAgICAgQ1tpLGpdIDwtIGp1bWxhaA0KDQogICAgfQ0KDQogIH0NCg0KICByZXR1cm4oQykNCg0KfQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgVmFsaWRhc2kgZnVuZ3NpDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KYSA8LSBtYXRyaXgoYygxLDIsMyw0KSwgbnJvdz0yKQ0KYiA8LSBtYXRyaXgoYyg1LDYsNyw4KSwgbnJvdz0yKQ0KDQpjX3N0ZCAgICA8LSBhICUqJSBiDQpjX21hbnVhbCA8LSBtYXRfbXVsdChhLGIpDQoNCnsNCiAgY2F0KCJIYXNpbCBzdGFuZGFyIChhICUqJSBiKTpcbiIpDQogIHByaW50KGNfc3RkKQ0KDQogIGNhdCgiXG5IYXNpbCBtYXRfbXVsdCAobWFudWFsKTpcbiIpDQogIHByaW50KGNfbWFudWFsKQ0KDQogIGNhdCgiXG5TZWxpc2loOlxuIikNCiAgcHJpbnQoY19zdGQgPT0gY19tYW51YWwpDQp9DQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAoYikgY29ycl9tYXRyaXgoWCkgLSBtYXRyaWtzIGtvcmVsYXNpIFBlYXJzb24gVEFOUEEgZnVuZ3NpIGNvcigpDQojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyBHdW5ha2FuIG1hdF9tdWx0IHVudHVrIFNFTVVBIHBlcmthbGlhbiBtYXRyaWtzDQoNCmNvcnJfbWF0cml4IDwtIGZ1bmN0aW9uKFgpIHsNCg0KICAjIE1lbmd1YmFoIG1lbmphZGkgbWF0cmlrcw0KICBYIDwtIGFzLm1hdHJpeChYKQ0KDQogICMgVWt1cmFuIGRhdGENCiAgbiA8LSBucm93KFgpDQogIHAgPC0gbmNvbChYKQ0KDQogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiAgIyAoaSkgSGl0dW5nIHJhdGEtcmF0YSBrb2xvbQ0KICAjIFhfYmFyID0gKDEvbikgKiBYXlQgJSolIDFfbg0KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiAgb25lX24gPC0gbWF0cml4KDEsIG5yb3cgPSBuLCBuY29sID0gMSkNCg0KICBYX2JhciA8LSAoMS9uKSAqIG1hdF9tdWx0KHQoWCksIG9uZV9uKQ0KDQogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQogICMgKGlpKSBIaXR1bmcgbWF0cmlrcyBrb3ZhcmlhbnMNCiAgIyBYX2MgPSBYIC0gMV9uICUqJSBYX2Jhcl5UDQogICMgUyAgID0gKDEvKG4tMSkpICogWF9jXlQgJSolIFhfYw0KICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiAgbWVhbl9tYXRyaXggPC0gbWF0X211bHQob25lX24sIHQoWF9iYXIpKQ0KDQogIFhfYyA8LSBYIC0gbWVhbl9tYXRyaXgNCg0KICBTIDwtICgxLyhuLTEpKSAqIG1hdF9tdWx0KHQoWF9jKSwgWF9jKQ0KDQogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiAgIyAoaWlpKSBIaXR1bmcgbWF0cmlrcyBrb3JlbGFzaQ0KICAjIFIgPSBEXnstMS8yfSBTIEReey0xLzJ9DQogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KICBEIDwtIGRpYWcoZGlhZyhTKSkNCg0KICBEX2ludl9oYWxmIDwtIGRpYWcoMSAvIHNxcnQoZGlhZyhEKSkpDQoNCiAgdGVtcCA8LSBtYXRfbXVsdChEX2ludl9oYWxmLCBTKQ0KDQogIFIgPC0gbWF0X211bHQodGVtcCwgRF9pbnZfaGFsZikNCg0KICByZXR1cm4oUikNCg0KfQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgVGVyYXBrYW4ga2UgZGF0YSBhaXJxdWFsaXR5DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KUl9oYXQgPC0gY29ycl9tYXRyaXgoZGF0YSkNCg0KY2F0KCJcbk1hdHJpa3MgS29yZWxhc2kgUGVhcnNvbjpcbiIpDQpwcmludChyb3VuZChSX2hhdCwgNikpDQoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBWYWxpZGFzaSBqYXdhYmFuDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KY2F0KCJcblZhbGlkYXNpIGRlbmdhbiBmdW5nc2kgY29yKCk6XG4iKQ0KcHJpbnQocm91bmQoUl9oYXQsMTApID09IHJvdW5kKGNvcihkYXRhKSwxMCkpDQpgYGANCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgKGMpIHZhcl90ZXN0X29uZSh4LCBzaWdtYTBfc3EsIGFscGhhKQ0KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCiMgSDA6IHNpZ21hXjIgPSAxMDAwICB2cyAgSDE6IHNpZ21hXjIgPiAxMDAwLCAgYWxwaGEgPSAwLjA1DQojIFRBTlBBIGZ1bmdzaSBidWlsdC1pbjogbWVhbigpLCBzdW0oKSwgdmFyKCksIHNkKCksIHZhci50ZXN0KCkNCiMgKEJvbGVoOiBkY2hpc3EsIHFjaGlzcSwgbGVuZ3RoLCBkc3QuKQ0KIw0KIyBMYW5na2FoOg0KIyAgKGkpICBTdGF0aXN0aWs6IGNoaV4yID0gKG4tMSkgKiBzXjIgLyBzaWdtYTBeMiwNCiMgICAgICAgIGRlbmdhbiBzXjIgPSBzdW0oKHhfaSAtIHhiYXIpXjIpIC8gKG4gLSAxKQ0KIyAgKGlpKSBwLXZhbHVlIHZpYSB0cmFwZXpvaWRhbCBwYWRhIGRpc3RyaWJ1c2kgY2hpLXNxdWFyZSAoc2lzaSBrYW5hbik6DQojICAgICAgICBwLXZhbHVlID0gMSAtIGludGVncmFsXzBee2NoaTJfaGl0fSBmX3tjaGleMl97bi0xfX0oeCkgZHgNCiMgICAgICAgIEFwcm9rc2ltYXNpOg0KIyAgICAgICAgICBpbnRlZ3JhbCB+IChoLzIpICogW2YoMCkgKyAyKnN1bV97aT0xfV57Ti0xfSBmKGkqaCkgKyBmKGNoaTJfaGl0KV0NCiMgICAgICAgIGRlbmdhbiBoID0gY2hpMl9oaXQgLyBOLCBOID0gMTAwMDAsIGRmID0gbiAtIDEuDQojICAoaWlpKSBLZXB1dHVzYW4gKyBpbnRlcnByZXRhc2kgZGFsYW0ga29udGVrcyB2YXJpYXNpIGthZGFyIG96b24gaGFyaWFuLg0KDQp2YXJfdGVzdF9vbmUgPC0gZnVuY3Rpb24oeCwgc2lnbWEwX3NxLCBhbHBoYSkgew0KICAjIC0tLSBUT0RPIChpKTogaGl0dW5nIG4sIHhiYXIsIGRhbiBzXjIgLS0tDQogIG4gPC0gbGVuZ3RoKHgpDQogIA0KICAjIEhpdHVuZyBzdW0oeCkgc2VjYXJhIG1hbnVhbCB1bnR1ayBtZW5jYXJpIHhiYXINCiAgdG90YWxfeCA8LSAwDQogIGZvciAoaSBpbiAxOm4pIHsNCiAgICB0b3RhbF94IDwtIHRvdGFsX3ggKyB4W2ldDQogIH0NCiAgeGJhciA8LSB0b3RhbF94IC8gbg0KICANCiAgIyBIaXR1bmcgc3VtKCh4X2kgLSB4YmFyKV4yKSBzZWNhcmEgbWFudWFsIHVudHVrIG1lbmNhcmkgc14yDQogIHRvdGFsX3NzIDwtIDANCiAgZm9yIChpIGluIDE6bikgew0KICAgIHRvdGFsX3NzIDwtIHRvdGFsX3NzICsgKHhbaV0gLSB4YmFyKV4yDQogIH0NCiAgc19zcSA8LSB0b3RhbF9zcyAvIChuIC0gMSkNCiAgDQogICMgLS0tIFRPRE8gKGkpOiBoaXR1bmcgY2hpMl9oaXQgZGFuIGRmIC0tLQ0KICBkZiA8LSBuIC0gMQ0KICBjaGkyX2hpdCA8LSAobiAtIDEpICogc19zcSAvIHNpZ21hMF9zcQ0KICANCiAgIyAtLS0gVE9ETyAoaWkpOiBhcHJva3NpbWFzaSBwLXZhbHVlIHZpYSB0cmFwZXpvaWRhbCAoTiA9IDEwMDAwKSAtLS0NCiAgTiA8LSAxMDAwMA0KICBoIDwtIGNoaTJfaGl0IC8gTg0KICANCiAgIyBmKDApIGRhbiBmKGNoaTJfaGl0KSBtZW5nZ3VuYWthbiBkY2hpc3ENCiAgZl8wIDwtIGRjaGlzcSgwLCBkZikNCiAgZl9lbmQgPC0gZGNoaXNxKGNoaTJfaGl0LCBkZikNCiAgDQogICMgTWVuZ2hpdHVuZyBzdW1fe2k9MX1ee04tMX0gZihpKmgpIHNlY2FyYSBtYW51YWwNCiAgc3VtX2ludGVyaW9yIDwtIDANCiAgZm9yIChpIGluIDE6KE4gLSAxKSkgew0KICAgIHhfaSA8LSBpICogaA0KICAgIHN1bV9pbnRlcmlvciA8LSBzdW1faW50ZXJpb3IgKyBkY2hpc3EoeF9pLCBkZikNCiAgfQ0KICANCiAgIyBNZW5naGl0dW5nIG5pbGFpIGludGVncmFsIGRhcmkgMCBzYW1wYWkgY2hpMl9oaXQNCiAgaW50ZWdyYWwgPC0gKGggLyAyKSAqIChmXzAgKyAyICogc3VtX2ludGVyaW9yICsgZl9lbmQpDQogIA0KICAjIHAtdmFsdWUgdW50dWsgdWppIHBpaGFrIGthbmFuIChIMTogc2lnbWFeMiA+IDEwMDApDQogIHBfdmFsdWUgPC0gMSAtIGludGVncmFsDQogIA0KICAjIC0tLSBUT0RPIChpaWkpOiBoaXR1bmcgY2hpMl9rcml0aXMgPSBxY2hpc3EoMSAtIGFscGhhLCBkZikgZGFuIGtlcHV0dXNhbiAtLS0NCiAgY2hpMl9rcml0aXMgPC0gcWNoaXNxKDEgLSBhbHBoYSwgZGYpDQogIA0KICBpZiAoY2hpMl9oaXQgPiBjaGkyX2tyaXRpcykgew0KICAgIGRlY2lzaW9uIDwtICJUb2xhayBIMCINCiAgfSBlbHNlIHsNCiAgICBkZWNpc2lvbiA8LSAiR2FnYWwgVG9sYWsgSDAiDQogIH0NCiAgDQogICMgS2VtYmFsaWthbiBsaXN0OiBzdGF0aXN0aWsgdWppLCBwLXZhbHVlLCBrZXB1dHVzYW4NCiAgcmVzdWx0cyA8LSBsaXN0KHN0YXRpc3RpYyA9IGNoaTJfaGl0LCBwX3ZhbHVlID0gcF92YWx1ZSwgZGVjaXNpb24gPSBkZWNpc2lvbikNCiAgcmV0dXJuKHJlc3VsdHMpDQp9DQoNCiMgVGVyYXBrYW4ga2UgT3pvbmUNCnggPC0gZGF0YSRPem9uZQ0KdGVzdF9yZXN1bHRzIDwtIHZhcl90ZXN0X29uZSh4LCBzaWdtYTBfc3EgPSAxMDAwLCBhbHBoYSA9IDAuMDUpDQpwcmludCh0ZXN0X3Jlc3VsdHMpDQoNCg0KY2F0KCJcbj09PT09PT09PT09PT09PT09PT0gSU5URVJQUkVUQVNJID09PT09PT09PT09PT09PT09PT1cbiIpDQpjYXQoIkJlcmRhc2Fya2FuIGhhc2lsIHVqaSBoaXBvdGVzaXMgZGVuZ2FuIGFscGhhID0gNSU6XG4iKQ0KY2F0KCJLZXB1dHVzYW4gICAgICA6IiwgdGVzdF9yZXN1bHRzJGRlY2lzaW9uLCAiXG4iKQ0KY2F0KCJOaWxhaSBQLVZhbHVlICA6Iiwgcm91bmQodGVzdF9yZXN1bHRzJHBfdmFsdWUsIDUpLCAiXG4iKQ0KY2F0KCJLZXNpbXB1bGFuICAgICA6IEthcmVuYSBQLVZhbHVlIDwgYWxwaGEgKGF0YXUgQ2hpLVNxdWFyZSBIaXR1bmcgPiBDaGktU3F1YXJlIEtyaXRpcyksXG4iKQ0KY2F0KCIgICAgICAgICAgICAgICAgIG1ha2EgdGVyZGFwYXQgY3VrdXAgYnVrdGkgdW50dWsgbWVueWF0YWthbiBiYWh3YSB2YXJpYXNpICh2YXJpYW5zKVxuIikNCmNhdCgiICAgICAgICAgICAgICAgICBrYWRhciBvem9uIGhhcmlhbiBkaSB1ZGFyYSBwZXJrb3RhYW4gdGVyc2VidXQgc3VkYWggc2VjYXJhIHNpZ25pZmlrYW5cbiIpDQpjYXQoIiAgICAgICAgICAgICAgICAgbGViaWggYmVzYXIgZGFyaSAxMDAwLlxuIikNCmBgYA0KDQoNCg0KDQoNCg==