diff --git a/vignettes/code_PAS_CHAOS.html b/vignettes/code_PAS_CHAOS.html
index e69de29..9c1815d 100644
--- a/vignettes/code_PAS_CHAOS.html
+++ b/vignettes/code_PAS_CHAOS.html
@@ -0,0 +1,2436 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
DLFCP and psoriasis dataset
+
PAS: Percentage of abnormal spots
+
CHAOS: spatial chaos score
+
+
+
+
library(tidyverse)
+
+
+
package 㤼㸱tidyverse㤼㸲 was built under R version 4.1.3package 㤼㸱ggplot2㤼㸲 was built under R version 4.1.3package 㤼㸱tibble㤼㸲 was built under R version 4.1.3package 㤼㸱tidyr㤼㸲 was built under R version 4.1.3package 㤼㸱readr㤼㸲 was built under R version 4.1.3package 㤼㸱purrr㤼㸲 was built under R version 4.1.3package 㤼㸱dplyr㤼㸲 was built under R version 4.1.3package 㤼㸱stringr㤼㸲 was built under R version 4.1.3package 㤼㸱forcats㤼㸲 was built under R version 4.1.3package 㤼㸱lubridate㤼㸲 was built under R version 4.1.3-- Attaching core tidyverse packages ------------------- tidyverse 2.0.0 --
+v dplyr 1.1.2 v readr 2.1.4
+v forcats 1.0.0 v stringr 1.5.0
+v ggplot2 3.4.2 v tibble 3.2.1
+v lubridate 1.9.2 v tidyr 1.3.0
+v purrr 1.0.1 -- Conflicts ------------------------------------- tidyverse_conflicts() --
+x dplyr::filter() masks stats::filter()
+x dplyr::lag() masks stats::lag()
+i Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errorsWarning messages:
+1: R graphics engine version 14 is not supported by this version of RStudio. The Plots tab will be disabled until a newer version of RStudio is installed.
+2: In do_once((if (is_R_CMD_check()) stop else warning)("The function xfun::isFALSE() will be deprecated in the future. Please ", :
+ The function xfun::isFALSE() will be deprecated in the future. Please consider using base::isFALSE(x) or identical(x, FALSE) instead.
+
+
+
library(parallel)
+
+
+#mac
+fx_CHAOS = function(clusterlabel, location){
+ # require(parallel)
+ matched_location=location
+ NAs = which(is.na(clusterlabel))
+ if(length(NAs>0)){
+ clusterlabel=clusterlabel[-NAs]
+ matched_location = matched_location[-NAs,]
+ }
+ matched_location = scale(matched_location)
+ dist_val = rep(0,length(unique(clusterlabel)))
+ count = 0
+ for(k in unique(clusterlabel)){
+ count = count + 1
+ location_cluster = matched_location[which(clusterlabel == k),]
+ if(length(location_cluster)==2){next}
+ #require(parallel)
+ results = mclapply(1:dim(location_cluster)[1], fx_1NN, location_in=location_cluster,mc.cores = 5) #This is the MC part
+ dist_val[count] = sum(unlist(results))
+ }
+ dist_val = na.omit(dist_val)
+ return(sum(dist_val)/length(clusterlabel))
+
+}
+
+
+#windows
+fx_PAS = function(clusterlabel, location){
+ # require(parallel)
+
+ matched_location=location
+ NAs = which(is.na(clusterlabel))
+ if(length(NAs>0)){
+ clusterlabel=clusterlabel[-NAs]
+ matched_location = matched_location[-NAs,]
+ }
+
+ results = mclapply(1:dim(matched_location)[1], fx_kNN, location_in=matched_location,k=10,cluster_in=clusterlabel, mc.cores = 5)
+ return(sum(unlist(results))/length(clusterlabel))
+}
+
+
+#mac
+fx_PAS = function(clusterlabel, location) {
+
+ plan(multisession, workers = 3) # sets the plan to use 5 cores for parallel processing
+
+ matched_location = location
+ NAs = which(is.na(clusterlabel))
+ if (length(NAs) > 0) {
+ clusterlabel = clusterlabel[-NAs]
+ matched_location = matched_location[-NAs, ]
+ }
+
+ results = future_lapply(1:dim(matched_location)[1], fx_kNN,
+ location_in = matched_location,
+ k = 10,
+ cluster_in = clusterlabel)
+
+ return(sum(unlist(results)) / length(clusterlabel))
+}
+
+
+library(future)
+
+
+
package 㤼㸱future㤼㸲 was built under R version 4.1.3
+
+
+
library(future.apply)
+
+
+#windows
+fx_CHAOS = function(clusterlabel, location){
+ # Set future plan to use 3 cores
+ plan(multisession, workers = 3)
+
+ matched_location = location
+ NAs = which(is.na(clusterlabel))
+
+ if(length(NAs) > 0){
+ clusterlabel = clusterlabel[-NAs]
+ matched_location = matched_location[-NAs,]
+ }
+
+ matched_location = scale(matched_location)
+ dist_val = rep(0, length(unique(clusterlabel)))
+ count = 0
+
+ for(k in unique(clusterlabel)){
+ count = count + 1
+ location_cluster = matched_location[which(clusterlabel == k),]
+
+ if(nrow(location_cluster) <= 2){next}
+
+ # Use future_lapply for parallelization
+ results = future_lapply(1:nrow(location_cluster), fx_1NN, location_in = location_cluster)
+
+ dist_val[count] = sum(unlist(results))
+ }
+
+ dist_val = na.omit(dist_val)
+
+ return(sum(dist_val) / length(clusterlabel))
+}
+
+
+
+library(pdist)
+
+
+
package 㤼㸱pdist㤼㸲 was built under R version 4.1.3
+
+
+
fx_1NN = function(i,location_in){
+ # library(pdist)
+ line_i = rep(0,dim(location_in)[1])
+ line_i = pdist(location_in[i,],location_in[-i,])@dist
+ return(min(line_i))
+}
+
+
+fx_kNN = function(i,location_in,k,cluster_in){
+ #library(pdist)
+ line_i = rep(0,dim(location_in)[1])
+ line_i = pdist(location_in[i,],location_in[-i,])@dist
+ ind = order(line_i)[1:k]
+ cluster_use = cluster_in[-i]
+ if(sum(cluster_use[ind] != cluster_in[i])>(k/2)){
+ return(1)
+ }else{
+ return(0)
+ }
+
+}
+
+
+
+
+
Clusters loading
+
+
+
+
location = read_csv('C:\\Users\\Juan\\Desktop\\temp_data\\151673\\spatial\\tissue_positions_list.csv')
+
+
+
New names:One or more parsing issues, call `problems()` on your data frame for
+details, e.g.:
+ dat <- vroom(...)
+ problems(dat)Rows: 4992 Columns: 7-- Column specification ---------------------------------------------------
+Delimiter: ","
+chr (1): ...1
+dbl (5): a, b, c, d, x
+lgl (1): y
+i Use `spec()` to retrieve the full column specification for this data.
+i Specify the column types or set `show_col_types = FALSE` to quiet this message.
+
+
+
location2 = readRDS('C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\DLPFC_spatial_3639cells_2cols.rds')
+dim(location2)
+
+
+
[1] 3639 2
+
+
+
dim(location)
+
+
+
[1] 4992 7
+
+
+
rownames(location) = location$...1
+
+
+
Setting row names on a tibble is deprecated.
+
+
+
GT = read_csv('C:\\Users\\Juan\\Desktop\\temp_data\\151673\\cluster_labels_151673.csv')
+
+
+
Rows: 3639 Columns: 22-- Column specification ---------------------------------------------------
+Delimiter: ","
+chr (2): key, ground_truth
+dbl (20): SpatialDE_PCA, SpatialDE_pool_PCA, HVG_PCA, pseudobulk_PCA, m...
+i Use `spec()` to retrieve the full column specification for this data.
+i Specify the column types or set `show_col_types = FALSE` to quiet this message.
+
+
+
stcca_c = readRDS('C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\stCCA_DLPFC_benchmark_clusters.rds')
+pca = readRDS('C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\PCA_DLPFC_benchmark_clusters.rds')
+
+# Get common row names
+common_rows <- intersect(rownames(location), rownames(location2))
+
+# Filter location2
+location2_filtered <- location2[common_rows, ]
+
+# Optionally, also filter location
+location_filtered <- location[common_rows, ]
+
+location2$thruth = GT$ground_truth
+
+
+
+
+
+
+
location2 %>% ggplot()+
+ geom_point(aes(x= imagecol ,y = imagerow ,color =thruth ))
+
+
+
+
+
+
+
+
+
+
# ground thruth
+fx_CHAOS(location2$thruth, location = location2[,c('imagecol','imagerow')])
+
+
+
[1] 0.06046773
+
+
+
+
+
+
+
#stcca
+# Iterate from 6 to 20, incrementing by 2 each time
+for (i in seq(6, 20, by=2)) {
+ # Generate the name of the source column in stcca_c
+ source_colname <- paste("stCCA CC dimension =", i)
+
+ # Generate the name of the new column to be created in location2
+ new_colname <- paste("stcca_", i, sep="")
+
+ # Assign the new column in location2 using values from stcca_c
+ location2[[new_colname]] <- stcca_c[[source_colname]]
+}
+
+#pca
+for (i in seq(6, 20, by=2)) {
+ # Generate the name of the source column in pca
+ source_colname <- paste0("PCA dimension=", i)
+
+ # Generate the name of the new column to be created in location2
+ new_colname <- paste("pca_", i, sep="")
+
+ # Assign the new column in location2 using values from pca
+ location2[[new_colname]] <- pca[[source_colname]]
+}
+
+#spaceflow
+for(i in seq(10, 30, by=5)) {
+ file_path <- paste0("C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\", i, "_res_0.3_domains.tsv")
+ # Read the TSV file into a data frame
+ spaceflow <- read.delim(file_path, header=TRUE, sep="\t")
+ # Add a new column to location2 using the label column from the read TSV
+ location2[[paste0("spaceflow", i)]] <- spaceflow$label
+}
+
+#stlearn
+
+for(i in seq(6, 20, by=2)) {
+ file_path <- paste0("C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\151673_", i, "comps_calc_cluster.csv")
+ # Read the CSV file into a data frame
+ comps_calc_cluster <- read.csv(file_path, header=TRUE)
+ # Add a new column to location2 using the label column from the read CSV
+ location2[[paste0("stLearn", i)]] <- comps_calc_cluster$X_pca_kmeans
+}
+
+
+
+
+
+
+
CHAOS DLFPC
+
+
+
+
cols_to_apply <- setdiff(names(location2), c('imagecol', 'imagerow'))
+
+results_dfchaos <- data.frame()
+
+results <- sapply(cols_to_apply, function(col) {
+ fx_CHAOS(location2[[col]], location = location2[, c('imagecol', 'imagerow')])
+})
+
+results_dfchaos <- as.data.frame(results)
+
+
+
+
+
+
+
results_dfchaos$Combinations = rownames(results_dfchaos)
+results_dfchaos
+
+
+
+
+
+
+
+
+
CHAOS stat
+
The lower, the less the chaos
+
+
+
+
ggplot(results_dfchaos, aes(x=Combinations, y=results)) +
+ geom_bar(stat="identity", position="dodge") +
+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
+ ggtitle("Output from fx_CHAOS") +
+ xlab("Variables") +
+ ylab("Values")
+
+
+
+
+
+
+
+
+
PAS DLFPC
+
+
+
+
cols_to_apply <- setdiff(names(location2), c('imagecol', 'imagerow'))
+
+results_dfpas <- data.frame()
+
+results <- sapply(cols_to_apply, function(col) {
+ fx_PAS(location2[[col]], location = location2[, c('imagecol', 'imagerow')])
+})
+
+results_dfpas <- as.data.frame(as.table(results))
+
+
+
+
+
+
+
results_dfpas
+
+
+
+
+
+
+
+
+
+
+
+
ggplot(results_dfpas, aes(x=Var1, y=Freq)) +
+ geom_bar(stat="identity", position="dodge") +
+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
+ ggtitle("Output from fx_PAS") +
+ xlab("Variables") +
+ ylab("Values")
+
+
+
+
+
+
+
+
+
+
results_dfchaos$dataset <- ifelse(grepl("^stcca_", results_dfchaos$Combinations), "stcca",
+ ifelse(grepl("^pca_", results_dfchaos$Combinations), "pca",
+ ifelse(grepl("^spaceflow", results_dfchaos$Combinations), "spaceflow",
+ ifelse(grepl("^stLearn", results_dfchaos$Combinations), "stLearn",
+ ifelse(grepl("^thruth", results_dfchaos$Combinations), "thruth", "Other")))))
+
+head(results_dfchaos)
+
+
+
+
+
+
+
+
+results_dfchaos_filtered <- results_dfchaos[results_dfchaos$dataset != "thruth", ]
+
+ggplot(results_dfchaos_filtered, aes(x = Combinations, y = results)) +
+ geom_bar(stat = "identity", position = "dodge") +
+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
+ ggtitle("Output from fx_CHAOS") +
+ xlab("Variables") +
+ ylab("Values") +
+ facet_wrap(~ dataset, scales = "free_x")
+
+
+
+
+
+
+
+
+
+
+
+results_dfpas$dataset <- ifelse(grepl("^stcca_", results_dfpas$Var1), "stcca",
+ ifelse(grepl("^pca_", results_dfpas$Var1), "pca",
+ ifelse(grepl("^spaceflow", results_dfpas$Var1), "spaceflow",
+ ifelse(grepl("^stLearn", results_dfpas$Var1), "stLearn",
+ ifelse(grepl("^thruth", results_dfpas$Var1), "thruth", "Other")))))
+
+# Filter out the 'thruth' rows
+results_dfpas_filtered <- results_dfpas[results_dfpas$dataset != "thruth", ]
+
+# Create a bar plot with ggplot2, with facet_wrap based on the 'dataset' column
+ggplot(results_dfpas_filtered, aes(x = Var1, y = Freq)) +
+ geom_bar(stat = "identity", position = "dodge") +
+ theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
+ ggtitle("Output from fx_PAS") +
+ xlab("Variables") +
+ ylab("Values") +
+ facet_wrap(~ dataset, scales = "free_x")
+
+
+
+
+
+
+
+
+
+
CHAOS boxplot
+
+
+
+
+
+
+ p = ggplot(results_dfchaos_filtered, aes(x = reorder(dataset, -results, FUN = mean), y = results, fill = dataset)) +
+ geom_boxplot(outlier.shape = NA, width = 0.35) +
+ geom_jitter(width = 0.1) +
+ theme_bw() +
+ theme(
+ legend.position = "none",
+ axis.line = element_line(),
+ plot.background = element_blank(),
+ panel.grid.major = element_blank(),
+ panel.grid.minor = element_blank(),
+ panel.border = element_blank(),
+ axis.text.x = element_text(size = 15, face = "bold", colour = "black"),
+ axis.text.y = element_text(size = 14, colour = "black"),
+ axis.title.y = element_text(size = 15, face = "bold")
+ ) +
+ scale_fill_manual(values = c("cornsilk1", "cornsilk2", "cornsilk3", "cornsilk4")) +
+ # scale_x_discrete(name = "", expand = c(0.5, 0)) +
+ # scale_y_continuous(limits = c(0, 0.3), breaks = seq(0, 0.3, by = 0.1)) +
+ ylab("CHAOS Score") +
+ labs(title = "CHAOS DLFCP")+
+ xlab('')
+
+p
+
+
+
+
+
+
+
+
+
PAS boxplot
+
+
+
+
+
+
+
+
+
+
+
# Save to a specific directory
+saveRDS(results_dfpas_filtered, "results_dfpas_filtered.rds")
+results_dfpas_filtered = readRDS("results_dfpas_filtered.rds")
+
+
+
+
+
+
Psoriasis dataset
+
+
+
+
coor_pp12 = readRDS('C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\Psoriasis_Coordinate_2023-07-14.RDS')
+
+cluster_pp12=readRDS('C:\\Users\\Juan\\Desktop\\all_clusters_dlfpc\\stCCA_psoriasis_cluster_label_0.35best.rds')
+
+all_pp12 = cbind(coor_pp12,cluster_pp12)
+
+
+
+
+
+
+
+
cols_to_apply <- setdiff(names(all_pp12), c('V5', 'V6'))
+
+results_dfpas <- data.frame()
+
+results <- sapply(cols_to_apply, function(col) {
+ fx_PAS(all_pp12[[col]], location = all_pp12[, c('V5', 'V6')])
+})
+
+results_dfpas <- as.data.frame(as.table(results))
+
+
+
+
+
+
+
results_dfpas
+
+
+
+
+
+
+
+
+results_dfpas %>%
+ ggplot(aes(x = Var1, y = Freq)) +
+ geom_point() + ylab('PAS score') + xlab('Resolutions')
+
+
+
+
+
+
+
+
+
+
cols_to_apply <- setdiff(names(all_pp12), c('V5', 'V6'))
+
+results_dfpas <- data.frame()
+
+results <- sapply(cols_to_apply, function(col) {
+ fx_CHAOS(all_pp12[[col]], location = all_pp12[, c('V5', 'V6')])
+})
+
+results_dfpas <- as.data.frame(as.table(results))
+
+
+
+
+
+
+
results_dfpas
+
+
+
+
+
+
+
+
+results_dfpas %>%
+ ggplot(aes(x = Var1, y = Freq)) +
+ geom_point() + ylab('CHAOS score') + xlab('Resolutions')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
LS0tDQp0aXRsZTogIk1lYXN1cmUgdGhlIGNvbnRpbnVpdHkgYW5kIHNtb290aCBvZiBvdXIgY2x1c3RlcmluZyBwZXJmb3JtYW5jZSBpbiB0aGUgc3BhdGlhbCBkb21haW4gdXNpbmcgUEFTIGFuZCBDSEFPUyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCkRMRkNQIGFuZCBwc29yaWFzaXMgZGF0YXNldA0KDQoNClBBUzogUGVyY2VudGFnZSBvZiBhYm5vcm1hbCBzcG90cw0KDQoNCkNIQU9TOiBzcGF0aWFsIGNoYW9zIHNjb3JlDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHBhcmFsbGVsKQ0KDQoNCiNtYWMgDQpmeF9DSEFPUyA9IGZ1bmN0aW9uKGNsdXN0ZXJsYWJlbCwgbG9jYXRpb24pew0KICAjIHJlcXVpcmUocGFyYWxsZWwpDQogIG1hdGNoZWRfbG9jYXRpb249bG9jYXRpb24NCiAgTkFzID0gd2hpY2goaXMubmEoY2x1c3RlcmxhYmVsKSkNCiAgaWYobGVuZ3RoKE5Bcz4wKSl7DQogICAgY2x1c3RlcmxhYmVsPWNsdXN0ZXJsYWJlbFstTkFzXQ0KICAgIG1hdGNoZWRfbG9jYXRpb24gPSBtYXRjaGVkX2xvY2F0aW9uWy1OQXMsXQ0KICB9DQogIG1hdGNoZWRfbG9jYXRpb24gPSBzY2FsZShtYXRjaGVkX2xvY2F0aW9uKQ0KICBkaXN0X3ZhbCA9IHJlcCgwLGxlbmd0aCh1bmlxdWUoY2x1c3RlcmxhYmVsKSkpDQogIGNvdW50ID0gMA0KICBmb3IoayBpbiB1bmlxdWUoY2x1c3RlcmxhYmVsKSl7DQogICAgY291bnQgPSBjb3VudCArIDENCiAgICBsb2NhdGlvbl9jbHVzdGVyID0gbWF0Y2hlZF9sb2NhdGlvblt3aGljaChjbHVzdGVybGFiZWwgPT0gayksXQ0KICAgIGlmKGxlbmd0aChsb2NhdGlvbl9jbHVzdGVyKT09Mil7bmV4dH0NCiAgICAjcmVxdWlyZShwYXJhbGxlbCkNCiAgICByZXN1bHRzID0gbWNsYXBwbHkoMTpkaW0obG9jYXRpb25fY2x1c3RlcilbMV0sIGZ4XzFOTiwgbG9jYXRpb25faW49bG9jYXRpb25fY2x1c3RlcixtYy5jb3JlcyA9IDUpICNUaGlzIGlzIHRoZSBNQyBwYXJ0DQogICAgZGlzdF92YWxbY291bnRdID0gc3VtKHVubGlzdChyZXN1bHRzKSkNCiAgfQ0KICBkaXN0X3ZhbCA9IG5hLm9taXQoZGlzdF92YWwpDQogIHJldHVybihzdW0oZGlzdF92YWwpL2xlbmd0aChjbHVzdGVybGFiZWwpKQ0KDQp9DQoNCg0KI3dpbmRvd3MNCmZ4X1BBUyA9IGZ1bmN0aW9uKGNsdXN0ZXJsYWJlbCwgbG9jYXRpb24pew0KICAjIHJlcXVpcmUocGFyYWxsZWwpDQoNCiAgbWF0Y2hlZF9sb2NhdGlvbj1sb2NhdGlvbg0KICBOQXMgPSB3aGljaChpcy5uYShjbHVzdGVybGFiZWwpKQ0KICBpZihsZW5ndGgoTkFzPjApKXsNCiAgICBjbHVzdGVybGFiZWw9Y2x1c3RlcmxhYmVsWy1OQXNdDQogICAgbWF0Y2hlZF9sb2NhdGlvbiA9IG1hdGNoZWRfbG9jYXRpb25bLU5BcyxdDQogIH0NCg0KICByZXN1bHRzID0gbWNsYXBwbHkoMTpkaW0obWF0Y2hlZF9sb2NhdGlvbilbMV0sIGZ4X2tOTiwgbG9jYXRpb25faW49bWF0Y2hlZF9sb2NhdGlvbixrPTEwLGNsdXN0ZXJfaW49Y2x1c3RlcmxhYmVsLCBtYy5jb3JlcyA9IDUpDQogIHJldHVybihzdW0odW5saXN0KHJlc3VsdHMpKS9sZW5ndGgoY2x1c3RlcmxhYmVsKSkNCn0NCg0KDQojbWFjDQpmeF9QQVMgPSBmdW5jdGlvbihjbHVzdGVybGFiZWwsIGxvY2F0aW9uKSB7DQogIA0KICBwbGFuKG11bHRpc2Vzc2lvbiwgd29ya2VycyA9IDMpICAjIHNldHMgdGhlIHBsYW4gdG8gdXNlIDUgY29yZXMgZm9yIHBhcmFsbGVsIHByb2Nlc3NpbmcNCiAgDQogIG1hdGNoZWRfbG9jYXRpb24gPSBsb2NhdGlvbg0KICBOQXMgPSB3aGljaChpcy5uYShjbHVzdGVybGFiZWwpKQ0KICBpZiAobGVuZ3RoKE5BcykgPiAwKSB7DQogICAgY2x1c3RlcmxhYmVsID0gY2x1c3RlcmxhYmVsWy1OQXNdDQogICAgbWF0Y2hlZF9sb2NhdGlvbiA9IG1hdGNoZWRfbG9jYXRpb25bLU5BcywgXQ0KICB9DQogIA0KICByZXN1bHRzID0gZnV0dXJlX2xhcHBseSgxOmRpbShtYXRjaGVkX2xvY2F0aW9uKVsxXSwgZnhfa05OLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb25faW4gPSBtYXRjaGVkX2xvY2F0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgayA9IDEwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9pbiA9IGNsdXN0ZXJsYWJlbCkNCiAgDQogIHJldHVybihzdW0odW5saXN0KHJlc3VsdHMpKSAvIGxlbmd0aChjbHVzdGVybGFiZWwpKQ0KfQ0KDQoNCmxpYnJhcnkoZnV0dXJlKQ0KbGlicmFyeShmdXR1cmUuYXBwbHkpDQoNCg0KI3dpbmRvd3MNCmZ4X0NIQU9TID0gZnVuY3Rpb24oY2x1c3RlcmxhYmVsLCBsb2NhdGlvbil7DQogICMgU2V0IGZ1dHVyZSBwbGFuIHRvIHVzZSAzIGNvcmVzDQogIHBsYW4obXVsdGlzZXNzaW9uLCB3b3JrZXJzID0gMykNCiAgDQogIG1hdGNoZWRfbG9jYXRpb24gPSBsb2NhdGlvbg0KICBOQXMgPSB3aGljaChpcy5uYShjbHVzdGVybGFiZWwpKQ0KICANCiAgaWYobGVuZ3RoKE5BcykgPiAwKXsNCiAgICBjbHVzdGVybGFiZWwgPSBjbHVzdGVybGFiZWxbLU5Bc10NCiAgICBtYXRjaGVkX2xvY2F0aW9uID0gbWF0Y2hlZF9sb2NhdGlvblstTkFzLF0NCiAgfQ0KICANCiAgbWF0Y2hlZF9sb2NhdGlvbiA9IHNjYWxlKG1hdGNoZWRfbG9jYXRpb24pDQogIGRpc3RfdmFsID0gcmVwKDAsIGxlbmd0aCh1bmlxdWUoY2x1c3RlcmxhYmVsKSkpDQogIGNvdW50ID0gMA0KICANCiAgZm9yKGsgaW4gdW5pcXVlKGNsdXN0ZXJsYWJlbCkpew0KICAgIGNvdW50ID0gY291bnQgKyAxDQogICAgbG9jYXRpb25fY2x1c3RlciA9IG1hdGNoZWRfbG9jYXRpb25bd2hpY2goY2x1c3RlcmxhYmVsID09IGspLF0NCiAgICANCiAgICBpZihucm93KGxvY2F0aW9uX2NsdXN0ZXIpIDw9IDIpe25leHR9DQogICAgDQogICAgIyBVc2UgZnV0dXJlX2xhcHBseSBmb3IgcGFyYWxsZWxpemF0aW9uDQogICAgcmVzdWx0cyA9IGZ1dHVyZV9sYXBwbHkoMTpucm93KGxvY2F0aW9uX2NsdXN0ZXIpLCBmeF8xTk4sIGxvY2F0aW9uX2luID0gbG9jYXRpb25fY2x1c3RlcikNCiAgICANCiAgICBkaXN0X3ZhbFtjb3VudF0gPSBzdW0odW5saXN0KHJlc3VsdHMpKQ0KICB9DQogIA0KICBkaXN0X3ZhbCA9IG5hLm9taXQoZGlzdF92YWwpDQogIA0KICByZXR1cm4oc3VtKGRpc3RfdmFsKSAvIGxlbmd0aChjbHVzdGVybGFiZWwpKQ0KfQ0KDQoNCg0KbGlicmFyeShwZGlzdCkNCg0KZnhfMU5OID0gZnVuY3Rpb24oaSxsb2NhdGlvbl9pbil7DQogICMgbGlicmFyeShwZGlzdCkNCiAgbGluZV9pID0gcmVwKDAsZGltKGxvY2F0aW9uX2luKVsxXSkNCiAgbGluZV9pID0gcGRpc3QobG9jYXRpb25faW5baSxdLGxvY2F0aW9uX2luWy1pLF0pQGRpc3QNCiAgcmV0dXJuKG1pbihsaW5lX2kpKQ0KfQ0KDQoNCmZ4X2tOTiA9IGZ1bmN0aW9uKGksbG9jYXRpb25faW4sayxjbHVzdGVyX2luKXsNCiAgI2xpYnJhcnkocGRpc3QpDQogIGxpbmVfaSA9IHJlcCgwLGRpbShsb2NhdGlvbl9pbilbMV0pDQogIGxpbmVfaSA9IHBkaXN0KGxvY2F0aW9uX2luW2ksXSxsb2NhdGlvbl9pblstaSxdKUBkaXN0DQogIGluZCA9IG9yZGVyKGxpbmVfaSlbMTprXQ0KICBjbHVzdGVyX3VzZSA9IGNsdXN0ZXJfaW5bLWldDQogIGlmKHN1bShjbHVzdGVyX3VzZVtpbmRdICE9IGNsdXN0ZXJfaW5baV0pPihrLzIpKXsNCiAgICByZXR1cm4oMSkNCiAgfWVsc2V7DQogICAgcmV0dXJuKDApDQogIH0NCg0KfQ0KYGBgDQoNCg0KIyMgQ2x1c3RlcnMgbG9hZGluZw0KDQpgYGB7cn0NCmxvY2F0aW9uID0gcmVhZF9jc3YoJ0M6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcdGVtcF9kYXRhXFwxNTE2NzNcXHNwYXRpYWxcXHRpc3N1ZV9wb3NpdGlvbnNfbGlzdC5jc3YnKQ0KbG9jYXRpb24yID0gcmVhZFJEUygnQzpcXFVzZXJzXFxKdWFuXFxEZXNrdG9wXFxhbGxfY2x1c3RlcnNfZGxmcGNcXERMUEZDX3NwYXRpYWxfMzYzOWNlbGxzXzJjb2xzLnJkcycpDQpkaW0obG9jYXRpb24yKQ0KZGltKGxvY2F0aW9uKQ0KDQpyb3duYW1lcyhsb2NhdGlvbikgPSBsb2NhdGlvbiQuLi4xDQoNCkdUID0gcmVhZF9jc3YoJ0M6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcdGVtcF9kYXRhXFwxNTE2NzNcXGNsdXN0ZXJfbGFiZWxzXzE1MTY3My5jc3YnKQ0Kc3RjY2FfYyA9IHJlYWRSRFMoJ0M6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcYWxsX2NsdXN0ZXJzX2RsZnBjXFxzdENDQV9ETFBGQ19iZW5jaG1hcmtfY2x1c3RlcnMucmRzJykNCnBjYSA9IHJlYWRSRFMoJ0M6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcYWxsX2NsdXN0ZXJzX2RsZnBjXFxQQ0FfRExQRkNfYmVuY2htYXJrX2NsdXN0ZXJzLnJkcycpDQoNCiMgR2V0IGNvbW1vbiByb3cgbmFtZXMNCmNvbW1vbl9yb3dzIDwtIGludGVyc2VjdChyb3duYW1lcyhsb2NhdGlvbiksIHJvd25hbWVzKGxvY2F0aW9uMikpDQoNCiMgRmlsdGVyIGxvY2F0aW9uMg0KbG9jYXRpb24yX2ZpbHRlcmVkIDwtIGxvY2F0aW9uMltjb21tb25fcm93cywgXQ0KDQojIE9wdGlvbmFsbHksIGFsc28gZmlsdGVyIGxvY2F0aW9uDQpsb2NhdGlvbl9maWx0ZXJlZCA8LSBsb2NhdGlvbltjb21tb25fcm93cywgXQ0KDQpsb2NhdGlvbjIkdGhydXRoID0gR1QkZ3JvdW5kX3RydXRoDQpgYGANCg0KDQpgYGB7cn0NCmxvY2F0aW9uMiAlPiUgZ2dwbG90KCkrDQogIGdlb21fcG9pbnQoYWVzKHg9IGltYWdlY29sICx5ID0gaW1hZ2Vyb3cgLGNvbG9yID10aHJ1dGggKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBncm91bmQgdGhydXRoDQpmeF9DSEFPUyhsb2NhdGlvbjIkdGhydXRoLCBsb2NhdGlvbiA9IGxvY2F0aW9uMlssYygnaW1hZ2Vjb2wnLCdpbWFnZXJvdycpXSkNCg0KYGBgDQoNCmBgYHtyfQ0KI3N0Y2NhDQojIEl0ZXJhdGUgZnJvbSA2IHRvIDIwLCBpbmNyZW1lbnRpbmcgYnkgMiBlYWNoIHRpbWUNCmZvciAoaSBpbiBzZXEoNiwgMjAsIGJ5PTIpKSB7DQogICMgR2VuZXJhdGUgdGhlIG5hbWUgb2YgdGhlIHNvdXJjZSBjb2x1bW4gaW4gc3RjY2FfYw0KICBzb3VyY2VfY29sbmFtZSA8LSBwYXN0ZSgic3RDQ0EgQ0MgZGltZW5zaW9uID0iLCBpKQ0KICANCiAgIyBHZW5lcmF0ZSB0aGUgbmFtZSBvZiB0aGUgbmV3IGNvbHVtbiB0byBiZSBjcmVhdGVkIGluIGxvY2F0aW9uMg0KICBuZXdfY29sbmFtZSA8LSBwYXN0ZSgic3RjY2FfIiwgaSwgc2VwPSIiKQ0KICANCiAgIyBBc3NpZ24gdGhlIG5ldyBjb2x1bW4gaW4gbG9jYXRpb24yIHVzaW5nIHZhbHVlcyBmcm9tIHN0Y2NhX2MNCiAgbG9jYXRpb24yW1tuZXdfY29sbmFtZV1dIDwtIHN0Y2NhX2NbW3NvdXJjZV9jb2xuYW1lXV0NCn0NCg0KI3BjYQ0KZm9yIChpIGluIHNlcSg2LCAyMCwgYnk9MikpIHsNCiAgIyBHZW5lcmF0ZSB0aGUgbmFtZSBvZiB0aGUgc291cmNlIGNvbHVtbiBpbiBwY2ENCiAgc291cmNlX2NvbG5hbWUgPC0gcGFzdGUwKCJQQ0EgZGltZW5zaW9uPSIsIGkpDQogIA0KICAjIEdlbmVyYXRlIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1uIHRvIGJlIGNyZWF0ZWQgaW4gbG9jYXRpb24yDQogIG5ld19jb2xuYW1lIDwtIHBhc3RlKCJwY2FfIiwgaSwgc2VwPSIiKQ0KICANCiAgIyBBc3NpZ24gdGhlIG5ldyBjb2x1bW4gaW4gbG9jYXRpb24yIHVzaW5nIHZhbHVlcyBmcm9tIHBjYQ0KICBsb2NhdGlvbjJbW25ld19jb2xuYW1lXV0gPC0gcGNhW1tzb3VyY2VfY29sbmFtZV1dDQp9DQoNCiNzcGFjZWZsb3cNCmZvcihpIGluIHNlcSgxMCwgMzAsIGJ5PTUpKSB7DQogIGZpbGVfcGF0aCA8LSBwYXN0ZTAoIkM6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcYWxsX2NsdXN0ZXJzX2RsZnBjXFwiLCBpLCAiX3Jlc18wLjNfZG9tYWlucy50c3YiKQ0KICAjIFJlYWQgdGhlIFRTViBmaWxlIGludG8gYSBkYXRhIGZyYW1lDQogIHNwYWNlZmxvdyA8LSByZWFkLmRlbGltKGZpbGVfcGF0aCwgaGVhZGVyPVRSVUUsIHNlcD0iXHQiKQ0KICAjIEFkZCBhIG5ldyBjb2x1bW4gdG8gbG9jYXRpb24yIHVzaW5nIHRoZSBsYWJlbCBjb2x1bW4gZnJvbSB0aGUgcmVhZCBUU1YNCiAgbG9jYXRpb24yW1twYXN0ZTAoInNwYWNlZmxvdyIsIGkpXV0gPC0gc3BhY2VmbG93JGxhYmVsDQp9DQoNCiNzdGxlYXJuDQoNCmZvcihpIGluIHNlcSg2LCAyMCwgYnk9MikpIHsNCiAgZmlsZV9wYXRoIDwtIHBhc3RlMCgiQzpcXFVzZXJzXFxKdWFuXFxEZXNrdG9wXFxhbGxfY2x1c3RlcnNfZGxmcGNcXDE1MTY3M18iLCBpLCAiY29tcHNfY2FsY19jbHVzdGVyLmNzdiIpDQogICMgUmVhZCB0aGUgQ1NWIGZpbGUgaW50byBhIGRhdGEgZnJhbWUNCiAgY29tcHNfY2FsY19jbHVzdGVyIDwtIHJlYWQuY3N2KGZpbGVfcGF0aCwgaGVhZGVyPVRSVUUpDQogICMgQWRkIGEgbmV3IGNvbHVtbiB0byBsb2NhdGlvbjIgdXNpbmcgdGhlIGxhYmVsIGNvbHVtbiBmcm9tIHRoZSByZWFkIENTVg0KICBsb2NhdGlvbjJbW3Bhc3RlMCgic3RMZWFybiIsIGkpXV0gPC0gY29tcHNfY2FsY19jbHVzdGVyJFhfcGNhX2ttZWFucw0KfQ0KDQpgYGANCg0KDQojIyBDSEFPUyBETEZQQyANCg0KYGBge3J9DQpjb2xzX3RvX2FwcGx5IDwtIHNldGRpZmYobmFtZXMobG9jYXRpb24yKSwgYygnaW1hZ2Vjb2wnLCAnaW1hZ2Vyb3cnKSkNCg0KcmVzdWx0c19kZmNoYW9zIDwtIGRhdGEuZnJhbWUoKQ0KDQpyZXN1bHRzIDwtIHNhcHBseShjb2xzX3RvX2FwcGx5LCBmdW5jdGlvbihjb2wpIHsNCiAgZnhfQ0hBT1MobG9jYXRpb24yW1tjb2xdXSwgbG9jYXRpb24gPSBsb2NhdGlvbjJbLCBjKCdpbWFnZWNvbCcsICdpbWFnZXJvdycpXSkNCn0pDQoNCnJlc3VsdHNfZGZjaGFvcyA8LSBhcy5kYXRhLmZyYW1lKHJlc3VsdHMpDQpgYGANCg0KYGBge3J9DQpyZXN1bHRzX2RmY2hhb3MkQ29tYmluYXRpb25zID0gcm93bmFtZXMocmVzdWx0c19kZmNoYW9zKQ0KcmVzdWx0c19kZmNoYW9zDQpgYGANCg0KQ0hBT1Mgc3RhdA0KDQpUaGUgbG93ZXIsIHRoZSBsZXNzIHRoZSBjaGFvcw0KDQpgYGB7cn0NCmdncGxvdChyZXN1bHRzX2RmY2hhb3MsIGFlcyh4PUNvbWJpbmF0aW9ucywgeT1yZXN1bHRzKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJkb2RnZSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKw0KICBnZ3RpdGxlKCJPdXRwdXQgZnJvbSBmeF9DSEFPUyIpICsNCiAgeGxhYigiVmFyaWFibGVzIikgKw0KICB5bGFiKCJWYWx1ZXMiKQ0KYGBgDQoNCg0KDQoNCg0KIyMgUEFTIERMRlBDDQoNCmBgYHtyfQ0KY29sc190b19hcHBseSA8LSBzZXRkaWZmKG5hbWVzKGxvY2F0aW9uMiksIGMoJ2ltYWdlY29sJywgJ2ltYWdlcm93JykpDQoNCnJlc3VsdHNfZGZwYXMgPC0gZGF0YS5mcmFtZSgpDQoNCnJlc3VsdHMgPC0gc2FwcGx5KGNvbHNfdG9fYXBwbHksIGZ1bmN0aW9uKGNvbCkgew0KICBmeF9QQVMobG9jYXRpb24yW1tjb2xdXSwgbG9jYXRpb24gPSBsb2NhdGlvbjJbLCBjKCdpbWFnZWNvbCcsICdpbWFnZXJvdycpXSkNCn0pDQoNCnJlc3VsdHNfZGZwYXMgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShyZXN1bHRzKSkNCmBgYA0KDQpgYGB7cn0NCnJlc3VsdHNfZGZwYXMNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChyZXN1bHRzX2RmcGFzLCBhZXMoeD1WYXIxLCB5PUZyZXEpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImRvZGdlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIGdndGl0bGUoIk91dHB1dCBmcm9tIGZ4X1BBUyIpICsNCiAgeGxhYigiVmFyaWFibGVzIikgKw0KICB5bGFiKCJWYWx1ZXMiKQ0KYGBgDQoNCg0KDQoNCg0KYGBge3J9DQpyZXN1bHRzX2RmY2hhb3MkZGF0YXNldCA8LSBpZmVsc2UoZ3JlcGwoIl5zdGNjYV8iLCByZXN1bHRzX2RmY2hhb3MkQ29tYmluYXRpb25zKSwgInN0Y2NhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiXnBjYV8iLCByZXN1bHRzX2RmY2hhb3MkQ29tYmluYXRpb25zKSwgInBjYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIl5zcGFjZWZsb3ciLCByZXN1bHRzX2RmY2hhb3MkQ29tYmluYXRpb25zKSwgInNwYWNlZmxvdyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIl5zdExlYXJuIiwgcmVzdWx0c19kZmNoYW9zJENvbWJpbmF0aW9ucyksICJzdExlYXJuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiXnRocnV0aCIsIHJlc3VsdHNfZGZjaGFvcyRDb21iaW5hdGlvbnMpLCAidGhydXRoIiwgIk90aGVyIikpKSkpDQoNCmhlYWQocmVzdWx0c19kZmNoYW9zKQ0KDQpyZXN1bHRzX2RmY2hhb3NfZmlsdGVyZWQgPC0gcmVzdWx0c19kZmNoYW9zW3Jlc3VsdHNfZGZjaGFvcyRkYXRhc2V0ICE9ICJ0aHJ1dGgiLCBdDQoNCmdncGxvdChyZXN1bHRzX2RmY2hhb3NfZmlsdGVyZWQsIGFlcyh4ID0gQ29tYmluYXRpb25zLCB5ID0gcmVzdWx0cykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKSArDQogIGdndGl0bGUoIk91dHB1dCBmcm9tIGZ4X0NIQU9TIikgKw0KICB4bGFiKCJWYXJpYWJsZXMiKSArDQogIHlsYWIoIlZhbHVlcyIpICsNCiAgZmFjZXRfd3JhcCh+IGRhdGFzZXQsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQpyZXN1bHRzX2RmcGFzJGRhdGFzZXQgPC0gaWZlbHNlKGdyZXBsKCJec3RjY2FfIiwgcmVzdWx0c19kZnBhcyRWYXIxKSwgInN0Y2NhIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJecGNhXyIsIHJlc3VsdHNfZGZwYXMkVmFyMSksICJwY2EiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIl5zcGFjZWZsb3ciLCByZXN1bHRzX2RmcGFzJFZhcjEpLCAic3BhY2VmbG93IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJec3RMZWFybiIsIHJlc3VsdHNfZGZwYXMkVmFyMSksICJzdExlYXJuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJedGhydXRoIiwgcmVzdWx0c19kZnBhcyRWYXIxKSwgInRocnV0aCIsICJPdGhlciIpKSkpKQ0KDQojIEZpbHRlciBvdXQgdGhlICd0aHJ1dGgnIHJvd3MNCnJlc3VsdHNfZGZwYXNfZmlsdGVyZWQgPC0gcmVzdWx0c19kZnBhc1tyZXN1bHRzX2RmcGFzJGRhdGFzZXQgIT0gInRocnV0aCIsIF0NCg0KIyBDcmVhdGUgYSBiYXIgcGxvdCB3aXRoIGdncGxvdDIsIHdpdGggZmFjZXRfd3JhcCBiYXNlZCBvbiB0aGUgJ2RhdGFzZXQnIGNvbHVtbg0KZ2dwbG90KHJlc3VsdHNfZGZwYXNfZmlsdGVyZWQsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsNCiAgZ2d0aXRsZSgiT3V0cHV0IGZyb20gZnhfUEFTIikgKw0KICB4bGFiKCJWYXJpYWJsZXMiKSArDQogIHlsYWIoIlZhbHVlcyIpICsNCiAgZmFjZXRfd3JhcCh+IGRhdGFzZXQsIHNjYWxlcyA9ICJmcmVlX3giKQ0KDQpgYGANCg0KIyMgQ0hBT1MgYm94cGxvdA0KDQpgYGB7cn0NCg0KDQoNCiBwID0gZ2dwbG90KHJlc3VsdHNfZGZjaGFvc19maWx0ZXJlZCwgYWVzKHggPSByZW9yZGVyKGRhdGFzZXQsIC1yZXN1bHRzLCBGVU4gPSBtZWFuKSwgeSA9IHJlc3VsdHMsIGZpbGwgPSBkYXRhc2V0KSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCB3aWR0aCA9IDAuMzUpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoKSwNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIGZhY2UgPSAiYm9sZCIsIGNvbG91ciA9ICJibGFjayIpLA0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3VyID0gImJsYWNrIiksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFjZSA9ICJib2xkIikNCiAgKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImNvcm5zaWxrMSIsICJjb3Juc2lsazIiLCAiY29ybnNpbGszIiwgImNvcm5zaWxrNCIpKSArDQogIyBzY2FsZV94X2Rpc2NyZXRlKG5hbWUgPSAiIiwgZXhwYW5kID0gYygwLjUsIDApKSArDQogIyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAwLjMpLCBicmVha3MgPSBzZXEoMCwgMC4zLCBieSA9IDAuMSkpICsgDQogIHlsYWIoIkNIQU9TIFNjb3JlIikgKw0KICBsYWJzKHRpdGxlID0gIkNIQU9TIERMRkNQIikrDQogIHhsYWIoJycpDQoNCnANCmBgYA0KDQoNCg0KIyMgUEFTIGJveHBsb3QNCg0KYGBge3J9DQoNCm5fY2F0ZWdvcmllcyA8LSBsZW5ndGgodW5pcXVlKHJlc3VsdHNfZGZwYXNfZmlsdGVyZWQkVmFyMSkpDQoNCg0KY29ybnNpbGtfc2hhZGVzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiY29ybnNpbGs0IiwgImNvcm5zaWxrMSIpKShuX2NhdGVnb3JpZXMpDQoNCg0KIHAgPSBnZ3Bsb3QocmVzdWx0c19kZnBhc19maWx0ZXJlZCwgYWVzKHggPSByZW9yZGVyKGRhdGFzZXQsIC1GcmVxLCBGVU4gPSBtZWFuKSwgeSA9IEZyZXEsIGZpbGwgPSBkYXRhc2V0KSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BLCB3aWR0aCA9IDAuMzUpICsNCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjEpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoKSwNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUsIGZhY2UgPSAiYm9sZCIsIGNvbG91ciA9ICJibGFjayIpLA0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgY29sb3VyID0gImJsYWNrIiksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFjZSA9ICJib2xkIikNCiAgKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImNvcm5zaWxrMSIsICJjb3Juc2lsazIiLCAiY29ybnNpbGszIiwgImNvcm5zaWxrNCIpKSArDQogIHNjYWxlX3hfZGlzY3JldGUobmFtZSA9ICIiLCBleHBhbmQgPSBjKDAuNSwgMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMC4zKSwgYnJlYWtzID0gc2VxKDAsIDAuMywgYnkgPSAwLjEpKSArIA0KICB5bGFiKCJQQVMgU2NvcmUiKSArDQogIGxhYnModGl0bGUgPSAiUEFTIikNCg0KcA0Kc2V0d2QoJ0M6XFxVc2Vyc1xcSnVhblxcRGVza3RvcFxcYWxsX2NsdXN0ZXJzX2RsZnBjJykNCmdnc2F2ZSgiUEFTX3Njb3JlX2RhdGFzZXRzXzktNS0yMDIzLnBkZiIsIHBsb3QgPSBwLCB3aWR0aCA9IDExLCBoZWlnaHQgPSAxMCkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBTYXZlIHRvIGEgc3BlY2lmaWMgZGlyZWN0b3J5DQpzYXZlUkRTKHJlc3VsdHNfZGZwYXNfZmlsdGVyZWQsICJyZXN1bHRzX2RmcGFzX2ZpbHRlcmVkLnJkcyIpDQpyZXN1bHRzX2RmcGFzX2ZpbHRlcmVkID0gcmVhZFJEUygicmVzdWx0c19kZnBhc19maWx0ZXJlZC5yZHMiKQ0KYGBgDQoNCg0KIyMgUHNvcmlhc2lzIGRhdGFzZXQNCg0KYGBge3J9DQpjb29yX3BwMTIgPSByZWFkUkRTKCdDOlxcVXNlcnNcXEp1YW5cXERlc2t0b3BcXGFsbF9jbHVzdGVyc19kbGZwY1xcUHNvcmlhc2lzX0Nvb3JkaW5hdGVfMjAyMy0wNy0xNC5SRFMnKQ0KDQpjbHVzdGVyX3BwMTI9cmVhZFJEUygnQzpcXFVzZXJzXFxKdWFuXFxEZXNrdG9wXFxhbGxfY2x1c3RlcnNfZGxmcGNcXHN0Q0NBX3Bzb3JpYXNpc19jbHVzdGVyX2xhYmVsXzAuMzViZXN0LnJkcycpDQoNCmFsbF9wcDEyID0gY2JpbmQoY29vcl9wcDEyLGNsdXN0ZXJfcHAxMikNCg0KYGBgDQoNCmBgYHtyfQ0KY29sc190b19hcHBseSA8LSBzZXRkaWZmKG5hbWVzKGFsbF9wcDEyKSwgYygnVjUnLCAnVjYnKSkNCg0KcmVzdWx0c19kZnBhcyA8LSBkYXRhLmZyYW1lKCkNCg0KcmVzdWx0cyA8LSBzYXBwbHkoY29sc190b19hcHBseSwgZnVuY3Rpb24oY29sKSB7DQogIGZ4X1BBUyhhbGxfcHAxMltbY29sXV0sIGxvY2F0aW9uID0gYWxsX3BwMTJbLCBjKCdWNScsICdWNicpXSkNCn0pDQoNCnJlc3VsdHNfZGZwYXMgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShyZXN1bHRzKSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzdWx0c19kZnBhcw0KDQpyZXN1bHRzX2RmcGFzICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBWYXIxLCB5ID0gRnJlcSkpICsNCiAgICBnZW9tX3BvaW50KCkgKyB5bGFiKCdQQVMgc2NvcmUnKSArIHhsYWIoJ1Jlc29sdXRpb25zJykNCmBgYA0KDQpgYGB7cn0NCmNvbHNfdG9fYXBwbHkgPC0gc2V0ZGlmZihuYW1lcyhhbGxfcHAxMiksIGMoJ1Y1JywgJ1Y2JykpDQoNCnJlc3VsdHNfZGZwYXMgPC0gZGF0YS5mcmFtZSgpDQoNCnJlc3VsdHMgPC0gc2FwcGx5KGNvbHNfdG9fYXBwbHksIGZ1bmN0aW9uKGNvbCkgew0KICBmeF9DSEFPUyhhbGxfcHAxMltbY29sXV0sIGxvY2F0aW9uID0gYWxsX3BwMTJbLCBjKCdWNScsICdWNicpXSkNCn0pDQoNCnJlc3VsdHNfZGZwYXMgPC0gYXMuZGF0YS5mcmFtZShhcy50YWJsZShyZXN1bHRzKSkNCmBgYA0KDQpgYGB7cn0NCnJlc3VsdHNfZGZwYXMNCg0KcmVzdWx0c19kZnBhcyAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEpKSArDQogICAgZ2VvbV9wb2ludCgpICsgeWxhYignQ0hBT1Mgc2NvcmUnKSArIHhsYWIoJ1Jlc29sdXRpb25zJykNCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwbG90KHJlc3VsdHNfZGZwYXMsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsNCiAgZ2d0aXRsZSgiT3V0cHV0IGZyb20gZnhfUEFTIikgKw0KICB4bGFiKCJWYXJpYWJsZXMiKSArDQogIHlsYWIoIlZhbHVlcyIpICsNCiAjIGZhY2V0X3dyYXAofiBkYXRhc2V0LCBzY2FsZXMgPSAiZnJlZV94IikNCmBgYA0KDQo=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+