Central Tendency

Assignment ~ Week 6

Logo

1 Cental Tendency

Central Tendency adalah ukuran statistik yang merepresentasikan nilai tipikal atau sentral dari suatu kumpulan data. Ukuran ini bertujuan untuk memberikan satu nilai yang paling mewakili keseluruhan data, sehingga memungkinkan kita memahami di mana sebagian besar nilai data terkonsentrasi. Tiga ukuran tendensi sentral yang paling umum adalah: Mean, Median, dan Modus.

1.1 Mean

Rata-rata diperoleh dengan membagi jumlah semua nilai data dengan jumlah total observasi. Nilai ini cocok untuk tipe data interval dan rasio.

1.2 Median

Median adalah nilai tengah dari kumpulan data yang diurutkan. Median cocok untuk data ordinal, interval, dan rasio. Langkah-langkah untuk Mencari Median:

  1. Susunlah data dalam urutan menaik.
  2. Jika jumlah titik data ganjil berada pada posisi n+1/2
  3. Jika genap median adalah rata-rata dari dua nilai tengah.

1.3 Modus

Modus adalah nilai yang paling sering muncul dalam suatu dataset. Modus dapat digunakan untuk data nominal, ordinal, interval, atau rasio.

2 Visualisasi untuk Central Tendency

Memahami ukuran tendensi sentral mean median, dan modus -lebih intuitif jika didukung oleh visualisasi. Representasi grafis seperti histogram dan boxplot membantu mengungkap bentuk, sebaran, dan keseimbangan yang mendasari suatu kumpulan data. Melalui alat visual ini, kita dapat mengidentifikasi apakah data tersebut simetris miring, kategoris atau multimodal.

Setiap visualisasi memberikan wawasan yang unik:

  1. Histogram menunjukkan distribusi frekuensi dan bagaimana ukuran sentral selaras dengan konsentrasi data.

  2. Kotak-kotak menyorot median, kuartil, dan keberadaan outlier dalam format yang ringkas.

Pada subbagian berikut, kita akan mengeksplorasi bagaimana kecenderungan sentral berperilaku dalam kondisi yang berbeda visualisasi histogram dan boxplot: menggunakan

Pada sub bagian berikut, kita akan mengeksplorasi bagaimana kecenderungan sentral berperilaku dalam kondisi yang berbeda visualisasi histogram dan boxplot:

  1. Simetris dan Tanpa Outlier ketika data terdistribusi secara merata di sekitar pusat.

  2. Nilai Ekstrem (Miring) ketika outlier menarik nilai rata-rata ke satu arah.

  3. Variabel Kategorikal - ketika data mewakili kelompok atau kelas yang berbeda.

  4. Lebih Dari Satu Modus ketika data memiliki beberapa puncak atau pusat konsentrasi.

2.1 Simetris dan Tidak Ada Outlier

Distribusi simetris terjadi ketika nilai-nilai data tersebar merata di sekitar titik pusat, menciptakan pola yang seimbang dan berbentuk lonceng. Dalam hal ini, nilai rata-rata median, dan modus semuanya berada pada atau mendekati titik pusat yang sama. Hal ini menunjukkan bahwa tidak ada outlier atau kemiringan signifikan yang menarik data ke satu sisi.

2.2 Nilai Ekstrem (Miring)

Distribusi miring terjadi ketika nilai-nilai data tidak terdistribusi secara simetris di sekitar pusat artinya salah satu ekor distribusi lebih panjang atau lebih melebar daripada yang lain. Kemiringan ini sering kali disebabkan oleh nilai-nilai ekstrem (outlier) yang menarik rata-rata ke satu arah, sementara median dan modus tetap mendekati puncak data.

Ketika suatu set data berisi nilai ekstrem tinggi atau rendah, distribusinya menjadi miring positif (miring ke kanan) atau miring negatif (miring ke kiri). Distorsi ini memengaruhi posisi ukuran tendensi sentral dan memberikan wawasan berharga tentang perilaku data yang mendasarinya.

2.3 Variabel Kategori

Variabel kategori membagi data ke dalam kelompok atau kategori yang berbeda. Ketika dikombinasikan dengan variabel numerik, kita dapat menganalisis perbedaan distribusi nilai numerik di berbagai kategori. Boxplot merupakan visualisasi yang sangat baik untuk tujuan ini diagram ini menunjukkan median, kuartil, rentang, dan outlier dalam setiap kelompok

2.4 Lebih Dari Satu Modus

Dalam banyak kumpulan data dunia nyata, distribusi nilai tidak selalu membentuk satu puncak yang halus. Sebaliknya, beberapa kumpulan data menunjukkan dua atau lebih puncak yang berbeda, yang dikenal sebagai beberapa modus. Setiap modus mewakili sebuah klaster tempat nilai-nilai cenderung terkonsentrasi artinya data memiliki beberapa wilayah dengan frekuensi tinggi, alih-alih satu lokasi sentral.

Tidak seperti histogram, boxplot tidak menampilkan jumlah puncak yang tepat, tetapi menunjukkan dengan jelas bahwa data tidak terdistribusi secara simetris misalnya, garis median mungkin tidak berada di tengah, dan kumis mungkin memanjang tidak merata ke satu sisi. Bersama-sama, histogram dan boxplot memberikan wawasan yang saling melengkapi:

  1. histogram mengungkapkan keseluruhan (dan beberapa mode), bentuk

  2. sementara boxplot menekankan penyebaran dan kemiringan data.

3 Persiapan

# 1) Paket yang diperlukan (instal jika perlu)
required_pkgs <- c("readxl", "dplyr", "ggplot2", "gridExtra", "moments")
to_install <- required_pkgs[!(required_pkgs %in% installed.packages()[, "Package"])]
if (length(to_install)) {
  install.packages(to_install, dependencies = TRUE, repos = getOption("repos"))
}
lapply(required_pkgs, library, character.only = TRUE)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Warning: package 'gridExtra' was built under R version 4.5.2
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
## Warning: package 'moments' was built under R version 4.5.2
## [[1]]
## [1] "readxl"    "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [7] "methods"   "base"     
## 
## [[2]]
## [1] "dplyr"     "readxl"    "stats"     "graphics"  "grDevices" "utils"    
## [7] "datasets"  "methods"   "base"     
## 
## [[3]]
##  [1] "ggplot2"   "dplyr"     "readxl"    "stats"     "graphics"  "grDevices"
##  [7] "utils"     "datasets"  "methods"   "base"     
## 
## [[4]]
##  [1] "gridExtra" "ggplot2"   "dplyr"     "readxl"    "stats"     "graphics" 
##  [7] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[5]]
##  [1] "moments"   "gridExtra" "ggplot2"   "dplyr"     "readxl"    "stats"    
##  [7] "graphics"  "grDevices" "utils"     "datasets"  "methods"   "base"
# file_path <- "D:/Tugas 6/Tugas 6/4 Central Tendency - Introduction to Statistics.xlsx"
file_path <- file.choose()
#file_path <- "D:/Tugas 6/Tugas 6/4 Central Tendency - Introduction to Statistics.xlsx"

