
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.
Mean
Rata-rata diperoleh dengan membagi jumlah semua nilai data dengan
jumlah total observasi. Nilai ini cocok untuk tipe data interval dan
rasio.
Modus
Modus adalah nilai yang paling sering muncul dalam suatu dataset.
Modus dapat digunakan untuk data nominal, ordinal, interval, atau
rasio.
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:
Histogram menunjukkan distribusi frekuensi dan bagaimana ukuran
sentral selaras dengan konsentrasi data.
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:
Simetris dan Tanpa Outlier ketika data terdistribusi secara
merata di sekitar pusat.
Nilai Ekstrem (Miring) ketika outlier menarik nilai rata-rata ke
satu arah.
Variabel Kategorikal - ketika data mewakili kelompok atau kelas
yang berbeda.
Lebih Dari Satu Modus ketika data memiliki beberapa puncak atau
pusat konsentrasi.
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.
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.
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
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:
histogram mengungkapkan keseluruhan (dan beberapa mode),
bentuk
sementara boxplot menekankan penyebaran dan kemiringan
data.
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>
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)
}

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==