if (!file.exists(file_path)) stop("File Excel tidak ditemukan. Ubah variable 'file_path' ke lokasi file yang benar.")

# 3) Baca data awal (pakai header)
raw <- readxl::read_excel(file_path, col_names = TRUE)
## New names:
## • `` -> `...1`
# Hapus kolom index auto jika ada (seperti ...1)
if ("...1" %in% names(raw)) raw <- raw %>% select(-`...1`)

# 4) Perbaiki header bila bergeser (ambil row 2 sebagai header jika perlu)
expected <- c("CustomerID", "Age", "Gender", "StoreLocation", "ProductCategory",
              "TotalPurchase", "NumberOfVisits", "FeedbackScore")

if (sum(tolower(names(raw)) %in% tolower(expected)) < 4) {
  message("Header tidak sesuai -> membaca ulang dan menggunakan baris ke-2 sebagai header.")
  raw2 <- readxl::read_excel(file_path, col_names = FALSE)
  header_row <- as.character(unlist(raw2[2, ]))
  header_row[is.na(header_row) | header_row == ""] <- paste0("X", seq_along(header_row))[is.na(header_row) | header_row == ""]
  df <- raw2[-c(1, 2), ]
  colnames(df) <- header_row
} else {
  df <- raw
}

# 5) Bersihkan nama kolom dan fallback assign expected bila perlu
colnames(df) <- trimws(gsub("\\n|\\r", " ", colnames(df)))
if (ncol(df) >= length(expected) && !all(expected %in% colnames(df))) {
  message("Fallback: assign 8 kolom pertama ke nama expected.")
  df <- df[, 1:length(expected)]
  colnames(df) <- expected
}
present_cols <- intersect(expected, colnames(df))
df <- df[, present_cols, drop = FALSE]

message("Kolom terdeteksi: ", paste(colnames(df), collapse = ", "))
## Kolom terdeteksi: CustomerID, Age, Gender, StoreLocation, ProductCategory, TotalPurchase, NumberOfVisits, FeedbackScore
message("Contoh 6 baris:")
## Contoh 6 baris:
print(utils::head(df, 6))
## # A tibble: 6 × 8
##   CustomerID   Age Gender StoreLocation ProductCategory TotalPurchase
##        <dbl> <dbl> <chr>  <chr>         <chr>                   <dbl>
## 1          1    32 M      West          Electronics               528
## 2          2    37 F      South         Books                      72
## 3          3    63 M      West          Electronics               327
## 4          4    41 M      North         Sports                    391
## 5          5    42 F      East          Electronics               514
## 6          6    66 F      East          Sports                    381
## # ℹ 2 more variables: NumberOfVisits <dbl>, FeedbackScore <dbl>

4 Central Tendency dari Setiap Variabel

Berdasarkan data yang kami pakai kolom-kolom yang termasuk varibel numerik adalah:

  • Age
  • Total Purchase
  • Number Of Visit
  • Feedback Score

Berdasarkan data yang kami pakai kolom-kolom yang termasuk varibel kategori adalah:

  • Gender
  • Store Location
  • Product Categry

Berikut adalah ringkasan Central Tendency mulai dari Mean (rata-rata), Median (nilai tengah), dan Modus (nilai yang paling sering muncul) untuk setiap kolom/variabel numerik.

# 6) Konversi numerik (robust)
to_num <- function(x) {
  x <- as.character(x)
  x <- trimws(x)
  x[x == ""] <- NA
  x <- gsub("\\.(?=\\d{3}(?:\\D|$))", "", x, perl = TRUE)  # hapus dot thousand
  x <- gsub(",", ".", x)                                   # koma -> titik
  suppressWarnings(as.numeric(x))
}

num_cols <- intersect(c("Age", "TotalPurchase", "NumberOfVisits", "FeedbackScore"), names(df))
for (col in num_cols) df[[col]] <- to_num(df[[col]])

# 7) Modus function
get_mode <- function(v) {
  v2 <- na.omit(round(v, 0))
  if (length(v2) == 0) return(NA)
  tab <- table(v2)
  as.numeric(names(tab)[which.max(tab)])
}

# 8) Hitung statistik ringkasan (ditampilkan di console)
stats_list <- lapply(num_cols, function(col) {
  v <- df[[col]]
  data.frame(
    Variable = col,
    N = sum(!is.na(v)),
    Mean = round(mean(v, na.rm = TRUE), 2),
    Median = round(median(v, na.rm = TRUE), 2),
    Mode = get_mode(v),
    Skewness = round(moments::skewness(v, na.rm = TRUE), 3),
    Kurtosis = round(moments::kurtosis(v, na.rm = TRUE), 3),
    stringsAsFactors = FALSE
  )
})
stats_tbl <- do.call(rbind, stats_list)
print(stats_tbl)
##         Variable   N   Mean Median Mode Skewness Kurtosis
## 1            Age 200  39.99   39.0   18    0.335    2.539
## 2  TotalPurchase 200 211.79  108.5   33    1.172    4.112
## 3 NumberOfVisits 200   5.16    5.0    5    0.372    2.893
## 4  FeedbackScore 200   2.80    3.0    1    0.204    1.707
# 9) Fungsi plotting: histogram + density + mean/median/mode lines
plot_var <- function(v, varname, bins = 30) {
  m <- mean(v, na.rm = TRUE)
  md <- median(v, na.rm = TRUE)
  mo <- get_mode(v)
  ggplot(data.frame(x = v), aes(x = x)) +
    geom_histogram(aes(y = after_stat(density)), bins = bins, fill = "#5ab4ac", color = "white", alpha = 0.85) +
    geom_density(color = "#2b8cbe", linewidth = 1.05) +
    geom_vline(xintercept = m, color = "black", linewidth = 1.05) +
    geom_vline(xintercept = md, color = "blue", linetype = "dashed", linewidth = 1.05) +
    geom_vline(xintercept = mo, color = "red", linetype = "dotdash", linewidth = 1.05) +
    labs(title = paste0("Distribusi: ", varname),
         subtitle = sprintf("N=%d  Mean=%.2f  Median=%.2f  Mode=%s", sum(!is.na(v)), m, md, mo),
         x = varname, y = "Density") +
    theme_minimal(base_size = 12)
}

# 10) Tampilkan plot untuk tiap kolom numerik (hanya tampilkan, tidak menyimpan)
plots <- list()
if (length(num_cols) == 0) message("Tidak ada kolom numerik untuk diplot.")
for (col in num_cols) {
  p <- plot_var(df[[col]], col, bins = 30)
  plots[[col]] <- p
  print(p)   # tampil di Plots pane atau di dokumen knitted
}

# 11) Jika lebih dari 1 plot, gabungkan dan tampilkan vertikal (tidak disimpan)
if (length(plots) > 1) {
  gridExtra::grid.arrange(grobs = plots, ncol = 1)
}

5 Analisis Boxplot per Kategori

  • Total Purchase
  • Number Ofvisity
  • ProductCategory
  • Storelocation
  • Gender
# 12) Boxplots per kategori (TotalPurchase & NumberOfVisits by ProductCategory / StoreLocation / Gender)
cat_cols <- intersect(c("ProductCategory", "StoreLocation", "Gender"), names(df))
if (length(cat_cols) == 0) {
  message("Kolom kategori (ProductCategory/StoreLocation/Gender) tidak ditemukan. Lewati boxplot.")
} else {
  make_box <- function(numeric_var, cat_var) {
    df_tmp <- df %>%
      dplyr::filter(!is.na(.data[[numeric_var]]), !is.na(.data[[cat_var]])) %>%
      dplyr::mutate(!!cat_var := as.factor(.data[[cat_var]]))
    if (nrow(df_tmp) == 0) return(NULL)
    p <- ggplot(df_tmp, aes_string(x = cat_var, y = numeric_var, fill = cat_var)) +
      geom_boxplot(outlier.colour = "red", outlier.shape = 16, alpha = 0.8) +
      stat_summary(fun = mean, geom = "point", shape = 23, size = 3, fill = "yellow") +
      labs(title = paste0(numeric_var, " by ", cat_var),
           subtitle = "Boxplot per kategori — titik = mean",
           x = cat_var, y = numeric_var) +
      theme_minimal(base_size = 12) +
      theme(legend.position = "none", axis.text.x = element_text(angle = 45, hjust = 1))
    return(p)
  }

  for (numvar in c("TotalPurchase", "NumberOfVisits")) {
    if (!(numvar %in% names(df))) next
    for (catv in cat_cols) {
      pbox <- make_box(numvar, catv)
      if (is.null(pbox)) next
      print(pbox)   # tampil di dokumen / Plots pane
    }
  }
}
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Interpretasi

1.Histogram menunjukkan distribusi frekuensi dan bagaimana ukuran sentral selaras dengan konsentrasi data.

2.Kotak-kotak menyorot median, kuartil, dan keberadaan outlier dalam format yang ringkas.

3.Ketika suatu set data berisi nilai ekstrem tinggi atau rendah, distribusinya menjadi miring positif (miring ke kanan) atau miring negatif (miring ke kiri)

4.histogram menggungkapkan keseluruhan (dan beberapa mode), bentuk sementara boxplot menekankan penyebaran dan kemiringan data.

LS0tDQp0aXRsZTogIkNlbnRyYWwgVGVuZGVuY3kiICAgICAgICMgTWFpbiB0aXRsZSBvZiB0aGUgZG9jdW1lbnQNCnN1YnRpdGxlOiAiQXNzaWdubWVudCB+IFdlZWsgNiIgICMgU3VidGl0bGUgb3IgdG9waWMgZm9yIHdlZWsgMg0KYXV0aG9yOg0KLSAiTS5GaXRyYWggQWlkaWwgSGFyYWhhcCINCi0gIlBhc2thbGlzIEZhcmVsbmF0YSBaYW1hc2kiDQotICJaaWRoYW4gQWxmYXJlemkgQWZkaGkiDQotICJIYW5hZmkgTWFsaWsgUmlmYWkiDQotICIgRGVuIFl1YW4gRnJhc3Nla2EiIyBSZXBsYWNlIHdpdGggeW91ciBmdWxsIG5hbWUNCmRhdGU6ICAiYHIgZm9ybWF0KFN5cy5EYXRlKCksICclQiAlZCwgJVknKWAiICMgQXV0byBkaXNwbGF5cyB0aGUgY3VycmVudCBkYXRlDQpvdXRwdXQ6ICAgICAgICAgICAgICAgICAgICAgICAgICMgT3V0cHV0IHNlY3Rpb24gZGVmaW5lcyB0aGUgZm9ybWF0IGFuZCBsYXlvdXQgDQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOiAgICAgICMgaHR0cHM6Ly9naXRodWIuY29tL2p1YmEvcm1kZm9ybWF0cw0KICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICAgICAgICAjIEVtYmVkcyBhbGwgcmVzb3VyY2VzIChDU1MsIEpTLCBpbWFnZXMpIA0KICAgIHRodW1ibmFpbHM6IHRydWUgICAgICAgICAgICAjIERpc3BsYXlzIGltYWdlIHRodW1ibmFpbHMgaW4gdGhlIGRvYw0KICAgIGxpZ2h0Ym94OiB0cnVlICAgICAgICAgICAgICAjIEVuYWJsZXMgY2xpY2sgdG8gZW5sYXJnZSBpbWFnZXMNCiAgICBnYWxsZXJ5OiB0cnVlICAgICAgICAgICAgICAgIyBHcm91cHMgaW1hZ2VzIGludG8gYW4gaW50ZXJhY3RpdmUgZ2FsbGVyeQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgICAgICAjIEF1dG9tYXRpY2FsbHkgbnVtYmVycyBhbGwgc2VjdGlvbnMNCiAgICBsaWJfZGlyOiBsaWJzICAgICAgICAgICAgICAgIyBEaXJlY3Rvcnkgd2hlcmUgSmF2YVNjcmlwdC9DU1MgbGlicmFyaWVzDQogICAgZGZfcHJpbnQ6ICJwYWdlZCIgICAgICAgICAgICMgRGlzcGxheXMgZGF0YSBmcmFtZXMgYXMgaW50ZXJhY3RpdmUgcGFnZWQgDQogICAgY29kZV9mb2xkaW5nOiAic2hvdyIgICAgICAgICMgQWxsb3dzIGZvbGRpbmcvdW5mb2xkaW5nIFIgY29kZSBibG9ja3MgDQogICAgY29kZV9kb3dubG9hZDogeWVzICAgICAgICAgICMgQWRkcyBhIGJ1dHRvbiB0byBkb3dubG9hZCBhbGwgUiBjb2RlDQotLS0NCg0KDQo8aW1nIGlkPSJGb3RvIiBzcmM9IkM6XFVzZXJzXFNyaSBCdWRpeWFudGlcRG93bmxvYWRzXFdoYXRzQXBwIEltYWdlIDIwMjUtMTAtMTAgYXQgMTguNDIuMTIuanBlZyIgYWx0PSJMb2dvIiBzdHlsZT0id2lkdGg6MjAwcHg7IGRpc3BsYXk6IGJsb2NrOyBtYXJnaW46IGF1dG87Ij4NCg0KIyBDZW50YWwgVGVuZGVuY3kNCkNlbnRyYWwgVGVuZGVuY3kgYWRhbGFoIHVrdXJhbiBzdGF0aXN0aWsgeWFuZyBtZXJlcHJlc2VudGFzaWthbiBuaWxhaSB0aXBpa2FsIGF0YXUgc2VudHJhbCBkYXJpIHN1YXR1IGt1bXB1bGFuIGRhdGEuIFVrdXJhbiBpbmkgYmVydHVqdWFuIHVudHVrIG1lbWJlcmlrYW4gc2F0dSBuaWxhaSB5YW5nIHBhbGluZyBtZXdha2lsaSBrZXNlbHVydWhhbiBkYXRhLCBzZWhpbmdnYSBtZW11bmdraW5rYW4ga2l0YSBtZW1haGFtaSBkaSBtYW5hIHNlYmFnaWFuIGJlc2FyIG5pbGFpIGRhdGEgdGVya29uc2VudHJhc2kuIFRpZ2EgdWt1cmFuIHRlbmRlbnNpIHNlbnRyYWwgeWFuZyBwYWxpbmcgdW11bSBhZGFsYWg6IE1lYW4sIE1lZGlhbiwgZGFuIE1vZHVzLg0KDQojIyBNZWFuDQpSYXRhLXJhdGEgZGlwZXJvbGVoIGRlbmdhbiBtZW1iYWdpIGp1bWxhaCBzZW11YSBuaWxhaSBkYXRhIGRlbmdhbiBqdW1sYWggdG90YWwgb2JzZXJ2YXNpLiBOaWxhaSBpbmkgY29jb2sgdW50dWsgdGlwZSBkYXRhIGludGVydmFsIGRhbiByYXNpby4NCg0KIyMgTWVkaWFuDQpNZWRpYW4gYWRhbGFoIG5pbGFpIHRlbmdhaCBkYXJpIGt1bXB1bGFuIGRhdGEgeWFuZyBkaXVydXRrYW4uIE1lZGlhbiBjb2NvayB1bnR1ayBkYXRhIG9yZGluYWwsIGludGVydmFsLCBkYW4gcmFzaW8uIExhbmdrYWgtbGFuZ2thaCB1bnR1ayBNZW5jYXJpIE1lZGlhbjoNCiANCjEuIFN1c3VubGFoIGRhdGEgZGFsYW0gdXJ1dGFuIG1lbmFpay4NCjIuIEppa2EganVtbGFoIHRpdGlrIGRhdGEgZ2FuamlsIGJlcmFkYSBwYWRhIHBvc2lzaSBuKzEvMg0KMy4gSmlrYSBnZW5hcCBtZWRpYW4gYWRhbGFoIHJhdGEtcmF0YSBkYXJpIGR1YSBuaWxhaSB0ZW5nYWguDQoNCiMjIE1vZHVzDQpNb2R1cyBhZGFsYWggbmlsYWkgeWFuZyBwYWxpbmcgc2VyaW5nIG11bmN1bCBkYWxhbSBzdWF0dSBkYXRhc2V0LiBNb2R1cyBkYXBhdCBkaWd1bmFrYW4gdW50dWsgZGF0YSBub21pbmFsLCBvcmRpbmFsLCBpbnRlcnZhbCwgYXRhdSByYXNpby4NCg0KIyBWaXN1YWxpc2FzaSB1bnR1ayBDZW50cmFsIFRlbmRlbmN5DQpNZW1haGFtaSB1a3VyYW4gdGVuZGVuc2kgc2VudHJhbCBtZWFuIG1lZGlhbiwgZGFuIG1vZHVzIC1sZWJpaCBpbnR1aXRpZiBqaWthIGRpZHVrdW5nIG9sZWggdmlzdWFsaXNhc2kuIFJlcHJlc2VudGFzaSBncmFmaXMgc2VwZXJ0aSBoaXN0b2dyYW0gZGFuIGJveHBsb3QgbWVtYmFudHUgbWVuZ3VuZ2thcCBiZW50dWssIHNlYmFyYW4sIGRhbiBrZXNlaW1iYW5nYW4geWFuZyBtZW5kYXNhcmkgc3VhdHUga3VtcHVsYW4gZGF0YS4gTWVsYWx1aSBhbGF0IHZpc3VhbCBpbmksIGtpdGEgZGFwYXQgbWVuZ2lkZW50aWZpa2FzaSBhcGFrYWggZGF0YSB0ZXJzZWJ1dCBzaW1ldHJpcyBtaXJpbmcsIGthdGVnb3JpcyBhdGF1IG11bHRpbW9kYWwuDQoNClNldGlhcCB2aXN1YWxpc2FzaSBtZW1iZXJpa2FuIHdhd2FzYW4geWFuZyB1bmlrOg0KDQoxLiBIaXN0b2dyYW0gbWVudW5qdWtrYW4gZGlzdHJpYnVzaSBmcmVrdWVuc2kgZGFuIGJhZ2FpbWFuYSB1a3VyYW4gc2VudHJhbCBzZWxhcmFzIGRlbmdhbiBrb25zZW50cmFzaSBkYXRhLg0KDQoyLiBLb3Rhay1rb3RhayBtZW55b3JvdCBtZWRpYW4sIGt1YXJ0aWwsIGRhbiBrZWJlcmFkYWFuIG91dGxpZXIgZGFsYW0gZm9ybWF0IHlhbmcgcmluZ2thcy4NCg0KUGFkYSBzdWJiYWdpYW4gYmVyaWt1dCwga2l0YSBha2FuIG1lbmdla3NwbG9yYXNpIGJhZ2FpbWFuYSBrZWNlbmRlcnVuZ2FuIHNlbnRyYWwgYmVycGVyaWxha3UgZGFsYW0ga29uZGlzaSB5YW5nIGJlcmJlZGEgdmlzdWFsaXNhc2kgaGlzdG9ncmFtIGRhbiBib3hwbG90OiBtZW5nZ3VuYWthbg0KDQpQYWRhIHN1YiBiYWdpYW4gYmVyaWt1dCwga2l0YSBha2FuIG1lbmdla3NwbG9yYXNpIGJhZ2FpbWFuYSBrZWNlbmRlcnVuZ2FuIHNlbnRyYWwgYmVycGVyaWxha3UgZGFsYW0ga29uZGlzaSB5YW5nIGJlcmJlZGEgdmlzdWFsaXNhc2kgaGlzdG9ncmFtIGRhbiBib3hwbG90OiANCg0KMS4gU2ltZXRyaXMgZGFuIFRhbnBhIE91dGxpZXIga2V0aWthIGRhdGEgdGVyZGlzdHJpYnVzaSBzZWNhcmEgbWVyYXRhIGRpIHNla2l0YXIgcHVzYXQuDQoNCjIuIE5pbGFpIEVrc3RyZW0gKE1pcmluZykga2V0aWthIG91dGxpZXIgbWVuYXJpayBuaWxhaSByYXRhLXJhdGEga2Ugc2F0dSBhcmFoLg0KDQozLiBWYXJpYWJlbCBLYXRlZ29yaWthbCAtIGtldGlrYSBkYXRhIG1ld2FraWxpIGtlbG9tcG9rIGF0YXUga2VsYXMgeWFuZyBiZXJiZWRhLg0KDQo0LiBMZWJpaCBEYXJpIFNhdHUgTW9kdXMga2V0aWthIGRhdGEgbWVtaWxpa2kgYmViZXJhcGEgcHVuY2FrIGF0YXUgcHVzYXQga29uc2VudHJhc2kuDQoNCiMjIFNpbWV0cmlzIGRhbiBUaWRhayBBZGEgT3V0bGllcg0KRGlzdHJpYnVzaSBzaW1ldHJpcyB0ZXJqYWRpIGtldGlrYSBuaWxhaS1uaWxhaSBkYXRhIHRlcnNlYmFyIG1lcmF0YSBkaSBzZWtpdGFyIHRpdGlrIHB1c2F0LCBtZW5jaXB0YWthbiBwb2xhIHlhbmcgc2VpbWJhbmcgZGFuIGJlcmJlbnR1ayBsb25jZW5nLiBEYWxhbSBoYWwgaW5pLCBuaWxhaSByYXRhLXJhdGEgbWVkaWFuLCBkYW4gbW9kdXMgc2VtdWFueWEgYmVyYWRhIHBhZGEgYXRhdSBtZW5kZWthdGkgdGl0aWsgcHVzYXQgeWFuZyBzYW1hLiBIYWwgaW5pIG1lbnVuanVra2FuIGJhaHdhIHRpZGFrIGFkYSBvdXRsaWVyIGF0YXUga2VtaXJpbmdhbiBzaWduaWZpa2FuIHlhbmcgbWVuYXJpayBkYXRhIGtlIHNhdHUgc2lzaS4NCg0KIyMgTmlsYWkgRWtzdHJlbSAoTWlyaW5nKQ0KRGlzdHJpYnVzaSBtaXJpbmcgdGVyamFkaSBrZXRpa2EgbmlsYWktbmlsYWkgZGF0YSB0aWRhayB0ZXJkaXN0cmlidXNpIHNlY2FyYSBzaW1ldHJpcyBkaSBzZWtpdGFyIHB1c2F0IGFydGlueWEgc2FsYWggc2F0dSBla29yIGRpc3RyaWJ1c2kgbGViaWggcGFuamFuZyBhdGF1IGxlYmloIG1lbGViYXIgZGFyaXBhZGEgeWFuZyBsYWluLiBLZW1pcmluZ2FuIGluaSBzZXJpbmcga2FsaSBkaXNlYmFia2FuIG9sZWggbmlsYWktbmlsYWkgZWtzdHJlbSAob3V0bGllcikgeWFuZyBtZW5hcmlrIHJhdGEtcmF0YSBrZSBzYXR1IGFyYWgsIHNlbWVudGFyYSBtZWRpYW4gZGFuIG1vZHVzIHRldGFwIG1lbmRla2F0aSBwdW5jYWsgZGF0YS4NCg0KS2V0aWthIHN1YXR1IHNldCBkYXRhIGJlcmlzaSBuaWxhaSBla3N0cmVtIHRpbmdnaSBhdGF1IHJlbmRhaCwgZGlzdHJpYnVzaW55YSBtZW5qYWRpIG1pcmluZyBwb3NpdGlmIChtaXJpbmcga2Uga2FuYW4pIGF0YXUgbWlyaW5nIG5lZ2F0aWYgKG1pcmluZyBrZSBraXJpKS4gRGlzdG9yc2kgaW5pIG1lbWVuZ2FydWhpIHBvc2lzaSB1a3VyYW4gdGVuZGVuc2kgc2VudHJhbCBkYW4gbWVtYmVyaWthbiB3YXdhc2FuIGJlcmhhcmdhIHRlbnRhbmcgcGVyaWxha3UgZGF0YSB5YW5nIG1lbmRhc2FyaW55YS4NCg0KIyMgVmFyaWFiZWwgS2F0ZWdvcmkNClZhcmlhYmVsIGthdGVnb3JpIG1lbWJhZ2kgZGF0YSBrZSBkYWxhbSBrZWxvbXBvayBhdGF1IGthdGVnb3JpIHlhbmcgYmVyYmVkYS4gS2V0aWthIGRpa29tYmluYXNpa2FuIGRlbmdhbiB2YXJpYWJlbCBudW1lcmlrLCBraXRhIGRhcGF0IG1lbmdhbmFsaXNpcyBwZXJiZWRhYW4gZGlzdHJpYnVzaSBuaWxhaSBudW1lcmlrIGRpIGJlcmJhZ2FpIGthdGVnb3JpLiBCb3hwbG90IG1lcnVwYWthbiB2aXN1YWxpc2FzaSB5YW5nIHNhbmdhdCBiYWlrIHVudHVrIHR1anVhbiBpbmkgZGlhZ3JhbSBpbmkgbWVudW5qdWtrYW4gbWVkaWFuLCBrdWFydGlsLCByZW50YW5nLCBkYW4gb3V0bGllciBkYWxhbSBzZXRpYXAga2Vsb21wb2sNCg0KIyMgTGViaWggRGFyaSBTYXR1IE1vZHVzDQpEYWxhbSBiYW55YWsga3VtcHVsYW4gZGF0YSBkdW5pYSBueWF0YSwgZGlzdHJpYnVzaSBuaWxhaSB0aWRhayBzZWxhbHUgbWVtYmVudHVrIHNhdHUgcHVuY2FrIHlhbmcgaGFsdXMuIFNlYmFsaWtueWEsIGJlYmVyYXBhIGt1bXB1bGFuIGRhdGEgbWVudW5qdWtrYW4gZHVhIGF0YXUgbGViaWggcHVuY2FrIHlhbmcgYmVyYmVkYSwgeWFuZyBkaWtlbmFsIHNlYmFnYWkgYmViZXJhcGEgbW9kdXMuIFNldGlhcCBtb2R1cyBtZXdha2lsaSBzZWJ1YWgga2xhc3RlciB0ZW1wYXQgbmlsYWktbmlsYWkgY2VuZGVydW5nIHRlcmtvbnNlbnRyYXNpIGFydGlueWEgZGF0YSBtZW1pbGlraSBiZWJlcmFwYSB3aWxheWFoIGRlbmdhbiBmcmVrdWVuc2kgdGluZ2dpLCBhbGloLWFsaWggc2F0dSBsb2thc2kgc2VudHJhbC4NCg0KVGlkYWsgc2VwZXJ0aSBoaXN0b2dyYW0sIGJveHBsb3QgdGlkYWsgbWVuYW1waWxrYW4ganVtbGFoIHB1bmNhayB5YW5nIHRlcGF0LCB0ZXRhcGkgbWVudW5qdWtrYW4gZGVuZ2FuIGplbGFzIGJhaHdhIGRhdGEgdGlkYWsgdGVyZGlzdHJpYnVzaSBzZWNhcmEgc2ltZXRyaXMgbWlzYWxueWEsIGdhcmlzIG1lZGlhbiBtdW5na2luIHRpZGFrIGJlcmFkYSBkaSB0ZW5nYWgsIGRhbiBrdW1pcyBtdW5na2luIG1lbWFuamFuZyB0aWRhayBtZXJhdGEga2Ugc2F0dSBzaXNpLiBCZXJzYW1hLXNhbWEsIGhpc3RvZ3JhbSBkYW4gYm94cGxvdCBtZW1iZXJpa2FuIHdhd2FzYW4geWFuZyBzYWxpbmcgbWVsZW5na2FwaToNCg0KMS4gaGlzdG9ncmFtIG1lbmd1bmdrYXBrYW4ga2VzZWx1cnVoYW4gKGRhbiBiZWJlcmFwYSBtb2RlKSwgYmVudHVrDQoNCjIuIHNlbWVudGFyYSBib3hwbG90IG1lbmVrYW5rYW4gcGVueWViYXJhbiBkYW4ga2VtaXJpbmdhbiBkYXRhLg0KDQojIFBlcnNpYXBhbg0KDQpgYGB7cn0NCiMgMSkgUGFrZXQgeWFuZyBkaXBlcmx1a2FuIChpbnN0YWwgamlrYSBwZXJsdSkNCnJlcXVpcmVkX3BrZ3MgPC0gYygicmVhZHhsIiwgImRwbHlyIiwgImdncGxvdDIiLCAiZ3JpZEV4dHJhIiwgIm1vbWVudHMiKQ0KdG9faW5zdGFsbCA8LSByZXF1aXJlZF9wa2dzWyEocmVxdWlyZWRfcGtncyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywgIlBhY2thZ2UiXSldDQppZiAobGVuZ3RoKHRvX2luc3RhbGwpKSB7DQogIGluc3RhbGwucGFja2FnZXModG9faW5zdGFsbCwgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBnZXRPcHRpb24oInJlcG9zIikpDQp9DQpsYXBwbHkocmVxdWlyZWRfcGtncywgbGlicmFyeSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KDQojIGZpbGVfcGF0aCA8LSAiRDovVHVnYXMgNi9UdWdhcyA2LzQgQ2VudHJhbCBUZW5kZW5jeSAtIEludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNzLnhsc3giDQpmaWxlX3BhdGggPC0gZmlsZS5jaG9vc2UoKQ0KI2ZpbGVfcGF0aCA8LSAiRDovVHVnYXMgNi9UdWdhcyA2LzQgQ2VudHJhbCBUZW5kZW5jeSAtIEludHJvZHVjdGlvbiB0byBTdGF0aXN0aWNzLnhsc3giDQoNCmlmICghZmlsZS5leGlzdHMoZmlsZV9wYXRoKSkgc3RvcCgiRmlsZSBFeGNlbCB0aWRhayBkaXRlbXVrYW4uIFViYWggdmFyaWFibGUgJ2ZpbGVfcGF0aCcga2UgbG9rYXNpIGZpbGUgeWFuZyBiZW5hci4iKQ0KDQojIDMpIEJhY2EgZGF0YSBhd2FsIChwYWthaSBoZWFkZXIpDQpyYXcgPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGZpbGVfcGF0aCwgY29sX25hbWVzID0gVFJVRSkNCg0KIyBIYXB1cyBrb2xvbSBpbmRleCBhdXRvIGppa2EgYWRhIChzZXBlcnRpIC4uLjEpDQppZiAoIi4uLjEiICVpbiUgbmFtZXMocmF3KSkgcmF3IDwtIHJhdyAlPiUgc2VsZWN0KC1gLi4uMWApDQoNCiMgNCkgUGVyYmFpa2kgaGVhZGVyIGJpbGEgYmVyZ2VzZXIgKGFtYmlsIHJvdyAyIHNlYmFnYWkgaGVhZGVyIGppa2EgcGVybHUpDQpleHBlY3RlZCA8LSBjKCJDdXN0b21lcklEIiwgIkFnZSIsICJHZW5kZXIiLCAiU3RvcmVMb2NhdGlvbiIsICJQcm9kdWN0Q2F0ZWdvcnkiLA0KICAgICAgICAgICAgICAiVG90YWxQdXJjaGFzZSIsICJOdW1iZXJPZlZpc2l0cyIsICJGZWVkYmFja1Njb3JlIikNCg0KaWYgKHN1bSh0b2xvd2VyKG5hbWVzKHJhdykpICVpbiUgdG9sb3dlcihleHBlY3RlZCkpIDwgNCkgew0KICBtZXNzYWdlKCJIZWFkZXIgdGlkYWsgc2VzdWFpIC0+IG1lbWJhY2EgdWxhbmcgZGFuIG1lbmdndW5ha2FuIGJhcmlzIGtlLTIgc2ViYWdhaSBoZWFkZXIuIikNCiAgcmF3MiA8LSByZWFkeGw6OnJlYWRfZXhjZWwoZmlsZV9wYXRoLCBjb2xfbmFtZXMgPSBGQUxTRSkNCiAgaGVhZGVyX3JvdyA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KHJhdzJbMiwgXSkpDQogIGhlYWRlcl9yb3dbaXMubmEoaGVhZGVyX3JvdykgfCBoZWFkZXJfcm93ID09ICIiXSA8LSBwYXN0ZTAoIlgiLCBzZXFfYWxvbmcoaGVhZGVyX3JvdykpW2lzLm5hKGhlYWRlcl9yb3cpIHwgaGVhZGVyX3JvdyA9PSAiIl0NCiAgZGYgPC0gcmF3MlstYygxLCAyKSwgXQ0KICBjb2xuYW1lcyhkZikgPC0gaGVhZGVyX3Jvdw0KfSBlbHNlIHsNCiAgZGYgPC0gcmF3DQp9DQoNCiMgNSkgQmVyc2loa2FuIG5hbWEga29sb20gZGFuIGZhbGxiYWNrIGFzc2lnbiBleHBlY3RlZCBiaWxhIHBlcmx1DQpjb2xuYW1lcyhkZikgPC0gdHJpbXdzKGdzdWIoIlxcbnxcXHIiLCAiICIsIGNvbG5hbWVzKGRmKSkpDQppZiAobmNvbChkZikgPj0gbGVuZ3RoKGV4cGVjdGVkKSAmJiAhYWxsKGV4cGVjdGVkICVpbiUgY29sbmFtZXMoZGYpKSkgew0KICBtZXNzYWdlKCJGYWxsYmFjazogYXNzaWduIDgga29sb20gcGVydGFtYSBrZSBuYW1hIGV4cGVjdGVkLiIpDQogIGRmIDwtIGRmWywgMTpsZW5ndGgoZXhwZWN0ZWQpXQ0KICBjb2xuYW1lcyhkZikgPC0gZXhwZWN0ZWQNCn0NCnByZXNlbnRfY29scyA8LSBpbnRlcnNlY3QoZXhwZWN0ZWQsIGNvbG5hbWVzKGRmKSkNCmRmIDwtIGRmWywgcHJlc2VudF9jb2xzLCBkcm9wID0gRkFMU0VdDQoNCm1lc3NhZ2UoIktvbG9tIHRlcmRldGVrc2k6ICIsIHBhc3RlKGNvbG5hbWVzKGRmKSwgY29sbGFwc2UgPSAiLCAiKSkNCm1lc3NhZ2UoIkNvbnRvaCA2IGJhcmlzOiIpDQpwcmludCh1dGlsczo6aGVhZChkZiwgNikpDQoNCmBgYA0KIyBDZW50cmFsIFRlbmRlbmN5IGRhcmkgU2V0aWFwIFZhcmlhYmVsDQpCZXJkYXNhcmthbiBkYXRhIHlhbmcga2FtaSBwYWthaSBrb2xvbS1rb2xvbSB5YW5nIHRlcm1hc3VrIHZhcmliZWwgbnVtZXJpayBhZGFsYWg6DQoNCi0gKipBZ2UqKg0KLSAqKlRvdGFsIFB1cmNoYXNlKioNCi0gKipOdW1iZXIgT2YgVmlzaXQqKg0KLSAqKkZlZWRiYWNrIFNjb3JlKioNCg0KQmVyZGFzYXJrYW4gZGF0YSB5YW5nIGthbWkgcGFrYWkga29sb20ta29sb20geWFuZyB0ZXJtYXN1ayB2YXJpYmVsIGthdGVnb3JpIGFkYWxhaDoNCg0KLSAqKkdlbmRlcioqDQotICoqU3RvcmUgTG9jYXRpb24qKg0KLSAqKlByb2R1Y3QgQ2F0ZWdyeSoqDQoNCkJlcmlrdXQgYWRhbGFoIHJpbmdrYXNhbiBDZW50cmFsIFRlbmRlbmN5IG11bGFpIGRhcmkgTWVhbiAocmF0YS1yYXRhKSwgTWVkaWFuIChuaWxhaSB0ZW5nYWgpLCBkYW4gTW9kdXMgKG5pbGFpIHlhbmcgcGFsaW5nIHNlcmluZyBtdW5jdWwpIHVudHVrIHNldGlhcCBrb2xvbS92YXJpYWJlbCBudW1lcmlrLg0KDQpgYGB7cn0NCiMgNikgS29udmVyc2kgbnVtZXJpayAocm9idXN0KQ0KdG9fbnVtIDwtIGZ1bmN0aW9uKHgpIHsNCiAgeCA8LSBhcy5jaGFyYWN0ZXIoeCkNCiAgeCA8LSB0cmltd3MoeCkNCiAgeFt4ID09ICIiXSA8LSBOQQ0KICB4IDwtIGdzdWIoIlxcLig/PVxcZHszfSg/OlxcRHwkKSkiLCAiIiwgeCwgcGVybCA9IFRSVUUpICAjIGhhcHVzIGRvdCB0aG91c2FuZA0KICB4IDwtIGdzdWIoIiwiLCAiLiIsIHgpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGtvbWEgLT4gdGl0aWsNCiAgc3VwcHJlc3NXYXJuaW5ncyhhcy5udW1lcmljKHgpKQ0KfQ0KDQpudW1fY29scyA8LSBpbnRlcnNlY3QoYygiQWdlIiwgIlRvdGFsUHVyY2hhc2UiLCAiTnVtYmVyT2ZWaXNpdHMiLCAiRmVlZGJhY2tTY29yZSIpLCBuYW1lcyhkZikpDQpmb3IgKGNvbCBpbiBudW1fY29scykgZGZbW2NvbF1dIDwtIHRvX251bShkZltbY29sXV0pDQoNCiMgNykgTW9kdXMgZnVuY3Rpb24NCmdldF9tb2RlIDwtIGZ1bmN0aW9uKHYpIHsNCiAgdjIgPC0gbmEub21pdChyb3VuZCh2LCAwKSkNCiAgaWYgKGxlbmd0aCh2MikgPT0gMCkgcmV0dXJuKE5BKQ0KICB0YWIgPC0gdGFibGUodjIpDQogIGFzLm51bWVyaWMobmFtZXModGFiKVt3aGljaC5tYXgodGFiKV0pDQp9DQoNCiMgOCkgSGl0dW5nIHN0YXRpc3RpayByaW5na2FzYW4gKGRpdGFtcGlsa2FuIGRpIGNvbnNvbGUpDQpzdGF0c19saXN0IDwtIGxhcHBseShudW1fY29scywgZnVuY3Rpb24oY29sKSB7DQogIHYgPC0gZGZbW2NvbF1dDQogIGRhdGEuZnJhbWUoDQogICAgVmFyaWFibGUgPSBjb2wsDQogICAgTiA9IHN1bSghaXMubmEodikpLA0KICAgIE1lYW4gPSByb3VuZChtZWFuKHYsIG5hLnJtID0gVFJVRSksIDIpLA0KICAgIE1lZGlhbiA9IHJvdW5kKG1lZGlhbih2LCBuYS5ybSA9IFRSVUUpLCAyKSwNCiAgICBNb2RlID0gZ2V0X21vZGUodiksDQogICAgU2tld25lc3MgPSByb3VuZChtb21lbnRzOjpza2V3bmVzcyh2LCBuYS5ybSA9IFRSVUUpLCAzKSwNCiAgICBLdXJ0b3NpcyA9IHJvdW5kKG1vbWVudHM6Omt1cnRvc2lzKHYsIG5hLnJtID0gVFJVRSksIDMpLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQp9KQ0Kc3RhdHNfdGJsIDwtIGRvLmNhbGwocmJpbmQsIHN0YXRzX2xpc3QpDQpwcmludChzdGF0c190YmwpDQoNCiMgOSkgRnVuZ3NpIHBsb3R0aW5nOiBoaXN0b2dyYW0gKyBkZW5zaXR5ICsgbWVhbi9tZWRpYW4vbW9kZSBsaW5lcw0KcGxvdF92YXIgPC0gZnVuY3Rpb24odiwgdmFybmFtZSwgYmlucyA9IDMwKSB7DQogIG0gPC0gbWVhbih2LCBuYS5ybSA9IFRSVUUpDQogIG1kIDwtIG1lZGlhbih2LCBuYS5ybSA9IFRSVUUpDQogIG1vIDwtIGdldF9tb2RlKHYpDQogIGdncGxvdChkYXRhLmZyYW1lKHggPSB2KSwgYWVzKHggPSB4KSkgKw0KICAgIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksIGJpbnMgPSBiaW5zLCBmaWxsID0gIiM1YWI0YWMiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44NSkgKw0KICAgIGdlb21fZGVuc2l0eShjb2xvciA9ICIjMmI4Y2JlIiwgbGluZXdpZHRoID0gMS4wNSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG0sIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMS4wNSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1kLCBjb2xvciA9ICJibHVlIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgbGluZXdpZHRoID0gMS4wNSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1vLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkb3RkYXNoIiwgbGluZXdpZHRoID0gMS4wNSkgKw0KICAgIGxhYnModGl0bGUgPSBwYXN0ZTAoIkRpc3RyaWJ1c2k6ICIsIHZhcm5hbWUpLA0KICAgICAgICAgc3VidGl0bGUgPSBzcHJpbnRmKCJOPSVkICBNZWFuPSUuMmYgIE1lZGlhbj0lLjJmICBNb2RlPSVzIiwgc3VtKCFpcy5uYSh2KSksIG0sIG1kLCBtbyksDQogICAgICAgICB4ID0gdmFybmFtZSwgeSA9ICJEZW5zaXR5IikgKw0KICAgIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpDQp9DQoNCiMgMTApIFRhbXBpbGthbiBwbG90IHVudHVrIHRpYXAga29sb20gbnVtZXJpayAoaGFueWEgdGFtcGlsa2FuLCB0aWRhayBtZW55aW1wYW4pDQpwbG90cyA8LSBsaXN0KCkNCmlmIChsZW5ndGgobnVtX2NvbHMpID09IDApIG1lc3NhZ2UoIlRpZGFrIGFkYSBrb2xvbSBudW1lcmlrIHVudHVrIGRpcGxvdC4iKQ0KZm9yIChjb2wgaW4gbnVtX2NvbHMpIHsNCiAgcCA8LSBwbG90X3ZhcihkZltbY29sXV0sIGNvbCwgYmlucyA9IDMwKQ0KICBwbG90c1tbY29sXV0gPC0gcA0KICBwcmludChwKSAgICMgdGFtcGlsIGRpIFBsb3RzIHBhbmUgYXRhdSBkaSBkb2t1bWVuIGtuaXR0ZWQNCn0NCg0KIyAxMSkgSmlrYSBsZWJpaCBkYXJpIDEgcGxvdCwgZ2FidW5na2FuIGRhbiB0YW1waWxrYW4gdmVydGlrYWwgKHRpZGFrIGRpc2ltcGFuKQ0KaWYgKGxlbmd0aChwbG90cykgPiAxKSB7DQogIGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKGdyb2JzID0gcGxvdHMsIG5jb2wgPSAxKQ0KfQ0KDQpgYGAgDQoNCg0KIyBBbmFsaXNpcyBCb3hwbG90IHBlciBLYXRlZ29yaQ0KLSAqKlRvdGFsIFB1cmNoYXNlKioNCi0gKipOdW1iZXIgT2Z2aXNpdHkqKg0KLSAqKlByb2R1Y3RDYXRlZ29yeSoqDQotICoqU3RvcmVsb2NhdGlvbioqDQotICoqR2VuZGVyKioNCg0KDQpgYGB7cn0NCg0KIyAxMikgQm94cGxvdHMgcGVyIGthdGVnb3JpIChUb3RhbFB1cmNoYXNlICYgTnVtYmVyT2ZWaXNpdHMgYnkgUHJvZHVjdENhdGVnb3J5IC8gU3RvcmVMb2NhdGlvbiAvIEdlbmRlcikNCmNhdF9jb2xzIDwtIGludGVyc2VjdChjKCJQcm9kdWN0Q2F0ZWdvcnkiLCAiU3RvcmVMb2NhdGlvbiIsICJHZW5kZXIiKSwgbmFtZXMoZGYpKQ0KaWYgKGxlbmd0aChjYXRfY29scykgPT0gMCkgew0KICBtZXNzYWdlKCJLb2xvbSBrYXRlZ29yaSAoUHJvZHVjdENhdGVnb3J5L1N0b3JlTG9jYXRpb24vR2VuZGVyKSB0aWRhayBkaXRlbXVrYW4uIExld2F0aSBib3hwbG90LiIpDQp9IGVsc2Ugew0KICBtYWtlX2JveCA8LSBmdW5jdGlvbihudW1lcmljX3ZhciwgY2F0X3Zhcikgew0KICAgIGRmX3RtcCA8LSBkZiAlPiUNCiAgICAgIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKC5kYXRhW1tudW1lcmljX3Zhcl1dKSwgIWlzLm5hKC5kYXRhW1tjYXRfdmFyXV0pKSAlPiUNCiAgICAgIGRwbHlyOjptdXRhdGUoISFjYXRfdmFyIDo9IGFzLmZhY3RvciguZGF0YVtbY2F0X3Zhcl1dKSkNCiAgICBpZiAobnJvdyhkZl90bXApID09IDApIHJldHVybihOVUxMKQ0KICAgIHAgPC0gZ2dwbG90KGRmX3RtcCwgYWVzX3N0cmluZyh4ID0gY2F0X3ZhciwgeSA9IG51bWVyaWNfdmFyLCBmaWxsID0gY2F0X3ZhcikpICsNCiAgICAgIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91ciA9ICJyZWQiLCBvdXRsaWVyLnNoYXBlID0gMTYsIGFscGhhID0gMC44KSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuID0gbWVhbiwgZ2VvbSA9ICJwb2ludCIsIHNoYXBlID0gMjMsIHNpemUgPSAzLCBmaWxsID0gInllbGxvdyIpICsNCiAgICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobnVtZXJpY192YXIsICIgYnkgIiwgY2F0X3ZhciksDQogICAgICAgICAgIHN1YnRpdGxlID0gIkJveHBsb3QgcGVyIGthdGVnb3JpIOKAlCB0aXRpayA9IG1lYW4iLA0KICAgICAgICAgICB4ID0gY2F0X3ZhciwgeSA9IG51bWVyaWNfdmFyKSArDQogICAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDEyKSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQogICAgcmV0dXJuKHApDQogIH0NCg0KICBmb3IgKG51bXZhciBpbiBjKCJUb3RhbFB1cmNoYXNlIiwgIk51bWJlck9mVmlzaXRzIikpIHsNCiAgICBpZiAoIShudW12YXIgJWluJSBuYW1lcyhkZikpKSBuZXh0DQogICAgZm9yIChjYXR2IGluIGNhdF9jb2xzKSB7DQogICAgICBwYm94IDwtIG1ha2VfYm94KG51bXZhciwgY2F0dikNCiAgICAgIGlmIChpcy5udWxsKHBib3gpKSBuZXh0DQogICAgICBwcmludChwYm94KSAgICMgdGFtcGlsIGRpIGRva3VtZW4gLyBQbG90cyBwYW5lDQogICAgfQ0KICB9DQp9DQoNCmBgYCANCg0KSW50ZXJwcmV0YXNpDQoNCjEuSGlzdG9ncmFtIG1lbnVuanVra2FuIGRpc3RyaWJ1c2kgZnJla3VlbnNpIGRhbiBiYWdhaW1hbmEgdWt1cmFuIHNlbnRyYWwgc2VsYXJhcyBkZW5nYW4ga29uc2VudHJhc2kgZGF0YS4NCg0KMi5Lb3Rhay1rb3RhayBtZW55b3JvdCBtZWRpYW4sIGt1YXJ0aWwsIGRhbiBrZWJlcmFkYWFuIG91dGxpZXIgZGFsYW0gZm9ybWF0IHlhbmcgcmluZ2thcy4NCg0KMy5LZXRpa2Egc3VhdHUgc2V0IGRhdGEgYmVyaXNpIG5pbGFpIGVrc3RyZW0gdGluZ2dpIGF0YXUgcmVuZGFoLCBkaXN0cmlidXNpbnlhIG1lbmphZGkgbWlyaW5nIHBvc2l0aWYgKG1pcmluZyBrZSBrYW5hbikgYXRhdSBtaXJpbmcgbmVnYXRpZiAobWlyaW5nIGtlIGtpcmkpDQoNCjQuaGlzdG9ncmFtIG1lbmdndW5na2Fwa2FuIGtlc2VsdXJ1aGFuIChkYW4gYmViZXJhcGEgbW9kZSksIGJlbnR1aw0Kc2VtZW50YXJhIGJveHBsb3QgbWVuZWthbmthbiBwZW55ZWJhcmFuIGRhbiBrZW1pcmluZ2FuIGRhdGEuDQoNCg==