Metadata
db <- src_sqlite(db_path, create = FALSE)
samples <- collect(tbl(db, "samples"))
read_mutsig_output <- function(dir, sample_table, external = FALSE) {
prop_table_path <- Sys.glob(file.path(dir, "*_props.tsv"))
sigs_path <- Sys.glob(file.path(dir, "../plots/*_multipanel.pdf"))
prop_table <- fread(prop_table_path)
sample_cols <- colnames(prop_table)[colnames(prop_table) != "signature"]
ith_cols <- str_detect(sample_cols, "patient")
patients <- str_extract(sample_cols[ith_cols], "(?<=patient_?)[0-9]+")
site_ids <- str_replace_all(sample_cols[ith_cols], "patient_?[0-9]+_", "")
FROM <- c("rectum_site_1", "rectum_site_2", "rectum_site_3", "rectum_site_4",
"pelvis_site_1", "pelvis_site_2", "cul_de_sac_site_1")
TO <- c("rectum_site_1", "rectum_site_1", "rectum_site_2", "rectum_site_2",
"pelvis_site_1", "pelvis_site_1", "cul-de-sac_site_1")
site_ids_fixed <- mapvalues(site_ids, from = FROM, to = TO)
df <- data.frame(patient_id = patients, site_id = site_ids_fixed, old_id = sample_cols[ith_cols])
if (length(df[with(df, patient_id == "11" & site_id == "left_ovary_site_2"),
]$site_id) > 0) {
df[with(df, patient_id == "11" & site_id == "left_ovary_site_2"), ]$site_id <- "left_ovary_site_3"
}
sample_subset <- unique(subset(sample_table, select = c("condensed_id",
"patient_id", "site_id")))
df <- plyr::join(df, sample_subset)
if (nrow(df) > 0) {
df$is_ancestral <- 0
df$is_ancestral[df$site_id == "ancestral"] <- 1
}
# colnames(prop_table)[ith_cols] <- make.unique(df$condensed_id)
prop_final <- melt(prop_table, id.vars = c("signature"), measure.vars = sample_cols,
variable.name = "old_id", value.name = "proportion")
if (external) {
prop_final_merged <- plyr::join(df, prop_final, by = c("old_id"), type = "full")
} else {
prop_final_merged <- plyr::join(df, prop_final, by = c("old_id"), type = "inner")
}
prop_final_merged <- prop_final_merged %>% mutate(new_id = ifelse(site_id %in%
c("ancestral", "residual"), yes = paste(patient_id, site_id, sep = "_"),
no = condensed_id))
na_idx <- is.na(prop_final_merged$new_id)
prop_final_merged$new_id[na_idx] <- as.character(prop_final_merged$old_id[na_idx])
result <- list(prop = prop_final_merged, sigplot = sigs_path)
return(result)
}
sum_total_variant_counts <- function(variant_sample) {
variant_sample %>% group_by(condensed_id, patient_id) %>% summarise(snv_count = sum(snv_count),
bkpt_count = sum(bkpt_count))
}
compute_mutsig_counts <- function(df) {
df <- df %>% mutate(is_sv = as.numeric(str_detect(signature, "^SV")), sigcount = ifelse(as.character(site_id) ==
"ancestral", yes = ifelse(is_sv, yes = ancestral_bkpt * proportion,
no = ancestral_snv * proportion), no = ifelse(is_sv, yes = bkpt_count *
proportion, no = snv_count * proportion)))
return(df)
}
create_mutsig_matrix <- function(df, col = "proportion") {
mat <- acast(df, new_id ~ signature, fun.aggregate = mean, value.var = col)
return(mat)
}
variant_res <- summarize_variant_counts(master_variant_file, master_breakpoint_file,
db_path)
Read 5.9% of 682516 rows
Read 682516 rows and 9 (of 9) columns from 0.026 GB file in 00:00:03
variant_sample <- variant_res$sample
variant_patient <- variant_res$patient
variant_sample_sum <- sum_total_variant_counts(variant_sample)
ihc_table <- fread(ihc_table_path)
ihc_table_subset <- subset(ihc_table, select = c("condensed_id", tiltypes))
ihc_table_slide <- fread(ihc_table_slide_path)
Sample level
Results
props_sample <- read_mutsig_output(mmctm_sample_result_dir, samples)
props_sample_prop <- subset(props_sample$prop, patient_id != "11")
props_sample_mat <- create_mutsig_matrix(props_sample_prop, col = "proportion")
props_sample_mat_scaled <- clip_values(scale(props_sample_mat), 2, -2)
sample_heat <- pheatmap(props_sample_mat_scaled, fontsize_row = 5, clustering_distance_rows = "correlation",
clustering_method = "ward.D2")
props_sample_merged <- Reduce(function(x, y) plyr::join(x, y), list(props_sample_prop,
variant_sample_sum, variant_patient, ihc_table_subset))
props_sample_merged <- compute_mutsig_counts(props_sample_merged)
COL <- "E_CD8_density"
measure <- "sigcount"
pvals <- setNames(ddply(props_sample_merged, .(signature), function(x) {
df <- as.data.frame(x)
df <- df[!is.na(df[, COL]), ]
corres <- summary(lmer(as.formula(paste0(measure, " ~ ", COL, " + (1|patient_id)")),
df))
pval <- unname(corres$coefficients[, 5][2])
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("signature", "p.value"))
ggplot(props_sample_merged, aes_string(x = COL, y = measure)) + geom_point(aes(colour = patient_id)) +
scale_colour_manual(values = pal_patient) + facet_wrap(~signature, scales = "free") +
geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1,
vjust = 1.5, size = 3, parse = TRUE) + theme_bw()
# x <- Reduce(function(x,y) merge(x,y, by=c('condensed_id')),
# list(rownames_to_column(as.data.frame(t(prop_matrix)), var =
# 'condensed_id'), ihc_table_subset)) x$patient_id <- map_id(x$condensed_id,
# from = 'condensed_id', to='patient_id', db_path) x <- subset(x, patient_id
# != '11') cols <- colnames(x)[!colnames(x) %in% c('condensed_id',
# 'patient_id')] rmat <- matrix(nrow=length(cols),ncol=length(cols)) pmat <-
# matrix(nrow=length(cols),ncol=length(cols)) for (i in 1:length(cols)) {
# for (j in i:length(cols)) { if (i != j) { col1 <- cols[i] col2 <- cols[j]
# formula <- paste0('`', col2, '`', '~', '`', col1, '`', '+ (1|patient_id)',
# sep=' ') res <- summary(lmer(formula, x)) pval <- tryCatch({
# unname(res$coefficients[,5][2]) }, error = function(e) { NA }) r <-
# unname(res$coefficients[,1][2]) rmat[i,j] <- rmat[j,i] <- r pmat[i,j] <-
# pmat[j,i] <- pval } else { rmat[i,j] <- 1 pmat[i,j] <- NA } } }
# resmat <- log10(pmat)*-sign(rmat) resmat[resmat == Inf] <- NA
# rownames(resmat) <- colnames(resmat) <- cols pheatmap(resmat,
# display_numbers = signif(pmat, 3), cluster_rows = FALSE, cluster_cols =
# FALSE, fontsize = 5)
# xmat <- x %>% select(-one_of('condensed_id', 'patient_id')) cormat <-
# corr.test(xmat, method='spearman', adjust='fdr') pheatmap(cormat$r,
# display_numbers = signif(cormat$p, 3), fontsize = 5)
Correlation matrices
ihc_mat <- as.data.frame(ihc_table_subset %>% dplyr::select(-condensed_id))
rownames(ihc_mat) <- ihc_table_subset$condensed_id
# TOTAL_TIL_COLS <- c('T_CD8_density', 'T_CD4_density', 'T_CD20_density',
# 'T_Plasma_density') ihc_mat <- subset(ihc_mat, select=TOTAL_TIL_COLS)
compute_ihc_mutsig_cor <- function(ihc_mat, mutsig_mat, patient_summary = FALSE,
ancestral = FALSE) {
if (patient_summary) {
ihc_mat <- rownames_to_column(as.data.frame(ihc_mat), "id")
mutsig_mat <- rownames_to_column(as.data.frame(mutsig_mat), "id")
if (ancestral) {
mutsig_mat <- subset(mutsig_mat, str_detect(id, "ancestral"))
} else {
mutsig_mat <- subset(mutsig_mat, !str_detect(id, "ancestral"))
}
ihc_mat$patient_id <- str_extract(ihc_mat$id, "^[0-9]+")
mutsig_mat$patient_id <- str_extract(mutsig_mat$id, "^[0-9]+")
ihc_mat <- ihc_mat %>% dplyr::select(-id) %>% group_by(patient_id) %>%
summarise_each(funs(mean(., na.rm = TRUE))) %>% column_to_rownames(var = "patient_id")
mutsig_mat <- mutsig_mat %>% dplyr::select(-id) %>% group_by(patient_id) %>%
summarise_each(funs(mean(., na.rm = TRUE))) %>% column_to_rownames(var = "patient_id")
ihc_mat <- as.matrix(ihc_mat)
mutsig_mat <- as.matrix(mutsig_mat)
}
intersect_samples <- intersect(rownames(mutsig_mat), rownames(ihc_mat))
mutsig <- mutsig_mat[intersect_samples, , drop = FALSE]
ihc <- ihc_mat[intersect_samples, , drop = FALSE]
pmat <- matrix(nrow = ncol(mutsig), ncol = ncol(ihc))
rmat <- matrix(nrow = ncol(mutsig), ncol = ncol(ihc))
rownames(pmat) <- rownames(rmat) <- colnames(mutsig)
colnames(pmat) <- colnames(rmat) <- colnames(ihc)
for (i in 1:ncol(mutsig)) {
for (j in 1:ncol(ihc)) {
corres <- cor.test(mutsig[, i], ihc[, j], method = "spearman")
pmat[i, j] <- corres$p.value
rmat[i, j] <- corres$estimate
}
}
return(list(p = pmat, r = rmat))
}
For adjusted p-values just run p.adjust.mat
on the p-value labels.
Sample-level
ihc_sample_cor <- compute_ihc_mutsig_cor(ihc_mat, props_sample_mat)
pheatmap(ihc_sample_cor$r, display_numbers = signif(ihc_sample_cor$p, 3))
ihc_sample_cor_summary <- compute_ihc_mutsig_cor(ihc_mat, props_sample_mat,
patient_summary = TRUE)
pheatmap(ihc_sample_cor_summary$r, display_numbers = signif(ihc_sample_cor_summary$p,
3))
Ancestral-descendant level
ihc_ad_cor <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat)
pheatmap(ihc_ad_cor$r, display_numbers = signif(ihc_ad_cor$p, 3))
ihc_ad_cor_summary <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat, patient_summary = TRUE)
pheatmap(ihc_ad_cor_summary$r, display_numbers = signif(ihc_ad_cor_summary$p,
3))
ihc_ad_cor_summary_ancestral <- compute_ihc_mutsig_cor(ihc_mat, props_ad_mat,
patient_summary = TRUE, ancestral = TRUE)
pheatmap(ihc_ad_cor_summary_ancestral$r, display_numbers = signif(ihc_ad_cor_summary_ancestral$p,
3))
Note: p-values shown are uncorrected. Patient-summarized correlations are insignificant after FDR correction.
Cluster-level analysis
Finding correlations at the level of individual signatures can be difficult. Even moreso because some published signatures are combinations of these signatures – e.g. SV-3 and SV-6 are both foldback signatures in the ancestral-descendant analysis.
Hence, we can look at the level of clusters from our heatmaps.
make_cluster_frame <- function(clusters) {
clusts <- rownames_to_column(as.data.frame(clusters), "new_id")
colnames(clusts)[2] <- "cluster"
clusts$cluster <- factor(clusts$cluster)
return(clusts)
}
NCLUST <- 2
selected_cols <- c("new_id", "condensed_id", "patient_id", "old_id", "cluster",
tiltypes)
Sample-level
clusters <- cutree(sample_heat$tree_row, NCLUST)
sample_clusts <- make_cluster_frame(clusters)
props_sample_merged_clusts <- join(props_sample_merged, sample_clusts)
sample_df <- unique(subset(props_sample_merged_clusts, select = selected_cols))
sample_df_melted <- melt(sample_df, id.vars = colnames(sample_df)[!colnames(sample_df) %in%
tiltypes], measure.vars = tiltypes, variable.name = "tiltype", value.name = "density")
pvals <- setNames(ddply(sample_df_melted, .(tiltype), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(density ~ cluster, df)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("tiltype", "p.value"))
ggplot(sample_df_melted, aes(x = cluster, y = density)) + geom_boxplot() + theme_bw() +
theme_Publication() + geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2,
height = 0)) + scale_color_manual(values = pal_patient) + facet_wrap(~tiltype,
scales = "free") + geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value),
hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
diversity_column <- "observedDiversity_mean"
diversity_files <- list(tcr = tcr_diversity_file, bcr = bcr_diversity_file)
diversity <- lapply(names(diversity_files), function(segment) {
f <- diversity_files[[segment]]
xcr_diversity <- read_xcr_diversity_file(f, db_path)
df <- subset(xcr_diversity, select = c("condensed_id", "patient_id", diversity_column))
colnames(df) <- mapvalues(colnames(df), from = diversity_column, to = segment)
return(df)
})
names(diversity) <- names(diversity_files)
xcr_diversity <- Reduce(plyr::join, diversity)
XCR_VARS <- c("tcr", "bcr")
molsubtypes <- fread(molecular_subtype_file)
molsubtypes <- setNames(molsubtypes, c("condensed_id", "subtype"))
proportion_subclonality <- fread(proportion_subclonality_file)
proportion_subclonality_subset <- subset(proportion_subclonality, select = c("condensed_id",
"proportion_subclonal"))
exprs <- fread(nanostring_data_path)
labels <- fread(nanostring_annotations_path)
celltype_matrix <- create_celltype_matrix(exprs, labels, db_path)
pathway_matrix <- create_pathway_matrix(exprs, labels, db_path)
celltype_df <- rownames_to_column(as.data.frame(t(celltype_matrix)), var = "condensed_id")
pathway_df <- rownames_to_column(as.data.frame(t(pathway_matrix)), var = "condensed_id")
NANOSTRING_VARS <- c(rownames(celltype_matrix), rownames(pathway_matrix))
colnames(sample_clusts) <- mapvalues(colnames(sample_clusts), "new_id", "condensed_id")
ihc_stabilize_subset <- stabilize_ihc_variances(ihc_table_slide, ihc_table,
tiltypes)
data_matrices <- list(sample_clusts, ihc_stabilize_subset, xcr_diversity, celltype_df,
pathway_df, proportion_subclonality_subset)
data_matrices <- lapply(data_matrices, function(x) {
x %>% dplyr::select(-one_of("patient_id"))
})
combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "full"), data_matrices)
combined_df <- subset(combined_df, !is.na(cluster) & !condensed_id %in% c("7_BrnM"))
combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
# ref_dendrogram <- prune(as.dendrogram(sample_heat$tree_row), '7_BrnM')
sample_order <- sample_heat$tree_row$labels[sample_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))
cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))
SIGS <- c("SV-3", "SNV-5", "SV-1", "SV-2", "SNV-1")
sig_annotations <- rownames_to_column(data.frame(props_sample_mat[, SIGS]),
var = "condensed_id")
## Take logarithms to ~variance stabilize
variant_count_annotations <- variant_sample %>% group_by_(.dots = "condensed_id") %>%
summarise(snv_count = log(sum(snv_count)), bkpt_count = log(sum(bkpt_count))) %>%
subset(select = c("condensed_id", "snv_count", "bkpt_count"))
row_annotations <- Reduce(plyr::join, list(cluster_annotations, molsubtypes,
sig_annotations, variant_count_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")
combined_mat_scaled <- scale(combined_mat)
# hcs <- lapply(unique(combined_df$cluster), function(clust) { ids <-
# subset(combined_df, cluster == clust)$condensed_id dists <-
# dist(combined_mat_scaled[ids,], method = 'euclidean') #dists <-
# proxy::dist(combined_mat_scaled[ids,], method = function(x,y)
# pairwise_dist(x,y,method='canberra')) hclust(dists, method = 'ward.D') })
# min_height <- max(sapply(hcs, function(x) max(unique(cophenetic(x)))))*1.1
# hc <- Reduce(function(x,y) merge(as.dendrogram(x),as.dendrogram(y),height
# = min_height), hcs) hc <- as.dendrogram(ref_dendrogram) hc <-
# as.dendrogram(sample_heat2$tree_row) ha <-
# HeatmapAnnotation(row_annotations[rownames(combined_mat_scaled),],
# which='row') Heatmap(clip_values(combined_mat_scaled, 2, -2), cluster_rows
# = hc2, cluster_columns = TRUE, split = 2, clustering_method_columns =
# 'ward.D2') + ha
pheatmap(clip_values(combined_mat_scaled, 2, -2)[sample_order, ], cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6)
What we can see is:
- H-HRD cluster is relatively homogeneous.
- Looks like there are two clusters within the H-FBI group – one characterized by high levels of immune activity (cytotoxicity, etc.) and one characterized by low levels.
This is a pretty significant finding.
Additionally, an interesting thing is that the patients/samples with the highest proportions of foldbacks – patients 2, 3, and 9 – are actually the ones with high immune response in the foldback group! Suggestive that perhaps foldback inversions can create neoepitopes. Of course very preliminary and low sample size though.
If you’re wondering why there’s no dendrogram on the vertical axis it’s because the plotting functions I’m currently using don’t allow for self-specified dendrograms. Trying to make one that lets me do so but it’s taking a bit of acrobatics and I’ve wasted a lot of time already …
To see ICGC validation, go to that section. I’ll add a link later …
TCR/BCR diversity
combined_df$patient_id <- map_id(combined_df$condensed_id, from = "condensed_id",
to = "patient_id", db_path)
combined_df_xcr <- melt(combined_df, id.vars = c("condensed_id", "patient_id",
"cluster"), measure.vars = XCR_VARS, variable.name = "type", value.name = "value")
pvals <- setNames(ddply(combined_df_xcr, .(type), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(value ~ cluster, df)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("type", "p.value"))
ggplot(combined_df_xcr, aes(x = cluster, y = value)) + geom_boxplot() + theme_bw() +
theme_Publication() + geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2,
height = 0)) + scale_color_manual(values = pal_patient) + facet_wrap(~type,
scales = "free") + geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value),
hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
Celltypes and pathways
combined_df$patient_id <- map_id(combined_df$condensed_id, from = "condensed_id",
to = "patient_id", db_path)
combined_df_nanostring <- melt(combined_df, id.vars = c("condensed_id", "patient_id",
"cluster"), measure.vars = NANOSTRING_VARS, variable.name = "type", value.name = "value")
pvals <- setNames(ddply(combined_df_nanostring, .(type), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(value ~ cluster, df)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("type", "p.value"))
ggplot(combined_df_nanostring, aes(x = cluster, y = value)) + geom_boxplot() +
theme_bw() + theme_Publication() + geom_jitter(aes(colour = patient_id),
position = position_jitter(width = 0.2, height = 0)) + scale_color_manual(values = pal_patient) +
facet_wrap(~type, scales = "free") + geom_text(data = pvals, aes(x = Inf,
y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
Ancestral-descendant level
clusters <- cutree(patient_heat$tree_row, NCLUST)
patient_clusts <- make_cluster_frame(clusters)
props_ad_merged_clusts <- join(props_ad_merged, patient_clusts)
patient_df <- unique(subset(props_ad_merged_clusts, select = selected_cols))
patient_df_melted <- melt(patient_df, id.vars = colnames(patient_df)[!colnames(patient_df) %in%
tiltypes], measure.vars = tiltypes, variable.name = "tiltype", value.name = "density")
pvals <- setNames(ddply(patient_df_melted, .(tiltype), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(density ~ cluster, df)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("tiltype", "p.value"))
ggplot(patient_df_melted, aes(x = cluster, y = density)) + geom_boxplot() +
theme_bw() + theme_Publication() + geom_jitter(aes(colour = patient_id),
position = position_jitter(width = 0.2, height = 0)) + scale_color_manual(values = pal_patient) +
facet_wrap(~tiltype, scales = "free") + geom_text(data = pvals, aes(x = Inf,
y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
Hence, the foldback group (cluster 2) has lower CD8+ and CD4+ densities than the HRD group, as expected.
A caveat is that p-values are computed with Wilcoxon, irrespective of patient number. We can’t really opt for a nonparametric nested ranks test because very few patients (4) are actually present in both clusters.
Ancestral analysis
Ancestral variants may have different properties from descendant variants.
Sample-specific
Here we’ll just naively allow for subclonal variants to be counted multiple times.
props_ad_merged$is_ancestral <- as.factor(props_ad_merged$is_ancestral)
pvals <- setNames(ddply(props_ad_merged, .(signature), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(proportion ~ is_ancestral, df)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("signature", "p.value"))
ggplot(props_ad_merged, aes(x = is_ancestral, y = proportion)) + geom_boxplot() +
geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2,
height = 0)) + theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) +
facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf,
y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
P-values are uncorrected.
Unsurprisingly, ancestral samples have significantly more of SNV-4, the age signature. Insignificant, but they also have less SV-1, which is one of the BRCA’s (small deletions).
Union
Here we’ll actually take the union of subclonal variants for each patient so we don’t overcount.
props_patient_ad_merged$is_ancestral <- as.factor(props_patient_ad_merged$is_ancestral)
pvals <- setNames(ddply(props_patient_ad_merged, .(signature), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(proportion ~ is_ancestral, df, paired = TRUE)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("signature", "p.value"))
ggplot(props_patient_ad_merged, aes(x = is_ancestral, y = proportion)) + geom_boxplot() +
geom_jitter(aes(colour = patient_id), position = position_jitter(width = 0.2,
height = 0)) + theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) +
facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf,
y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
Aside from age and HRD which were described in McPherson et al., there’s additionally the SV-3 signature – a translocation signature. Implying that translocations are early (ancestral) events in HGSC – perhaps this is interesting? I have to do a literature search.
anc_desc_patient <- dcast(props_patient_ad_merged, formula = patient_id + signature ~
is_ancestral, value.var = "proportion")
pvals <- setNames(ddply(anc_desc_patient, .(signature), function(x) {
df <- as.data.frame(x)
corres <- with(df, cor.test(`0`, `1`, method = "spearman"))
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("signature", "p.value"))
ggplot(anc_desc_patient, aes(x = `1`, y = `0`)) + geom_point() + geom_point(aes(colour = patient_id)) +
theme_bw() + theme_Publication() + scale_colour_manual(values = pal_patient) +
facet_wrap(~signature, scales = "free") + geom_text(data = pvals, aes(x = Inf,
y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
ICGC validation
FBI subclusters
specimen_data <- fread(icgc_specimen_file)
icgc_subtypes <- fread(icgc_subtype_file)
icgc_expr_mat <- fread(icgc_expr_mat_file)
icgc_expr_df <- icgc_expr_mat %>% as.data.frame %>% column_to_rownames("icgc_donor_id") %>%
t %>% as.data.frame %>% rownames_to_column("Name")
icgc_celltype_matrix <- create_pathway_matrix(icgc_expr_df, labels, db_path,
convert_ids = FALSE)
icgc_immune <- rownames_to_column(as.data.frame(t(icgc_celltype_matrix)), var = "icgc_donor_id")
# cytotoxic_markers <- c('PRF1', 'GZMA', 'HLA-A', 'HLA-B', 'HLA-C', 'GZMK',
# 'GZMM', 'GZMH', 'GNLY', 'GZMB') icgc_immune <-
# as.data.frame(subset(icgc_expr_mat, select=c('icgc_donor_id',
# cytotoxic_markers))) for (i in 2:ncol(icgc_immune)) { icgc_immune[,i] <-
# log10(icgc_immune[,i]) }
props_ov_combined <- read_mutsig_output(mmctm_ov_combined_result_dir, samples,
external = TRUE)
props_ov_combined_prop <- props_ov_combined$prop
props_ov_combined_mat <- create_mutsig_matrix(props_ov_combined_prop, col = "proportion")
# props_ov_combined_mat_scaled <- as.data.frame(apply(props_ov_combined_mat,
# 2, function(x) { #x <- logit(x) (x-median(x,na.rm=TRUE))/mad(x,na.rm=TRUE)
# }))
props_ov_combined_mat_scaled <- scale(props_ov_combined_mat)
# props_ov_combined_mat_scaled <- clip_values(props_ov_combined_mat_scaled,
# 3, -3) clip_values(scale(props_ov_combined_mat), 2, -2)
ov_combined_heat <- pheatmap(props_ov_combined_mat_scaled, fontsize_row = 6,
clustering_distance_rows = "correlation", clustering_method = "ward.D2")
NCLUST <- 3
clusters <- cutree(ov_combined_heat$tree_row, NCLUST)
ov_combined_clusts <- make_cluster_frame(clusters)
colnames(icgc_immune) <- mapvalues(colnames(icgc_immune), "icgc_donor_id", "new_id")
data_matrices <- list(ov_combined_clusts, icgc_immune)
data_matrices <- lapply(data_matrices, function(x) {
x %>% dplyr::select(-one_of("patient_id"))
})
combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
# combined_df <- subset(combined_df, cluster == 1)
combined_mat <- subset(combined_df, select = -c(new_id, cluster))
rownames(combined_mat) <- combined_df$new_id
sample_order <- ov_combined_heat$tree_row$labels[ov_combined_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))
cluster_annotations <- subset(combined_df, select = c(new_id, cluster))
SIGS <- c("SV-8", "SNV-1", "SV-4", "SV-7", "SV-5", "SNV-7", "SV-1")
sig_annotations <- rownames_to_column(data.frame(props_ov_combined_mat[, SIGS],
check.names = FALSE), var = "new_id")
subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype",
"nmf_subtype"))
colnames(subtype_annotations)[1] <- "new_id"
row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations,
subtype_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "new_id")
select_rows <- rownames(subset(row_annotations, `SV-4` < 1))
notx <- subset(specimen_data, str_detect(specimen_type, "Primary") & specimen_donor_treatment_type ==
"no treatment")$icgc_donor_id
sample_order <- intersect(sample_order, select_rows)
sample_order <- intersect(sample_order, notx)
# order_fbi <- order(row_annotations[rownames(combined_mat_scaled),]$`SV-8`)
# combined_mat_scaled <- scale(combined_mat)
combined_mat_scaled <- as.data.frame(apply(combined_mat, 2, function(x) (x -
median(x, na.rm = TRUE))/mad(x, na.rm = TRUE)))
combined_mat_scaled <- combined_mat_scaled[sample_order, ]
other_mat <- props_ov_combined_mat_scaled[sample_order, ]
pheatmap(clip_values(cbind(combined_mat_scaled, other_mat), 2, -2), cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation",
clustering_method = "ward.D2")
# rowmean <- rowMeans(combined_mat_scaled) a <- merge(row_annotations,
# as.data.frame(rowmean), by='row.names') ggplot(subset(a, cluster != 1),
# aes(x=rowmean > median(rowmean), y = `SNV-7`)) + geom_boxplot() +
# theme_bw()
clusters <- setNames(cluster_annotations, c("icgc_donor_id", "cluster"))
Differential expression
icgc_expr_melted <- fread(icgc_expr_raw_file)
Read 0.0% of 3709596 rows
Read 11.6% of 3709596 rows
Read 23.5% of 3709596 rows
Read 35.3% of 3709596 rows
Read 47.4% of 3709596 rows
Read 59.3% of 3709596 rows
Read 71.2% of 3709596 rows
Read 83.0% of 3709596 rows
Read 95.2% of 3709596 rows
Read 3709596 rows and 15 (of 15) columns from 0.484 GB file in 00:00:16
icgc_expr_casted_matrix <- expression_df_to_matrix(icgc_expr_melted, summarize_over = "icgc_donor_id",
measure_var = "raw_read_count")
icgc_counts <- icgc_expr_casted_matrix %>% column_to_rownames(var = "icgc_donor_id")
icgc_counts_filtered <- icgc_counts[clusters$icgc_donor_id, ]
icgc_counts_filtered <- icgc_counts_filtered %>% t %>% as.data.frame
dge <- DGEList(counts = icgc_counts_filtered)
dge <- calcNormFactors(dge)
library_sizes <- colSums(icgc_counts_filtered)
library_sizes
DO46325 DO46327 DO46328 DO46329 DO46330 DO46331 DO46332
78792862 106156028 146714663 100782140 66648443 94187980 128035792
DO46333 DO46334 DO46336 DO46338 DO46340 DO46342 DO46344
70763442 87776088 106659467 67264480 72586319 91076278 97575765
DO46346 DO46350 DO46352 DO46354 DO46356 DO46358 DO46360
78219082 65895620 95169618 92438598 46265120 90175214 96687938
DO46362 DO46364 DO46366 DO46368 DO46370 DO46372 DO46374
95856331 101399457 90792124 97051216 100193800 92056106 116039147
DO46376 DO46378 DO46380 DO46382 DO46384 DO46386 DO46388
81664713 159721278 90029262 80192737 80784914 88436986 95577226
DO46390 DO46392 DO46394 DO46396 DO46398 DO46400 DO46402
88612126 91723742 91209587 87119354 79391516 93620832 94446982
DO46404 DO46408 DO46412 DO46448 DO46493 DO46551 DO46560
97746414 91333342 95814730 88596500 123302138 84121097 109112881
DO46561 DO46566 DO46568 DO46571 DO46576 DO46581 DO46586
99873236 79993818 95909367 75957744 91988944 98993304 95877151
DO46588 DO46591 DO46597 DO46602 DO46606 DO46611
78452166 113011642 94472903 96109428 98892638 61422278
max(library_sizes)/min(library_sizes)
[1] 3.452304
design <- model.matrix(~0 + factor(clusters$cluster))
colnames(design) <- paste0("clust", 1:NCLUST)
contrast.matrix <- makeContrasts(clust1 - clust3, clust2 - (clust1 + clust3)/2,
clust2 - clust1, levels = design)
limma-trend
We straddle close to threshold for doing this but let’s do it anyways.
fit_trend <- rnaseq_DE_initial_fit(dge, design, type = "limma_trend")
fit_trend_contrasts <- contrasts.fit(fit_trend, contrast.matrix)
fit_trend_contrasts <- eBayes(fit_trend_contrasts)
hrd_fbi_genes <- topTable(fit_trend_contrasts, coef = "clust2 - (clust1 + clust3)/2",
adjust.method = "BH", number = Inf, sort = "p")
fbi_immune_genes <- topTable(fit_trend_contrasts, coef = "clust1 - clust3",
number = Inf, sort = "p")
hrd_fbi_highimmune_genes <- topTable(fit_trend_contrasts, coef = "clust2 - clust1",
number = Inf, sort = "p")
hrd_fbi_pathways <- pathway_analysis(hrd_fbi_genes, gs_type = "go")
fbi_immune_pathways <- pathway_analysis(fbi_immune_genes, gs_type = "go")
hrd_fbi_highimmune_pathways <- pathway_analysis(hrd_fbi_highimmune_genes, gs_type = "go")
HRD vs. FBI samples (clust2 vs. clust1+clust3)
datatable(hrd_fbi_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
High immune FBI vs. low immune FBI (clust1 vs. clust3)
datatable(fbi_immune_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
HRD vs. high immune FBI (clust2 vs. clust1)
datatable(hrd_fbi_highimmune_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
voom
We have greater than 3-fold variability in library size, so we’re better off using the voom method for DE analysis.
fit_voom <- rnaseq_DE_initial_fit(dge, design, type = "voom")
fit_voom_contrasts <- contrasts.fit(fit_voom, contrast.matrix)
fit_voom_contrasts <- eBayes(fit_voom_contrasts)
hrd_fbi_genes <- topTable(fit_voom_contrasts, coef = "clust2 - (clust1 + clust3)/2",
adjust.method = "BH", number = Inf, sort = "p")
fbi_immune_genes <- topTable(fit_voom_contrasts, coef = "clust1 - clust3", number = Inf,
sort = "p")
hrd_fbi_highimmune_genes <- topTable(fit_voom_contrasts, coef = "clust2 - clust1",
number = Inf, sort = "p")
hrd_fbi_pathways <- pathway_analysis(hrd_fbi_genes, gs_type = "kegg")
fbi_immune_pathways <- pathway_analysis(fbi_immune_genes, gs_type = "kegg")
hrd_fbi_highimmune_pathways <- pathway_analysis(hrd_fbi_highimmune_genes, gs_type = "kegg")
# pv.out.list <- sapply(path.ids2, function(pid) pathview( gene.data =
# exp.fc, pathway.id = pid, species = 'hsa', out.suffix=out.suffix))
Note: BRCA1, BRCA2, and RPA are also downregulated in the HRD group – the entire HR-associated gene cluster isn’t significantly downregulated though.
One of the key reasons why the entire HR pathway isn’t significantly downregulated is because PolQ is part of it and PolQ is upregulated. Of note, PolQ promotes MMEJ, providing further evidence for MMEJ activity in FBI tumours.
HRD vs. FBI samples (clust2 vs. clust1+clust3)
datatable(hrd_fbi_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
High immune FBI vs. low immune FBI (clust1 vs. clust3)
datatable(fbi_immune_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
HRD vs. high immune FBI (clust2 vs. clust1)
datatable(hrd_fbi_highimmune_pathways, extensions = "Buttons", options = list(pageLength = 5,
scrollX = TRUE, dom = "Bfrtip", buttons = c("copy", "csv", "excel", "pdf",
"print")))
Some DNA repair genes
v <- voom(icgc_counts_filtered, design, normalize = "quantile", plot = FALSE)
icgc_expr_df_wide <- v$E %>% t %>% as.data.frame %>% rownames_to_column("icgc_donor_id")
icgc_expr_df_melted <- reshape2::melt(icgc_expr_df_wide, id.vars = c("icgc_donor_id"),
measure.vars = colnames(icgc_expr_df_wide)[2:ncol(icgc_expr_df_wide)], variable.name = "Name",
value.name = "expression")
icgc_expr_df_melted_labeled <- plyr::join(icgc_expr_df_melted, clusters)
REPAIR_GENES <- c("BRCA1", "BRCA2", "POLQ", "FEN1", "XRCC1", "PARP1", "LIG3")
icgc_expr_df_melted_labeled_filtered <- subset(icgc_expr_df_melted_labeled,
Name %in% REPAIR_GENES)
ggplot(subset(icgc_expr_df_melted_labeled_filtered, !is.na(cluster)), aes(x = cluster,
y = expression)) + geom_boxplot() + geom_jitter(position = position_jitter(width = 0.2,
height = 0)) + theme_bw() + theme_Publication() + facet_wrap(~Name, scales = "free")
Survival
icgc_clinical <- read_donor_specimen_survival(icgc_donor_file, icgc_specimen_file)
icgc_clinical_labeled <- plyr::join(clusters, icgc_clinical)
icgc_clinical_labeled$SurvObj <- with(icgc_clinical_labeled, Surv(donor_survival_time,
donor_vital_status == "deceased"))
Survival by FBI status …
simple_survival_analysis(SurvObj ~ cluster != 2, data = icgc_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster != 2=FALSE 27 25 29.3 0.641 1.34
cluster != 2=TRUE 38 34 29.7 0.634 1.34
Chisq= 1.3 on 1 degrees of freedom, p= 0.246
simple_survival_analysis(SurvObj ~ cluster, data = icgc_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=1 21 18 15.8 0.293 0.413
cluster=2 27 25 29.3 0.641 1.344
cluster=3 17 16 13.8 0.344 0.473
Chisq= 1.3 on 2 degrees of freedom, p= 0.509
TCGA validation
While we can’t directly do mutation signature analysis with MMCTM on exome data (I suppose this is theoretically possible but we’d probably be restricted in the types of events we can call, like long SVs), we can look at the FBI-HLAMP finding from Yikan’s paper and see if that lines up with immune signatures.
tcga_expr_mat <- read_tcga_exprs(tcga_expr_mat_file)
Read 0.0% of 570 rows
Read 570 rows and 12497 (of 12497) columns from 0.121 GB file in 00:00:05
tcga_ov_annotation <- fread(tcga_ov_annotation_file)
# tcga_fbi_hlamp_proportions <- fread(tcga_fbi_hlamp_proportions_file)
tcga_expr_df <- tcga_expr_mat %>% as.data.frame %>% column_to_rownames("tcga_sample_id") %>%
t %>% as.data.frame %>% rownames_to_column("Name")
tcga_celltype_matrix <- create_pathway_matrix(tcga_expr_df, labels, db_path,
convert_ids = FALSE)
tcga_immune <- rownames_to_column(as.data.frame(t(tcga_celltype_matrix)), var = "tcga_sample_id")
mat <- tcga_immune %>% column_to_rownames("tcga_sample_id") %>% scale #%>% clip_values(2, -2)
row_annotations <- tcga_ov_annotation %>% as.data.frame %>% column_to_rownames("tcga_sample_id")
tcgaheat <- pheatmap(mat, annotation_row = row_annotations, cluster_rows = TRUE,
cluster_cols = TRUE, clustering_method = "ward.D", show_rownames = FALSE)
# tcga_clusters <-
# rownames_to_column(data.frame(cluster=factor(cutree(tcgaheat$tree_row,
# 2))), var = 'tcga_sample_id')
col <- "Cytotoxicity"
tcga_clusters <- rownames_to_column(data.frame(cluster = mat[, col] > median(mat[,
col])), "tcga_sample_id") %>% mutate(cluster = as.factor(as.numeric(cluster)))
# row_annotations <- plyr::join(tcga_ov_annotation, tcga_clusters) %>%
# as.data.frame %>% column_to_rownames('tcga_sample_id') pheatmap(mat,
# annotation_row = row_annotations, cluster_rows = TRUE, cluster_cols =
# TRUE, clustering_method = 'ward.D', show_rownames = FALSE)
df_merged <- plyr::join(tcga_immune, tcga_ov_annotation)
expr_measure_vars <- colnames(tcga_immune)[2:ncol(tcga_immune)]
df_merged_melted <- melt(df_merged, id.vars = c("tcga_sample_id", "Subgroup",
"MolecularSubtype", "BRCA.status"), measure.vars = expr_measure_vars, variable.name = "Measure",
value.name = "Expression")
pvals <- setNames(ddply(subset(df_merged_melted, !is.na(Subgroup)), .(Measure),
function(x) {
df <- as.data.frame(x)
testres <- kruskal.test(Expression ~ factor(Subgroup), data = df)
pval <- testres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("Measure", "p.value"))
ggplot(subset(df_merged_melted, !is.na(Subgroup)), aes(x = Subgroup, y = Expression)) +
geom_boxplot() + theme_bw() + facet_wrap(~Measure, scales = "free", ncol = 3) +
theme_Publication() + geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value),
hjust = 1.1, vjust = 1.5, size = 3, parse = TRUE)
P-values are uncorrected. So after correction, we aren’t going to get anything significant.
In conclusion, the immune subgroups constitute a new subgrouping independent of FBI-HLAMP.
I’ve also done correlation testing using the logR values that Yikan’s provided me (rather than the discrete groupings of no AMP, FBI-AMP low, and FBI-AMP high), but the correlations are very poor (rho values of ~ -0.05 or so, p-values of >=0.3).
Survival
tcga_clinical <- read_tcga_clinical(tcga_donor_file, type = "synapse2", filter = TRUE,
unique = FALSE)
tcga_clinical_labeled <- Reduce(function(x, y) plyr::join(x, y, type = "full"),
list(tcga_clusters, tcga_clinical, tcga_ov_annotation))
tcga_clinical_labeled$SurvObj <- with(tcga_clinical_labeled, Surv(ifelse(vital_status ==
"Dead", death_days_to, last_contact_days_to), vital_status == "Dead"))
# tcga_clinical_labeled <- subset(tcga_clinical_labeled,
# additional_drug_therapy == 'NO' & additional_immuno_therapy == 'NO' &
# additional_pharmaceutical_therapy == 'NO' & targeted_molecular_therapy ==
# 'NO' & radiation_therapy == 'NO') tcga_clinical_labeled <-
# subset(tcga_clinical_labeled, str_detect(tumor_stage, '^III'))
FBI-AMP colocalization
Let’s first stratify by Yikan’s groupings:
simple_survival_analysis(SurvObj ~ Subgroup, data = tcga_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
n=430, 147 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 173 86 67.7 4.94 7.43
Subgroup=FBI-AMP Low 182 80 92.1 1.60 2.87
Subgroup=No AMP 75 43 49.2 0.77 1.03
Chisq= 7.4 on 2 degrees of freedom, p= 0.0243
Note: The logrank p-values in the paper are incorrect, the one’s I’ve computed are the right ones. In the paper, the death days from the dead patients were not correctly combined into the computation, so p-values were computed without the dead patients.
Immune activity
We’ll now stratify by cluster
, a variable that indicates whether or not we’re above median in terms of Cytotoxicity. In other words, 1 means we’re high immune, and 0 means we’re low immune.
simple_survival_analysis(SurvObj ~ cluster, data = tcga_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
n=562, 15 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 280 162 148 1.38 2.79
cluster=1 282 132 146 1.39 2.79
Chisq= 2.8 on 1 degrees of freedom, p= 0.095
Low immune tumours trend towards poor survival but this is not significant at all.
Now let’s try by treating immune activity as a continuous variable, controlling for other covariates:
tcga_immune_continuous <- tcga_celltype_matrix %>% t %>% as.data.frame %>% rownames_to_column("tcga_sample_id")
tcga_clinical_labeled_continuous <- plyr::join(tcga_clinical_labeled, tcga_immune_continuous)
coxph(SurvObj ~ Cytotoxicity + age_at_initial_pathologic_diagnosis + tumor_grade +
str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy + additional_drug_therapy +
additional_immuno_therapy, tcga_clinical_labeled_continuous)
Call:
coxph(formula = SurvObj ~ Cytotoxicity + age_at_initial_pathologic_diagnosis +
tumor_grade + str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy +
additional_drug_therapy + additional_immuno_therapy, data = tcga_clinical_labeled_continuous)
coef exp(coef) se(coef)
Cytotoxicity -1.68e-01 8.45e-01 8.25e-02
age_at_initial_pathologic_diagnosis 1.95e-02 1.02e+00 5.50e-03
tumor_gradeG1 -4.72e-01 6.24e-01 1.27e+00
tumor_gradeG2 -2.99e-01 7.42e-01 1.06e+00
tumor_gradeG3 -1.19e-02 9.88e-01 1.06e+00
tumor_gradeG4 3.76e-01 1.46e+00 1.45e+00
tumor_gradeGB 1.14e-02 1.01e+00 1.45e+00
tumor_gradeGX 3.20e-01 1.38e+00 1.16e+00
str_extract(tumor_stage, "[IV]+")II 1.12e-01 1.12e+00 7.00e-01
str_extract(tumor_stage, "[IV]+")III 9.32e-01 2.54e+00 5.91e-01
str_extract(tumor_stage, "[IV]+")IV 1.19e+00 3.29e+00 6.07e-01
additional_chemo_therapy[Not Available] -9.30e-03 9.91e-01 3.53e+03
additional_chemo_therapyNO -1.14e+00 3.19e-01 3.53e+03
additional_chemo_therapyYES -9.73e-01 3.78e-01 3.53e+03
additional_drug_therapy[Not Available] -1.49e+01 3.44e-07 2.42e+03
additional_drug_therapy[Pending] 5.16e-01 1.68e+00 3.53e+03
additional_drug_therapyNO 4.78e-01 1.61e+00 3.53e+03
additional_drug_therapyYES -1.28e-02 9.87e-01 3.53e+03
additional_immuno_therapy[Not Available] 1.44e+01 1.87e+06 2.58e+03
additional_immuno_therapy[Pending] -1.40e+01 8.52e-07 4.19e+03
additional_immuno_therapyNO -1.95e-01 8.23e-01 3.30e-01
additional_immuno_therapyYES NA NA 0.00e+00
z p
Cytotoxicity -2.04 0.04158
age_at_initial_pathologic_diagnosis 3.55 0.00038
tumor_gradeG1 -0.37 0.71118
tumor_gradeG2 -0.28 0.77908
tumor_gradeG3 -0.01 0.99097
tumor_gradeG4 0.26 0.79603
tumor_gradeGB 0.01 0.99372
tumor_gradeGX 0.28 0.78225
str_extract(tumor_stage, "[IV]+")II 0.16 0.87332
str_extract(tumor_stage, "[IV]+")III 1.58 0.11449
str_extract(tumor_stage, "[IV]+")IV 1.96 0.04961
additional_chemo_therapy[Not Available] 0.00 1.00000
additional_chemo_therapyNO 0.00 0.99974
additional_chemo_therapyYES 0.00 0.99978
additional_drug_therapy[Not Available] -0.01 0.99509
additional_drug_therapy[Pending] 0.00 0.99988
additional_drug_therapyNO 0.00 0.99989
additional_drug_therapyYES 0.00 1.00000
additional_immuno_therapy[Not Available] 0.01 0.99553
additional_immuno_therapy[Pending] 0.00 0.99734
additional_immuno_therapyNO -0.59 0.55541
additional_immuno_therapyYES NA NA
Likelihood ratio test=46.8 on 21 df, p=0.00101
n= 559, number of events= 292
(18 observations deleted due to missingness)
Indeed, immune activity is significantly associated with prolonged survival (negative coefficient = fewer death events).
Stratification by foldback-AMP status
fbi_high <- subset(tcga_clinical_labeled, Subgroup == "FBI-AMP High")
fbi_low <- subset(tcga_clinical_labeled, Subgroup == "FBI-AMP Low")
fbi_no <- subset(tcga_clinical_labeled, Subgroup == "No AMP")
fbi_nothigh <- subset(tcga_clinical_labeled, Subgroup %in% c("FBI-AMP Low",
"No AMP"))
simple_survival_analysis(SurvObj ~ cluster, data = fbi_high)
Call:
survival::survdiff(formula = formula, data = data)
n=172, 2 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 90 45 48.9 0.310 0.739
cluster=1 82 40 36.1 0.419 0.739
Chisq= 0.7 on 1 degrees of freedom, p= 0.39
simple_survival_analysis(SurvObj ~ cluster, data = fbi_low)
Call:
survival::survdiff(formula = formula, data = data)
n=181, 5 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 91 42 43.5 0.0514 0.116
cluster=1 90 37 35.5 0.0630 0.116
Chisq= 0.1 on 1 degrees of freedom, p= 0.733
simple_survival_analysis(SurvObj ~ cluster, data = fbi_no)
Call:
survival::survdiff(formula = formula, data = data)
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 34 24 18.2 1.84 3.23
cluster=1 41 19 24.8 1.35 3.23
Chisq= 3.2 on 1 degrees of freedom, p= 0.0724
What’s curious about this is that it seems only the no AMP group gets any benefit from having a high immune response. So there’s no benefit within the foldback group of high/low immune response.
simple_survival_analysis(SurvObj ~ cluster, data = fbi_nothigh)
Call:
survival::survdiff(formula = formula, data = data)
n=256, 5 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=0 125 66 61.9 0.275 0.559
cluster=1 131 56 60.1 0.283 0.559
Chisq= 0.6 on 1 degrees of freedom, p= 0.455
Stratification by immune activity
immune_low <- subset(tcga_clinical_labeled, cluster == 0)
immune_high <- subset(tcga_clinical_labeled, cluster == 1)
simple_survival_analysis(SurvObj ~ Subgroup, data = immune_high)
Call:
survival::survdiff(formula = formula, data = data)
n=213, 72 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 82 40 28.1 5.005 7.303
Subgroup=FBI-AMP Low 90 37 40.5 0.299 0.531
Subgroup=No AMP 41 19 27.4 2.569 3.935
Chisq= 8.3 on 2 degrees of freedom, p= 0.0155
simple_survival_analysis(SurvObj ~ Subgroup, data = immune_low)
Call:
survival::survdiff(formula = formula, data = data)
n=215, 70 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High 90 45 38.6 1.060 1.651
Subgroup=FBI-AMP Low 91 42 50.8 1.535 2.843
Subgroup=No AMP 34 24 21.6 0.275 0.345
Chisq= 2.9 on 2 degrees of freedom, p= 0.236
Likewise, the only benefit of being non-foldback is derived from the immune-high group – there’s no difference between being foldback or not if you’re in the low immune group.
Multivariate models
simple_survival_analysis(SurvObj ~ Subgroup + cluster, data = tcga_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
n=428, 149 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
Subgroup=FBI-AMP High, cluster=0 90 45 38.4 1.118 1.380
Subgroup=FBI-AMP High, cluster=1 82 40 28.5 4.624 5.436
Subgroup=FBI-AMP Low, cluster=0 91 42 50.1 1.307 1.730
Subgroup=FBI-AMP Low, cluster=1 90 37 41.1 0.415 0.523
Subgroup=No AMP, cluster=0 34 24 21.0 0.444 0.496
Subgroup=No AMP, cluster=1 41 19 27.9 2.819 3.331
Chisq= 10.9 on 5 degrees of freedom, p= 0.0525
We’ll next combined FBI-AMP low and No AMP into a single category called FBI-NotHigh.
tcga_clinical_labeled$newgroup <- ifelse(tcga_clinical_labeled$Subgroup == "FBI-AMP High",
"FBI-High", "FBI-NotHigh")
simple_survival_analysis(SurvObj ~ newgroup + cluster, data = tcga_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
n=428, 149 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
newgroup=FBI-High, cluster=0 90 45 38.4 1.118 1.380
newgroup=FBI-High, cluster=1 82 40 28.5 4.624 5.436
newgroup=FBI-NotHigh, cluster=0 125 66 71.0 0.358 0.549
newgroup=FBI-NotHigh, cluster=1 131 56 69.0 2.448 3.686
Chisq= 8.7 on 3 degrees of freedom, p= 0.0337
Let’s also do Cox proportional hazards models:
mod1 <- coxph(SurvObj ~ Cytotoxicity + Subgroup + age_at_initial_pathologic_diagnosis +
tumor_grade + str_extract(tumor_stage, "[IV]+") + additional_chemo_therapy +
additional_drug_therapy + additional_immuno_therapy, tcga_clinical_labeled_continuous)
anova(mod1)
mod2 <- coxph(SurvObj ~ Cytotoxicity + Subgroup + age_at_initial_pathologic_diagnosis,
tcga_clinical_labeled_continuous)
anova(mod2)
mod3 <- coxph(SurvObj ~ Cytotoxicity + (Subgroup == "FBI-AMP High") + age_at_initial_pathologic_diagnosis,
subset(tcga_clinical_labeled_continuous, !is.na(Subgroup)))
anova(mod3)
So the effects of immune activity and FBI/HRD status are collinear.
Final run
Instead of overwriting the previous, this is its own section since the underlying implementation has changed substantially.
variant_table <- read_variant_file(master_variant_file, db_path)
breakpoint_table <- read_variant_file(master_breakpoint_file, db_path)
sig_results <- read_signature_files(mmctm_final_patient_dir, variant_table,
breakpoint_table, db_path)
sig_results_snv <- subset(sig_results$snv, is_present == 1)
sig_results_sv <- subset(sig_results$sv, is_present == 1)
sig_results_nonith <- sig_results$nonith %>% as.data.frame %>% column_to_rownames("signature") %>%
t %>% as.data.frame %>% rownames_to_column("condensed_id")
signature_labels <- str_extract(c(colnames(sig_results_snv), colnames(sig_results_sv)),
"SN?V-[0-9]+")
signature_labels <- signature_labels[!is.na(signature_labels)]
summarize_signature_proportions <- function(x, by = c("condensed_id", "patient_id"),
signature_labels, report_count = FALSE) {
props <- subset(x, select = c(by, colnames(x)[colnames(x) %in% signature_labels])) %>%
group_by_(.dots = by) %>% summarise_each(funs(sum))
if (report_count) {
counts <- subset(x, select = c(by, colnames(x)[colnames(x) %in% signature_labels])) %>%
group_by_(.dots = by) %>% summarise(n = n())
props <- plyr::join(props %>% as.data.frame, counts %>% as.data.frame)
}
sums <- rowSums(subset(props, select = colnames(props[colnames(props) %in%
signature_labels])))
sigs <- intersect(signature_labels, colnames(props))
for (sig in sigs) {
props[, sig] <- props[, sig]/sums
}
return(props)
}
Sample signature proportions
These proportions won’t be exactly equal to the topic proportions that the model outputs (they are variational estimates), but we’re actually doing pretty darn well.
TODO: Make a QC plot. From a glance it seems that proportion error is usually within 1-5% relative error.
sample_props_snv <- summarize_signature_proportions(sig_results_snv, by = c("condensed_id",
"patient_id"), signature_labels)
sample_props_sv <- summarize_signature_proportions(sig_results_sv, by = c("condensed_id",
"patient_id"), signature_labels)
patient_props_snv <- sig_results_snv %>% subset(select = c("patient_id", "chrom",
"coord", "ref", "alt", intersect(signature_labels, colnames(sig_results_snv)))) %>%
unique %>% summarize_signature_proportions(by = c("patient_id"), signature_labels)
patient_props_sv <- sig_results_sv %>% subset(select = c("patient_id", "prediction_id",
intersect(signature_labels, colnames(sig_results_sv)))) %>% unique %>% summarize_signature_proportions(by = c("patient_id"),
signature_labels)
sample_props <- plyr::join(sample_props_snv %>% as.data.frame, sample_props_sv %>%
as.data.frame)
sample_props <- rbind.fill(sample_props, sig_results_nonith)
sample_props_mat <- sample_props %>% column_to_rownames("condensed_id") %>%
subset(select = c(colnames(sample_props)[colnames(sample_props) %in% signature_labels]))
sample_props_mat_scaled <- scale(sample_props_mat)
# [,c('SNV-2', 'SNV-5', 'SV-3', 'SV-6', 'SV-8', 'SV-1', 'SV-7', 'SV-5',
# 'SNV-3')]
sample_props_heat <- pheatmap(sample_props_mat_scaled, fontsize_row = 5, clustering_method = "ward.D2")
patient_props <- plyr::join(patient_props_snv %>% as.data.frame, patient_props_sv %>%
as.data.frame)
patient_props <- rbind.fill(patient_props, sig_results_nonith %>% plyr::rename(c(condensed_id = "patient_id")))
patient_props_mat <- patient_props %>% column_to_rownames("patient_id") %>%
subset(select = c(colnames(patient_props)[colnames(patient_props) %in% signature_labels]))
patient_props_mat_scaled <- scale(patient_props_mat)
# [,c('SNV-2', 'SNV-5', 'SV-3', 'SV-6', 'SV-8', 'SV-1', 'SV-7', 'SV-5',
# 'SNV-3')]
patient_props_heat <- pheatmap(patient_props_mat_scaled, fontsize_row = 5, clustering_method = "ward.D2",
clustering_distance_rows = "correlation")
ITH cohort
sample_clusts <- as.data.frame(cutree(sample_props_heat$tree_row, 4)) %>% setNames(c("cluster")) %>%
rownames_to_column("condensed_id")
ihc_stabilize_subset <- stabilize_ihc_variances(ihc_table_slide, ihc_table,
tiltypes)
data_matrices <- list(sample_clusts, ihc_stabilize_subset, xcr_diversity, celltype_df,
pathway_df, proportion_subclonality_subset)
data_matrices <- lapply(data_matrices, function(x) {
x %>% dplyr::select(-one_of("patient_id"))
})
combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "full"), data_matrices)
combined_df <- subset(combined_df, !is.na(cluster) & !condensed_id %in% c("7_BrnM"))
combined_df <- subset(combined_df, condensed_id %in% subset(samples, project_code ==
"ITH")$condensed_id)
combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
# ref_dendrogram <- prune(as.dendrogram(sample_heat$tree_row), '7_BrnM')
sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))
cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))
SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(sample_props_mat[, SIGS], check.names = FALSE),
var = "condensed_id")
## Take logarithms to ~variance stabilize
variant_count_annotations <- variant_sample %>% group_by_(.dots = "condensed_id") %>%
summarise(snv_count = log(sum(snv_count)), bkpt_count = log(sum(bkpt_count))) %>%
subset(select = c("condensed_id", "snv_count", "bkpt_count"))
row_annotations <- Reduce(plyr::join, list(cluster_annotations, molsubtypes,
sig_annotations, variant_count_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")
row_annotations_ith <- row_annotations
combined_mat_scaled <- scale(combined_mat)
pheatmap(clip_values(combined_mat_scaled, 2, -2)[sample_order, ], cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6)
combined_mat_scaled_ith <- combined_mat_scaled
ICGC validation
icgc_fbi_status <- fread(wang_icgc_fbi_status_file)
colnames(icgc_fbi_status) <- mapvalues(colnames(icgc_fbi_status), from = c("Case_ID"),
to = c("condensed_id"))
colnames(icgc_immune) <- mapvalues(colnames(icgc_immune), "new_id", "condensed_id")
data_matrices <- list(sample_clusts, icgc_immune)
data_matrices <- lapply(data_matrices, function(x) {
x %>% dplyr::select(-one_of("patient_id"))
})
combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
# combined_df <- subset(combined_df, cluster == 1)
combined_mat <- subset(combined_df, select = -c(condensed_id, cluster))
rownames(combined_mat) <- combined_df$condensed_id
sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat))
cluster_annotations <- subset(combined_df, select = c(condensed_id, cluster))
SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(sample_props_mat[, SIGS], check.names = FALSE),
var = "condensed_id")
subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype",
"nmf_subtype"))
colnames(subtype_annotations)[1] <- "condensed_id"
icgc_fbi_annotations <- subset(icgc_fbi_status, select = c("condensed_id", "Patch et al. Class",
"Patch et al. Molecular Subgroup", "BRCA.status", "Subgroup"))
row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations,
subtype_annotations, icgc_fbi_annotations))
row_annotations <- row_annotations %>% column_to_rownames(var = "condensed_id")
row_annotations_noith <- row_annotations
# select_rows <- rownames(subset(row_annotations, `SV-4` < 1))
notx <- subset(specimen_data, str_detect(specimen_type, "Primary") & specimen_donor_treatment_type ==
"no treatment")$icgc_donor_id
# sample_order <- intersect(sample_order, select_rows)
sample_order <- intersect(sample_order, notx)
# order_fbi <- order(row_annotations[rownames(combined_mat_scaled),]$`SV-8`)
# combined_mat_scaled <- scale(combined_mat)
combined_mat_scaled <- as.data.frame(apply(combined_mat, 2, function(x) (x -
median(x, na.rm = TRUE))/mad(x, na.rm = TRUE)))
combined_mat_scaled <- combined_mat_scaled[sample_order, ]
other_mat <- sample_props_mat_scaled[sample_order, ]
pheatmap(clip_values(cbind(combined_mat_scaled, other_mat), 2, -2), cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation",
clustering_method = "ward.D2")
combined_mat_scaled_noith <- combined_mat_scaled
icgc_clinical_labeled <- Reduce(function(x, y) plyr::join(x, y, type = "full"),
list(setNames(cluster_annotations, c("icgc_donor_id", "cluster")), icgc_clinical,
setNames(subset(icgc_fbi_annotations, select = c("condensed_id", "BRCA.status",
"Subgroup")), c("icgc_donor_id", "BRCA.status", "Wang_subgroup"))))
icgc_clinical_labeled$SurvObj <- with(icgc_clinical_labeled, Surv(donor_survival_time,
donor_vital_status == "deceased"))
Survival by FBI status …
simple_survival_analysis(SurvObj ~ cluster != 1, data = subset(icgc_clinical_labeled,
cluster != 4))
Call:
survival::survdiff(formula = formula, data = data)
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster != 1=FALSE 28 26 28.9 0.298 0.627
cluster != 1=TRUE 36 32 29.1 0.297 0.627
Chisq= 0.6 on 1 degrees of freedom, p= 0.428
simple_survival_analysis(SurvObj ~ cluster, data = subset(icgc_clinical_labeled,
cluster != 4))
Call:
survival::survdiff(formula = formula, data = data)
N Observed Expected (O-E)^2/E (O-E)^2/V
cluster=1 28 26 28.94 0.298 0.627
cluster=2 25 23 21.32 0.133 0.223
cluster=3 11 9 7.75 0.203 0.249
Chisq= 0.7 on 2 degrees of freedom, p= 0.716
simple_survival_analysis(SurvObj ~ Wang_subgroup, data = icgc_clinical_labeled)
Call:
survival::survdiff(formula = formula, data = data)
n=66, 19 observations deleted due to missingness.
N Observed Expected (O-E)^2/E (O-E)^2/V
Wang_subgroup=High FBI 36 35 28.6 1.45 2.89
Wang_subgroup=Low FBI 30 25 31.4 1.32 2.89
Chisq= 2.9 on 1 degrees of freedom, p= 0.0889
Combined
Sample-level
combined_mat_scaled_all <- rbind.fill(combined_mat_scaled_ith %>% as.data.frame %>%
rownames_to_column("condensed_id"), combined_mat_scaled_noith %>% as.data.frame %>%
rownames_to_column("condensed_id")) %>% column_to_rownames("condensed_id")
row_annotations_all <- rbind.fill(row_annotations_ith %>% rownames_to_column("condensed_id"),
row_annotations_noith %>% rownames_to_column("condensed_id")) %>% column_to_rownames("condensed_id")
sample_order <- sample_props_heat$tree_row$labels[sample_props_heat$tree_row$order]
sample_order <- intersect(sample_order, rownames(combined_mat_scaled_all))
# combined_mat_scaled <- as.data.frame(apply(combined_mat_all, 2,
# function(x) (x-median(x,na.rm=TRUE))/mad(x,na.rm=TRUE)))
combined_mat_scaled <- combined_mat_scaled_all[sample_order, ]
nanostring_vars <- rownames(icgc_celltype_matrix)
combined_mat_scaled_subset <- subset(combined_mat_scaled, select = nanostring_vars)
combined_mat_scaled_subset <- combined_mat_scaled_subset[!apply(combined_mat_scaled_subset,
1, function(x) all(is.na(x))), ]
pheatmap(clip_values(combined_mat_scaled_subset, 2, -2), cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations_all, fontsize = 6,
clustering_distance_cols = "correlation", clustering_method = "ward.D2")
Doesn’t really cluster consistently between ICGC and our samples.
TODO: Batch correct ICGC and our data together (I think I already did this somewhere) – and normalize the final matrix in one step rather than normalizing separately for each subcohort and combining those together. Otherwise we could always be subject to the scenario where the ITH cohort may be skewed in immune response (either all relatively low or high) compared to the ICGC cohort.
TODO: Use absolute counts of mutations for each signature, and cluster based on those. It may be that samples with too low/high mutation counts are not clustering properly.
Patient-level
# ith_icgc_batch_corrected_expression_file <-
# '~/shahlab/projects/ITH_Immune/paper/results/tables/run2/ith_icgc_merged_bc.tsv'
ith_icgc_expression <- fread(ith_icgc_batch_corrected_expression_file)
ith_icgc_celltype_expr <- create_celltype_matrix(ith_icgc_expression, labels,
db_path, convert_ids = FALSE)
ith_icgc_pathway_expr <- create_pathway_matrix(ith_icgc_expression, labels,
db_path, convert_ids = FALSE)
icgc_specimen_data <- fread(icgc_specimen_file)
summarize_expression_by_patient <- function(expr) {
primary_specimen_dat <- subset(icgc_specimen_data, str_detect(specimen_type,
"Primary"))
notx <- subset(primary_specimen_dat, str_detect(specimen_type, "Primary") &
specimen_donor_treatment_type == "no treatment")$icgc_donor_id
primary_specimen_dat <- subset(primary_specimen_dat, icgc_donor_id %in%
notx)
x <- setNames(melt(as.matrix(expr)), c("Name", "sample", "expr"))
x$patient_id <- map_id(as.character(x$sample), from = "condensed_id", to = "patient_id",
db_path)
idx <- is.na(x$patient_id)
donor_labels <- df_as_map(primary_specimen_dat, as.character(x$sample[idx]),
from = "icgc_specimen_id", to = "icgc_donor_id")
x$patient_id[idx] <- donor_labels
x <- subset(x, !is.na(patient_id))
x_sum <- x %>% group_by(Name, patient_id) %>% summarise(expr = mean(expr,
na.rm = TRUE))
res <- dcast(x_sum, formula = Name ~ patient_id, value.var = "expr")
return(res)
}
NCLUST <- 3
clusters <- cutree(patient_props_heat$tree_row, NCLUST)
patient_clusts <- make_cluster_frame(clusters)
cluster_annotations <- subset(plyr::rename(patient_clusts, c(new_id = "patient_id")),
select = c(patient_id, cluster))
ith_icgc_celltype_expr_patient <- summarize_expression_by_patient(ith_icgc_celltype_expr)
ith_icgc_pathway_expr_patient <- summarize_expression_by_patient(ith_icgc_pathway_expr)
expr_dat <- ith_icgc_pathway_expr_patient
combined_patient_mat <- expr_dat %>% as.data.frame %>% column_to_rownames(var = "Name") %>%
t %>% as.data.frame %>% rownames_to_column("patient_id")
data_matrices <- list(cluster_annotations, combined_patient_mat)
data_matrices <- lapply(data_matrices, function(x) {
x
})
combined_df <- Reduce(function(x, y) plyr::join(x, y, type = "inner"), data_matrices)
combined_mat <- subset(combined_df, select = -c(cluster))
rownames(combined_mat) <- NULL
combined_mat <- combined_mat %>% column_to_rownames("patient_id")
SIGS <- c("SV-3", "SNV-5", "SV-6", "SV-8", "SNV-2")
sig_annotations <- rownames_to_column(data.frame(patient_props_mat[, SIGS],
check.names = FALSE), var = "patient_id")
subtype_annotations <- subset(icgc_subtypes, select = c("icgc_donor_id", "subtype",
"nmf_subtype"))
colnames(subtype_annotations)[1] <- "patient_id"
icgc_fbi_status_patient <- fread(wang_icgc_fbi_status_file)
colnames(icgc_fbi_status_patient) <- mapvalues(colnames(icgc_fbi_status_patient),
from = c("Case_ID"), to = c("patient_id"))
icgc_fbi_annotations <- subset(icgc_fbi_status_patient, select = c("patient_id",
"Patch et al. Class", "Patch et al. Molecular Subgroup", "BRCA.status",
"Subgroup"))
row_annotations <- Reduce(plyr::join, list(cluster_annotations, sig_annotations,
subtype_annotations, icgc_fbi_annotations))
row_annotations <- row_annotations %>% column_to_rownames("patient_id")
combined_patient_mat_scaled <- combined_mat %>% scale
patient_order <- patient_props_heat$tree_row$labels[patient_props_heat$tree_row$order]
patient_order <- intersect(patient_order, rownames(combined_patient_mat_scaled))
pheatmap(clip_values(combined_patient_mat_scaled[patient_order, ], 2, -2), cluster_rows = FALSE,
cluster_cols = TRUE, annotation_row = row_annotations, fontsize = 6, clustering_distance_cols = "correlation",
clustering_method = "ward.D2")
Ancestral-descendant signature proportions
ancdesc_props_snv <- unique(subset(sig_results_snv, select = c("patient_id",
"is_ancestral", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in%
colnames(sig_results_snv)]))) %>% summarize_signature_proportions(by = c("patient_id",
"is_ancestral"), signature_labels)
ancdesc_props_sv <- unique(subset(sig_results_sv, select = c("patient_id", "prediction_id",
"is_ancestral", signature_labels[signature_labels %in% colnames(sig_results_sv)]))) %>%
summarize_signature_proportions(by = c("patient_id", "is_ancestral"), signature_labels)
ancdesc_props <- plyr::join(ancdesc_props_snv %>% as.data.frame, ancdesc_props_sv %>%
as.data.frame)
ancdesc_props_mat <- subset(ancdesc_props, select = colnames(ancdesc_props)[colnames(ancdesc_props) %in%
signature_labels])
ancdesc_props_scaled <- scale(ancdesc_props_mat)
rownames(ancdesc_props_scaled) <- with(ancdesc_props, paste(patient_id, is_ancestral,
sep = "_"))
pheatmap(ancdesc_props_scaled, clustering_distance_rows = "euclidean", clustering_method = "ward.D2")
ancdesc_props_melted <- melt(ancdesc_props, id.vars = c("patient_id", "is_ancestral"),
measure.vars = intersect(colnames(ancdesc_props), signature_labels), variable.name = "signature",
value.name = "proportion")
pvals <- setNames(ddply(ancdesc_props_melted, .(signature), function(x) {
df <- as.data.frame(x)
corres <- wilcox.test(proportion ~ is_ancestral, df, paired = TRUE)
pval <- corres$p.value
eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
return(as.character(as.expression(eq)))
}), c("signature", "p.value"))
ggplot(ancdesc_props_melted, aes(x = factor(is_ancestral), y = proportion)) +
geom_boxplot() + facet_wrap(~signature, scales = "free") + geom_text(data = pvals,
aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, vjust = 1.5, size = 3,
parse = TRUE) + theme_bw() + theme_Publication()
Origin node signature proportions
Note: We cannot get node-specific rearrangement signatures.
Note: These node labels DO NOT correspond to node labels within the clone phylogeny; they correspond to nodes within the Dollo model.
node_props_snv <- unique(subset(sig_results_snv, select = c("patient_id", "origin_node",
"chrom", "coord", "ref", "alt", signature_labels[signature_labels %in% colnames(sig_results_snv)]))) %>%
summarize_signature_proportions(by = c("patient_id", "origin_node"), signature_labels,
report_count = TRUE)
node_props_mat <- subset(node_props_snv, select = colnames(node_props_snv)[colnames(node_props_snv) %in%
signature_labels])
node_props_scaled <- scale(node_props_mat)
rownames(node_props_scaled) <- with(node_props_snv, paste(patient_id, origin_node,
sep = "_"))
row_annotations <- subset(node_props_snv, select = c("patient_id", "n"))
row_annotations$n <- log2(row_annotations$n)
row_annotations$patient_id <- factor_id(row_annotations$patient_id, type = "patient_id",
db_path)
rownames(row_annotations) <- rownames(node_props_scaled)
clustering_colours <- list(patient_id = pal_patient)
pheatmap(clip_values(node_props_scaled, 2, -2), clustering_distance_rows = "euclidean",
clustering_method = "ward.D2", fontsize_row = 5, annotation_row = row_annotations,
annotation_colors = clustering_colours)
Looks like there are similar selection pressures acting at each part of the sample phylogeny – i.e. mutation signatures are ‘relatively’ consistent within patients throughout time.
Clonal phylogeny branch-specific signature proportions
NOTE: THE LABELS ON THESE TREES MAY NOT BE THE SAME AS THOSE IN THE MAPSCAPES (ugh).
tree_branch_data <- read_clone_tree_data(clone_tree_file, clone_branch_length_file,
clone_prevalence_file, db_path)
trees <- lapply(tree_branch_data, function(x) x$tree)
branch_lengths <- rbind.fill(lapply(tree_branch_data, function(x) x$branch_dat))
snv_cluster <- rbind.fill(lapply(seq_along(snv_cluster_files), function(i) {
f <- snv_cluster_files[[i]]
patient_id <- snv_cluster_patients[[i]]
snv_cluster <- read_snv_cluster(f, clone_branch_length_file, db_path)
return(snv_cluster)
}))
snv_cluster <- plyr::join(snv_cluster, branch_lengths)
snv_cluster_filtered <- subset(snv_cluster, !is.na(label))
sig_results_snv_cluster <- plyr::join(sig_results_snv, snv_cluster_filtered)
branch_props_snv <- unique(subset(sig_results_snv_cluster, select = c("patient_id",
"label", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in%
colnames(sig_results_snv_cluster)]))) %>% summarize_signature_proportions(by = c("patient_id",
"label"), signature_labels, report_count = TRUE)
branch_props_snv_melted <- melt(branch_props_snv, id.vars = c("patient_id",
"label", "n"), measure.vars = intersect(signature_labels, colnames(branch_props_snv)),
variable.name = "signature", value.name = "proportion")
ggplot(branch_props_snv_melted, aes(x = label, y = proportion)) + geom_bar(aes(fill = signature),
stat = "identity") + facet_wrap(~patient_id, ncol = 1, scales = "free_x") +
theme_bw() + theme_Publication() + geom_text(data = unique(subset(branch_props_snv_melted,
select = c("patient_id", "label", "n"))), aes(x = label, y = 1, label = n),
vjust = -0.2, stat = "identity") + ylim(c(0, 1.2))
What if we use MAP assignments? (This would allow us to apply chi-square tests.)
TODO: Figure out how to apply tests between k > 2 groups of proportions… in other words, a test of homogeneity.
id_vars <- colnames(sig_results_snv_cluster)[!colnames(sig_results_snv_cluster) %in%
signature_labels]
sig_results_snv_cluster_melted <- melt(sig_results_snv_cluster, id.vars = id_vars,
measure.vars = intersect(signature_labels, colnames(sig_results_snv_cluster)),
variable.name = "signature", value.name = "proportion")
maxes <- sig_results_snv_cluster_melted %>% group_by_(.dots = id_vars) %>% summarise(maxprop = max(proportion))
sig_results_snv_cluster_melted <- plyr::join(sig_results_snv_cluster_melted,
maxes)
sig_results_snv_cluster_melted$proportion_map <- ifelse(sig_results_snv_cluster_melted$proportion ==
sig_results_snv_cluster_melted$maxprop, 1, 0)
sig_results_snv_cluster_casted <- dcast(sig_results_snv_cluster_melted, formula = paste0(paste(id_vars,
collapse = "+"), "~ signature"), value.var = "proportion_map")
branch_props_snv_map <- unique(subset(sig_results_snv_cluster_casted, select = c("patient_id",
"label", "chrom", "coord", "ref", "alt", signature_labels[signature_labels %in%
colnames(sig_results_snv_cluster_casted)]))) %>% summarize_signature_proportions(by = c("patient_id",
"label"), signature_labels, report_count = TRUE)
branch_props_snv_map_melted <- melt(branch_props_snv_map, id.vars = c("patient_id",
"label", "n"), measure.vars = intersect(signature_labels, colnames(branch_props_snv_map)),
variable.name = "signature", value.name = "proportion")
ggplot(branch_props_snv_map_melted, aes(x = label, y = proportion)) + geom_bar(aes(fill = signature),
stat = "identity") + facet_wrap(~patient_id, ncol = 1, scales = "free_x") +
theme_bw() + theme_Publication() + geom_text(data = unique(subset(branch_props_snv_map_melted,
select = c("patient_id", "label", "n"))), aes(x = label, y = 1, label = n),
vjust = -0.2, stat = "identity") + ylim(c(0, 1.2))
Adjusted clone trees
trees_age <- trees
age_signature <- "SNV-5"
branch_props_dat <- branch_props_snv_map_melted
tree_objects <- lapply(seq_along(trees_age), function(i) {
tree <- trees_age[[i]]
tree_old <- tree
patient <- names(trees_age)[i]
branch_props_dat_sub <- subset(branch_props_dat, patient_id == as.numeric(patient) &
signature == age_signature)
branch_props_dat_sub$length <- with(branch_props_dat_sub, proportion * n)
edge_lengths <- tree@edge.length
idx <- which(edge_lengths == 0)
to_labels <- tree@label[str_extract(names(edge_lengths), "[0-9]+$")]
lengths <- df_as_map(branch_props_dat_sub, unname(to_labels), from = "label",
to = "length")
if (length(idx) > 0) {
lengths[idx] <- 0
}
tree@edge.length <- lengths
names(tree@edge.length) <- names(edge_lengths)
return(list(all = tree_old, age = tree))
})
names(tree_objects) <- names(trees_age)
tmp <- unlist(tree_objects)
# ignore <- lapply(seq_along(tmp), function(i) { print(plot(tmp[[i]],
# show.node.label = TRUE, main = names(tmp)[i])) })
find_ancestors <- function(tree) {
x <- phylobase:::.phylo4ToDataFrame(tree)
root <- subset(x, node.type == "root")$label
root_number <- names(which(tree@label == root))
direct_descendants <- subset(x, ancestor == as.numeric(root_number))$label
if (length(direct_descendants) == 1) {
return(c(root, direct_descendants))
} else {
return(root)
}
}
patients <- unique(branch_props_dat$patient_id)
root_data <- rbind.fill(lapply(patients, function(pat) {
tree <- trees[[as.character(pat)]]
ancestors <- find_ancestors(tree)
rbind.fill(lapply(ancestors, function(x) {
data.frame(label = x, patient_id = pat)
}))
}))
root_data$node_type <- "root"
branch_data_annotated <- plyr::join(branch_props_dat, root_data)
branch_data_annotated$node_type[is.na(branch_data_annotated$node_type)] <- "descendant"
root_proportions <- branch_data_annotated %>% subset(node_type == "root") %>%
group_by(patient_id, signature) %>% summarise(proportion = weighted.mean(proportion,
w = n)) %>% plyr::rename(c(proportion = "root_proportion"))
branch_data_annotated <- plyr::join(branch_data_annotated, root_proportions)
branch_data_annotated$proportion_diff <- with(branch_data_annotated, proportion -
root_proportion)
ggplot(branch_data_annotated %>% subset(node_type == "descendant" & n > 40),
aes(x = proportion_diff, fill = signature)) + geom_histogram(binwidth = 0.02,
alpha = 0.4, position = "identity") + theme_bw() + theme_Publication()
LS0tCnRpdGxlOiAiTXV0YXRpb24gc2lnbmF0dXJlcyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogdHJ1ZQpwYXJhbXM6CiAgcm1kOiAibXV0YXRpb25fc2lnbmF0dXJlcy5SbWQiCi0tLQogICAgICAgICAgICAgICAgICAgICAgICBgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMjIyMjIyMjIFNuYWtlbWFrZSBoZWFkZXIgIyMjIyMjIyMKbGlicmFyeShtZXRob2RzKQpTbmFrZW1ha2UgPC0gc2V0Q2xhc3MoCiAgICAiU25ha2VtYWtlIiwKICAgIHNsb3RzID0gYygKICAgICAgICBpbnB1dCA9ICJsaXN0IiwKICAgICAgICBvdXRwdXQgPSAibGlzdCIsCiAgICAgICAgcGFyYW1zID0gImxpc3QiLAogICAgICAgIHdpbGRjYXJkcyA9ICJsaXN0IiwKICAgICAgICB0aHJlYWRzID0gIm51bWVyaWMiLAogICAgICAgIGxvZyA9ICJsaXN0IiwKICAgICAgICByZXNvdXJjZXMgPSAibGlzdCIsCiAgICAgICAgY29uZmlnID0gImxpc3QiLAogICAgICAgIHJ1bGUgPSAiY2hhcmFjdGVyIgogICAgKQopCnNuYWtlbWFrZSA8LSBTbmFrZW1ha2UoCiAgICBpbnB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy90cmVlX2RhdGEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3N5bmFwc2VfY2xpbkFsbF9kYXRhLnRzdicsICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfYnJlYWtwb2ludHMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LWFuY2VzdHJ5LXNhbXBsZS9vdXRwdXQnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnRfd2l0aC1vdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX2ZpbmFsX3BhdGllbnRfc2lncGxvdC5wbmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21lbHRlZC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL3Jlc291cmNlcy9tbWN0bV9zYW1wbGVfc2lncGxvdC5wbmcnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvaWNnY19wcmltYXJ5X3R1bW91cl9zdWJ0eXBlcy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvd2ViL3Jlc291cmNlcy9tbWN0bV9vdl9jb21iaW5lZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9hZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2NvbWJpbmVkX292X21tY3RtL291dHB1dCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvb2xkX3Byb3BvcnRpb25fc3ViY2xvbmFsLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8yLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8zLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF80LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF83LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF85LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzEyLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTQudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE1LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xNi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTcudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvbW9sc3VidHlwZXMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9UUkIvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJ1JtZC9tdXRhdGlvbl9zaWduYXR1cmVzLlJtZCcsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGVfc2xpZGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9jbG9uZV9kYXRhLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0Mvc3BlY2ltZW4udHN2JywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL09WQVVfZXhwcl9tYXRyaXgudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9yZXNvdXJjZXMvbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90LnBuZycsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvYnJhbmNoX2RhdGEudHN2JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL25nLjM4NDktUzEyLnR4dCcsICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS90Y2dhX292X2Fubm90YXRpb25fc3VwMTMudHh0JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL2Rvbm9yLk9WLUFVLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnQtYW5jZXN0cnkvb3V0cHV0JywgJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL2V4cHJfbWF0cml4X25vcm1hbGl6ZV9zdGFuZGFyZGl6ZV9ub2R1cGxpY2F0ZXMudHN2JywgInRjZ2FfY2xpbmljYWwiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3N5bmFwc2VfY2xpbkFsbF9kYXRhLnRzdicsICJtYXN0ZXJfYnJlYWtwb2ludF9maWxlIiA9ICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfYnJlYWtwb2ludHMudHN2JywgIm1tY3RtX3BhdGllbnRfYWRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L291dHB1dCcsICJtbWN0bV9zYW1wbGVfYWRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1hbmNlc3RyeS1zYW1wbGUvb3V0cHV0JywgIm5hbm9zdHJpbmdfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9wYW5jYW5jZXJfYW5ub3RhdGlvbnMudHN2JywgIm1hc3Rlcl92YXJpYW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9zbnZzLnRzdicsICJpY2djX2V4cHJfbWVsdGVkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWVsdGVkLnRzdicsICJtbWN0bV9maW5hbF9wYXRpZW50X3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9yZXNvdXJjZXMvbW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90LnBuZycsICJjbG9uZV90cmVlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy90cmVlX2RhdGEudHN2JywgIm1tY3RtX3NhbXBsZV9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9zaWdwbG90LnBuZycsICJpY2djX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJtbWN0bV9vdl9jb21iaW5lZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QucG5nJywgIm1tY3RtX3NhbXBsZV9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3NhbXBsZV9hZF9zaWdwbG90LnBuZycsICJtbWN0bV9vdl9jb21iaW5lZF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgInN1YmNsb25hbGl0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL29sZF9wcm9wb3J0aW9uX3N1YmNsb25hbC50c3YnLCAic252X2NsdXN0ZXJfZmlsZXMiID0gYygnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfNC50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfNy50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfOS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTAudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzExLnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xMi50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE0LnRzdicsICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvc252X2NsdXN0ZXIvcGF0aWVudF8xNS50c3YnLCAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvY2xvbmVzL3Nudl9jbHVzdGVyL3BhdGllbnRfMTYudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2Nsb25lcy9zbnZfY2x1c3Rlci9wYXRpZW50XzE3LnRzdicpLCAiaXRoX2ljZ2NfYmMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YnLCAibW9sZWN1bGFyX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9tb2xzdWJ0eXBlcy50c3YnLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAibm90ZWJvb2siID0gJ1JtZC9tdXRhdGlvbl9zaWduYXR1cmVzLlJtZCcsICJpaGNfdGFibGVfc2xpZGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZV9zbGlkZS50c3YnLCAiY2xvbmVfcHJldmFsZW5jZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvY2xvbmVfZGF0YS50c3YnLCAibmFub3N0cmluZ19kYXRhIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9uYW5vc3RyaW5nX3Jlc3VsdHMvaXRoX2Z1bGwvcWMvbGltbWFfcXVhbnRpbGUvbm9ybWFsaXplZF9leHByZXNzaW9uX3ZvYV9sYWJlbHNfZmlsdGVyZWQudHN2JywgIm1tY3RtX3NhbXBsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAiaWNnY19zcGVjaW1lbiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0Mvc3BlY2ltZW4udHN2JywgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgInNpdGVfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL2FuYWx5c2lzL1JtZC9fc2l0ZS55bWwnLCAiaWNnY19leHByX21hdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21hdHJpeC50c3YnLCAibW1jdG1fcGF0aWVudF9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy93ZWIvcmVzb3VyY2VzL21tY3RtX3BhdGllbnRfYWRfc2lncGxvdC5wbmcnLCAiY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9jbG9uZXMvYnJhbmNoX2RhdGEudHN2JywgIndhbmdfZmJpX3N0YXR1cyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvbmcuMzg0OS1TMTIudHh0JywgInRjZ2Ffb3ZfYW5ub3RhdGlvbiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvdGNnYV9vdl9hbm5vdGF0aW9uX3N1cDEzLnR4dCcsICJpY2djX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9kb25vci5PVi1BVS50c3YnLCAiaWhjX3RhYmxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy90YWJsZXMvcnVuMi9paGNfdGFibGUudHN2JywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YnLCAidGNnYV9leHByX21hdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvZXhwcl9tYXRyaXhfbm9ybWFsaXplX3N0YW5kYXJkaXplX25vZHVwbGljYXRlcy50c3YnKSwKICAgIG91dHB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9tdXRhdGlvbl9zaWduYXR1cmVzLm5iLmh0bWwnKSwKICAgIHBhcmFtcyA9IGxpc3QoYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgJ2l0aGktYW5hbHlzaXMtbXV0YXRpb24tc2lnbmF0dXJlLW5vdGVib29rJywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgYygnMScsICcyJywgJzMnLCAnNCcsICc3JywgJzknLCAnMTAnLCAnMTEnLCAnMTInLCAnMTMnLCAnMTQnLCAnMTUnLCAnMTYnLCAnMTcnKSwgIm11dHNpZ190aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAibmFtZSIgPSAnaXRoaS1hbmFseXNpcy1tdXRhdGlvbi1zaWduYXR1cmUtbm90ZWJvb2snLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgInNudl9jbHVzdGVyX3BhdGllbnRzIiA9IGMoJzEnLCAnMicsICczJywgJzQnLCAnNycsICc5JywgJzEwJywgJzExJywgJzEyJywgJzEzJywgJzE0JywgJzE1JywgJzE2JywgJzE3JykpLAogICAgd2lsZGNhcmRzID0gbGlzdCgpLAogICAgdGhyZWFkcyA9IDEsCiAgICBsb2cgPSBsaXN0KCcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyL211dGF0aW9uX3NpZ25hdHVyZXMubG9nJyksCiAgICByZXNvdXJjZXMgPSBsaXN0KCksCiAgICBjb25maWcgPSBsaXN0KCJub3RlYm9va19kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYicsICJuY2x1c3RzIiA9IDMsICJ0YWJsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yJywgIm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vcGxvdHMvb3Zfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAibXZjbHVzdF9uY2x1c3QiID0gMywgIm11dGF0aW9uX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL211dGF0aW9uX3NpZ25hdHVyZXMuUm1kJywgImV4YW1wbGVfbXNhX3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjEzL29sZC9hbGlnbm1lbnRfcGxvdHMvbXNhL2l0aDJfMi9jbHVzdDkvaW5kZWxfcmV2ZXJzZWQuaHRtbCcsICJkZWZhdWx0X3NhbXBsZXIiID0gJ0hNQycsICJtbWN0bV9wYXRpZW50X2FuY2VzdHJhbF9kZXNjZW5kYW50X3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnQtYW5jZXN0cnkvb3V0cHV0JywgImJjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9JR0hfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAibWFzdGVyX3ZhcmlhbnRfZmlsZSIgPSAnL3NoYWhsYWIvYW1jcGhlcnNvbi9wcm9qZWN0cy9pdGgzL2l0aDMvbm90ZWJvb2tzL2Jlc3Bva2UvaXRoX3NudnMudHN2JywgImljZ2NfZXhwcl9tZWx0ZWQiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL09WQVVfZXhwcl9tZWx0ZWQudHN2JywgInNwYXRpYWxfbm90ZWJvb2siID0gJ1JtZC9zcGF0aWFsX2FuYWx5c2lzLlJtZCcsICJ0Y2dhX2NsaW5pY2FsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvVENHQS9zeW5hcHNlX2NsaW5BbGxfZGF0YS50c3YnLCAiaXRoX3N0YXRfdHlwZXMiID0gYygnZW50cm9weScsICdwb3N0cHJvY2Vzc2VkX2RpdmVyZ2VuY2UnLCAnY29tYmluZWRfaXRoX25vcm1hbGl6ZWQnLCAncHJvcG9ydGlvbl9zdWJjbG9uYWwnKSwgInRpbHNfZm9yX2NsdXN0ZXIiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgIml0aF9zdGF0aXN0aWNzX25vdGVib29rIiA9ICdSbWQvaXRoX3N0YXRpc3RpY3MuUm1kJywgInByZXZhbGVuY2VfdGhyZXNob2xkIiA9IDAuMDEsICJ0aWxfY2xhc3NpZmllcl9ub3RlYm9vayIgPSAnUm1kL3RpbF9jbGFzc2lmaWVyLlJtZCcsICJleGFtcGxlX2Fubm90YXRpb25zIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9maW5hbF9wYXJ0aXRpb25zL2l0aDJfMi9jbHVzdDkvYW5ub3RhdGlvbnNfZmxhZ2dlZC50c3YnLCAidmFyaWFiaWxpdHlfdHlwZSIgPSAnc3RhYmlsaXplJywgImltbXVuZV92YXJpYWJpbGl0eV9ub3RlYm9vayIgPSAnUm1kL2ltbXVuZV92YXJpYWJpbGl0eS5SbWQnLCAiaW1tdHlwZXJfbW9kZWxzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9pbW10eXBlcl9yZXN1bHRzL2tsYXJlbmJlZWsvYWFfdmovZ3JhZGJvb3N0JywgImJlbmNobWFya2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9iZW5jaG1hcmtzL3BhcGVyYW5hbHlzaXMyJywgIm5hbm9zdHJpbmdfZGF0YSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbmFub3N0cmluZ19yZXN1bHRzL2l0aF9mdWxsL3FjL2xpbW1hX3F1YW50aWxlL25vcm1hbGl6ZWRfZXhwcmVzc2lvbl92b2FfbGFiZWxzX2ZpbHRlcmVkLnRzdicsICJ4Y3JfbWFwcGluZ19ub3RlYm9vayIgPSAnUm1kL3hjcl9tYXBwaW5nLlJtZCcsICJpaGNfcnVuMSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaWhjL2NkOGNkM2NkMjAvdmFsaWRhdGVkX3N0YXRzX3dlaWdodGVkX25ldy5yZGF0YScsICJwaGVub3R5cGVfdGhyZXNob2xkIiA9IDAuODUsICJuZW9lZGl0aW5nX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbmVvZWRpdGluZy9ydW40JywgInRjZ2Ffb3ZfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL3RjZ2Ffb3ZfYW5ub3RhdGlvbl9zdXAxMy50eHQnLCAibG9nZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2NsdXN0dG1wL3BhcGVyYW5hbHlzaXMyJywgInhjcl9kaXN0YW5jZV9tZXRob2QiID0gJ2hvcm4nLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgImljZ2NfbW9sZWN1bGFyX3N1YnR5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9pY2djX3ByaW1hcnlfdHVtb3VyX3N1YnR5cGVzLnRzdicsICJuYW5vc3RyaW5nX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL25hbm9zdHJpbmdfc2lnbmF0dXJlcy5SbWQnLCAiaW50ZXJtZWRpYXRlX2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvaW50ZXJtZWRpYXRlcy9ydW4yJywgIlBOR19ERU5TSVRZIiA9IDMwMCwgImJjcnBoeWxvX2NvcnJlbGF0aW9uc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19jb3JyZWxhdGlvbnMuUm1kJywgIm1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YnLCAibW1jdG1fc2FtcGxlX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9wbG90cy9pdGgtYnktc2FtcGxlX3Nudi1zdl9zaWdzX211bHRpcGFuZWwucGRmJywgIml0aF90aWxfbm90ZWJvb2siID0gJ1JtZC9pdGhfdGlsX2RlbnNpdGllcy5SbWQnLCAiaW1tdHlwZXJfbGVuZ3RocyIgPSAnMTEgMTIgMTMgMTQgMTUgMTYgMTcgMTgnLCAieGNyX2Nsb25lc19ub3RlYm9vayIgPSAnUm1kL3hjcl9jbG9uZXNfYW5hbHlzaXMuUm1kJywgIm5lb2FudGlnZW5fZWRpdGluZ19ub3RlYm9vayIgPSAnUm1kL2ltbXVub2VkaXRpbmcuUm1kJywgImxpYnJhcnlfc2l6ZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9saWJyYXJ5X3NpemVzLnRzdicsICJpZ3BhcnRpdGlvbl9vdXRkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjIyJywgImRyaXZlcl9hbmFseXNpc19ub3RlYm9vayIgPSAnUm1kL2RyaXZlcl9hbmFseXNpcy5SbWQnLCAidGNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL1RSQi9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAidl9kaWN0aW9uYXJ5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvc3VicHJvamVjdHMvaW1tdHlwZXIvbWV0YWRhdGEvaW1ndC9Ib21vX3NhcGllbnNfVFJCVi5mYXN0YScsICJjbG9uZV9wcmV2YWxlbmNlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9jbG9uZV9kYXRhLnRzdicsICJpaGNfeGNyX3N0YXRzX25vdGVib29rIiA9ICdSbWQvaWhjX3hjcl9zdGF0cy5SbWQnLCAibW1jdG1fc2FtcGxlX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAibW1jdG1fYW5jZXN0cmFsX2Rlc2NlbmRhbnRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktYW5jZXN0cnktc2FtcGxlL291dHB1dCcsICJtdWx0aXZpZXdjbHVzdGVyaW5nX25vdGVib29rIiA9ICdSbWQvbXVsdGl2aWV3Y2x1c3RlcmluZy5SbWQnLCAiY2xvbmFsX3NhbXBsZXJzIiA9IGMoJ0hNQycsICdOVVRTJyksICJpdGhfcHJvamVjdF9yZXN1bHRzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL2l0aDMvZGF0YS9yZXN1bHRzJywgIm1hc3Rlcl9icmVha3BvaW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9icmVha3BvaW50cy50c3YnLCAiYmNycGh5bG9fdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImJjcl9kaXZlcnNpdHkiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9wb3N0cHJvY2Vzcy9JR0gvcG9zdGZpbHRlcl9kaXZlcnNpdHlfc3RhdHMvZGl2ZXJzaXR5LnN0cmljdC5yZXNhbXBsZWQudHh0JywgIlBOR19RVUFMSVRZIiA9IDMwMCwgImNsb25lX3RyZWVfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL3RyZWVfZGF0YS50c3YnLCAibW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50X3dpdGgtb3YvcGxvdHMvaXRoLWJ5LXBhdGllbnRfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAic2l0ZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvYW5hbHlzaXMvUm1kL19zaXRlLnltbCcsICJwYXRpZW50c19mb3JfY2xvbmFsIiA9IGMoMSwgMiwgMywgNCwgNywgOSwgMTAsIDExLCAxMiwgMTMsIDE0LCAxNSwgMTYsIDE3KSwgIm1tY3RtX3NhbXBsZV9hZF9zaWdwbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1hbmNlc3RyeS1zYW1wbGUvcGxvdHMvaXRoLWJ5LWFuY2VzdHJhbC1zYW1wbGVfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAic2FkX25vdGVib29rIiA9ICdSbWQvc3BlY2llc19hYnVuZGFuY2VfZGlzdHJpYnV0aW9ucy5SbWQnLCAieGNyX3FjX25vdGVib29rIiA9ICdSbWQvcmVwbGljYXRlcy5SbWQnLCAibXZjbHVzdF90aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScpLCAibXV0c2lnX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JywgJ1RfQ0Q4X2RlbnNpdHknLCAnVF9DRDRfZGVuc2l0eScsICdUX0NEMjBfZGVuc2l0eScsICdUX1BsYXNtYV9kZW5zaXR5JyksICJpY2djX25vcm1hbGl6ZWRfcmVhZHNfbWF0cml4IiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWF0cml4LnRzdicsICJ4Y3JtYXBzY2FwZV9ub3RlYm9vayIgPSAnUm1kL3hjcm1hcHNjYXBlLlJtZCcsICJrbm93bl9zdWJ0eXBlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vYXJyYXkvc3VidHlwZXMva25vd25fc3VidHlwZXMudHN2JywgInRjcl9jbG9ub3R5cGVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvY2xvbm90eXBlcy9UUkJfY2xvbm90eXBlc19maWx0ZXJlZC50eHQnLCAiY2xhc3NpZmllcl90eXBlIiA9ICdrbm4nLCAidGNnYV9leHByX21hdHJpeCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvZXhwcl9tYXRyaXhfbm9ybWFsaXplX3N0YW5kYXJkaXplX25vZHVwbGljYXRlcy50c3YnLCAiZmlndXJlX2dhbGxlcnlfbm90ZWJvb2siID0gJ1JtZC9maWd1cmVzLlJtZCcsICJjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9icmFuY2hfZGF0YS50c3YnLCAid2FuZ19mYmlfc3RhdHVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9uZy4zODQ5LVMxMi50eHQnLCAiaWNnY19jbGluaWNhbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvZG9ub3IuT1YtQVUudHN2JywgImloY19ydW4yIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9paGMvY2Q3OWNkMTM4Y2Q2OC92YWxpZGF0ZWRfc3RhdHNfd2VpZ2h0ZWQucmRhdGEnLCAibmFub3N0cmluZ19hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9uYW5vc3RyaW5nL3BhbmNhbmNlcl9hbm5vdGF0aW9ucy50c3YnLCAiaWNnY19zcGVjaW1lbl9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9zcGVjaW1lbi50c3YnLCAicHJvcG9ydGlvbl9zdWJjbG9uYWxfZmlsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL29sZF9wcm9wb3J0aW9uX3N1YmNsb25hbC50c3YnLCAibW9sc3VidHlwZV9ub3RlYm9vayIgPSAnUm1kL21vbGVjdWxhcl9zdWJ0eXBlcy5SbWQnLCAiZHJpdmVyX21hcCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2RyaXZlcnMvZGF0YS9nZW5lX2xpc3RfbWFwcGVkLmJlZCcsICJqX2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJKLmZhc3RhJywgImloY194Y3JfdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgInN1YnR5cGVfbWFya2VyX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9zdWJ0eXBlX21hcmtlcnMudHN2JywgIm1tY3RtX292X2NvbWJpbmVkX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgIml0aF9zdGF0c19maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICJtb2xzdWJ0eXBlX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJ0aWxzX2Zvcl92YXJpYWJpbGl0eSIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScpLCAic3BhdGlhbF9yZXN1bHRfZGlycyIgPSBsaXN0KCJlcGl0aGVsaWFsIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9zcGF0c2ltL2l0aDMvYWJjJywgInN0cm9tYWwiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL3NwYXRzaW0vaXRoNS9hYmMnKSwgImtub3duX3N1YnR5cGVzX21lcmdlZCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvZXhwcmVzc2lvbi9rbm93bl9zdWJ0eXBlc19tZXJnZWQudHN2JywgIm1tY3RtX3BhdGllbnRfYWRfc2lncGxvdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktcGF0aWVudC1hbmNlc3RyeS9wbG90cy9pdGgtYnktcGF0aWVudC1hbmNlc3RyeV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJpbmRleF9ub3RlYm9vayIgPSAnUm1kL2luZGV4LlJtZCcsICJtYXBzY2FwZV9ub3RlYm9vayIgPSAnUm1kL21hcHNjYXBlLlJtZCcsICJiY3JwaHlsb19leGFtcGxlc19ub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19leGFtcGxlcy5SbWQnKSwKICAgIHJ1bGUgPSAnbXV0YXRpb25fc2lnbmF0dXJlX25vdGVib29rJwopCiMjIyMjIyMjIE9yaWdpbmFsIHNjcmlwdCAjIyMjIyMjIyMKCiAgICAgICAgICAgICAgICAgICAgICAgIGBgYAoKCmBgYHtyIGdsb2JhbF9jaHVua19vcHRpb25zLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHRpZHk9VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoaXRoaS51dGlscykKbG9hZF9iYXNlX2xpYnMoKQpsaWJyYXJ5KHN1cnZpdmFsKQpsaWJyYXJ5KHJtcykKbGlicmFyeShlZGdlUikKbGlicmFyeShsaW1tYSkKbGlicmFyeShnYWdlKQpsaWJyYXJ5KHBhdGh2aWV3KQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGFwZSkKbGlicmFyeShwaHl0b29scykKbGlicmFyeShwaHlsb2Jhc2UpCgpsaWJyYXJ5KGl0aGkubWV0YSkKbGlicmFyeShpdGhpLnhjcikKbGlicmFyeShpdGhpLmloYykKbGlicmFyeShpdGhpLnNlcSkKbGlicmFyeShpdGhpLmNsb25lcykKbGlicmFyeShpdGhpLmV4cHIpCmxpYnJhcnkoaXRoaS5leHRlcm5hbCkKYGBgCgojIyBDb2xvdXIgcGFsZXR0ZXMKCmBgYHtyfQpwYWxfcGF0aWVudCA8LSBzZWxlY3RfcGFsZXR0ZSgicGF0aWVudCIpCmBgYAoKIyMgUGFyYW1ldGVycwoKYGBge3J9CmRiX3BhdGggPC0gc25ha2VtYWtlQHBhcmFtcyRkYgoKbW1jdG1fc2FtcGxlX3Jlc3VsdF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9kaXIKbW1jdG1fc2FtcGxlX2FkX3Jlc3VsdF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9hZF9kaXIKbW1jdG1fcGF0aWVudF9hZF9yZXN1bHRfZGlyIDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9wYXRpZW50X2FkX2RpcgptbWN0bV9vdl9jb21iaW5lZF9yZXN1bHRfZGlyIDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9vdl9jb21iaW5lZF9kaXIKbW1jdG1fZmluYWxfcGF0aWVudF9kaXIgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX2ZpbmFsX3BhdGllbnRfZGlyCgptbWN0bV9zYW1wbGVfc2lncGxvdCA8LSBzbmFrZW1ha2VAaW5wdXQkbW1jdG1fc2FtcGxlX3NpZ3Bsb3QKbW1jdG1fc2FtcGxlX2FkX3NpZ3Bsb3QgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX3NhbXBsZV9hZF9zaWdwbG90Cm1tY3RtX3BhdGllbnRfYWRfc2lncGxvdCA8LSBzbmFrZW1ha2VAaW5wdXQkbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90Cm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QgPC0gc25ha2VtYWtlQGlucHV0JG1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QKbW1jdG1fZmluYWxfcGF0aWVudF9zaWdwbG90IDwtIHNuYWtlbWFrZUBpbnB1dCRtbWN0bV9maW5hbF9wYXRpZW50X3NpZ3Bsb3QKCm1hc3Rlcl92YXJpYW50X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JG1hc3Rlcl92YXJpYW50X2ZpbGUKbWFzdGVyX2JyZWFrcG9pbnRfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkbWFzdGVyX2JyZWFrcG9pbnRfZmlsZQoKaWhjX3RhYmxlX3BhdGggPC0gc25ha2VtYWtlQGlucHV0JGloY190YWJsZQppaGNfdGFibGVfc2xpZGVfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkaWhjX3RhYmxlX3NsaWRlCnRpbHR5cGVzIDwtIHNuYWtlbWFrZUBwYXJhbXMkbXV0c2lnX3RpbHR5cGVzCgp0Y3JfZGl2ZXJzaXR5X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JHRjcl9kaXZlcnNpdHkKYmNyX2RpdmVyc2l0eV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRiY3JfZGl2ZXJzaXR5CgpuYW5vc3RyaW5nX2RhdGFfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19kYXRhCm5hbm9zdHJpbmdfYW5ub3RhdGlvbnNfcGF0aCA8LSBzbmFrZW1ha2VAaW5wdXQkbmFub3N0cmluZ19hbm5vdGF0aW9ucwoKbW9sZWN1bGFyX3N1YnR5cGVfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkbW9sZWN1bGFyX3N1YnR5cGVzCgppY2djX2V4cHJfbWF0X2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGljZ2NfZXhwcl9tYXQKaWNnY19zcGVjaW1lbl9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRpY2djX3NwZWNpbWVuCmljZ2Nfc3VidHlwZV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRpY2djX3N1YnR5cGVzCmljZ2NfZXhwcl9yYXdfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkaWNnY19leHByX21lbHRlZAppY2djX2Rvbm9yX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGljZ2NfY2xpbmljYWwKCnRjZ2FfZXhwcl9tYXRfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9leHByX21hdAp0Y2dhX292X2Fubm90YXRpb25fZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9vdl9hbm5vdGF0aW9uCnRjZ2FfZG9ub3JfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkdGNnYV9jbGluaWNhbAoKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkc3ViY2xvbmFsaXR5Cgp3YW5nX2ljZ2NfZmJpX3N0YXR1c19maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCR3YW5nX2ZiaV9zdGF0dXMKCnNudl9jbHVzdGVyX2ZpbGVzIDwtIHNuYWtlbWFrZUBpbnB1dCRzbnZfY2x1c3Rlcl9maWxlcwpzbnZfY2x1c3Rlcl9wYXRpZW50cyA8LSBzbmFrZW1ha2VAcGFyYW1zJHNudl9jbHVzdGVyX3BhdGllbnRzCgpjbG9uZV90cmVlX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX3RyZWVfZmlsZQpjbG9uZV9wcmV2YWxlbmNlX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX3ByZXZhbGVuY2VfZmlsZQpjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGNsb25lX2JyYW5jaF9sZW5ndGhfZmlsZQoKaXRoX2ljZ2NfYmF0Y2hfY29ycmVjdGVkX2V4cHJlc3Npb25fZmlsZSA8LSBzbmFrZW1ha2VAaW5wdXQkaXRoX2ljZ2NfYmMKYGBgCgojIyBNZXRhZGF0YQoKYGBge3J9CmRiIDwtIHNyY19zcWxpdGUoZGJfcGF0aCwgY3JlYXRlPUZBTFNFKQpzYW1wbGVzIDwtIGNvbGxlY3QodGJsKGRiLCAic2FtcGxlcyIpKQpgYGAKCmBgYHtyfQpyZWFkX211dHNpZ19vdXRwdXQgPC0gZnVuY3Rpb24oZGlyLCBzYW1wbGVfdGFibGUsIGV4dGVybmFsPUZBTFNFKSB7CiAgcHJvcF90YWJsZV9wYXRoIDwtIFN5cy5nbG9iKGZpbGUucGF0aChkaXIsICIqX3Byb3BzLnRzdiIpKQogIHNpZ3NfcGF0aCA8LSBTeXMuZ2xvYihmaWxlLnBhdGgoZGlyLCAiLi4vcGxvdHMvKl9tdWx0aXBhbmVsLnBkZiIpKQogIAogIHByb3BfdGFibGUgPC0gZnJlYWQocHJvcF90YWJsZV9wYXRoKQogIAogIHNhbXBsZV9jb2xzIDwtIGNvbG5hbWVzKHByb3BfdGFibGUpW2NvbG5hbWVzKHByb3BfdGFibGUpICE9ICJzaWduYXR1cmUiXQogIGl0aF9jb2xzIDwtIHN0cl9kZXRlY3Qoc2FtcGxlX2NvbHMsICJwYXRpZW50IikKICAKICBwYXRpZW50cyA8LSBzdHJfZXh0cmFjdChzYW1wbGVfY29sc1tpdGhfY29sc10sICIoPzw9cGF0aWVudF8/KVswLTldKyIpCiAgc2l0ZV9pZHMgPC0gc3RyX3JlcGxhY2VfYWxsKHNhbXBsZV9jb2xzW2l0aF9jb2xzXSwgInBhdGllbnRfP1swLTldK18iLCAiIikKICAKICBGUk9NIDwtIGMoInJlY3R1bV9zaXRlXzEiLCAicmVjdHVtX3NpdGVfMiIsICJyZWN0dW1fc2l0ZV8zIiwgInJlY3R1bV9zaXRlXzQiLAogICAgICAgICAgICAicGVsdmlzX3NpdGVfMSIsICJwZWx2aXNfc2l0ZV8yIiwgImN1bF9kZV9zYWNfc2l0ZV8xIikKICBUTyA8LSBjKCJyZWN0dW1fc2l0ZV8xIiwgInJlY3R1bV9zaXRlXzEiLCAicmVjdHVtX3NpdGVfMiIsICJyZWN0dW1fc2l0ZV8yIiwKICAgICAgICAgICJwZWx2aXNfc2l0ZV8xIiwgInBlbHZpc19zaXRlXzEiLCAiY3VsLWRlLXNhY19zaXRlXzEiKQogIAogIHNpdGVfaWRzX2ZpeGVkIDwtIG1hcHZhbHVlcyhzaXRlX2lkcywgZnJvbSA9IEZST00sIHRvPVRPKQogIGRmIDwtIGRhdGEuZnJhbWUocGF0aWVudF9pZD1wYXRpZW50cywgc2l0ZV9pZD1zaXRlX2lkc19maXhlZCwgb2xkX2lkPXNhbXBsZV9jb2xzW2l0aF9jb2xzXSkKICBpZiAobGVuZ3RoKGRmW3dpdGgoZGYsIHBhdGllbnRfaWQgPT0gIjExIiAmIHNpdGVfaWQgPT0gImxlZnRfb3Zhcnlfc2l0ZV8yIiksXSRzaXRlX2lkKSA+IDApIHsKICAgIGRmW3dpdGgoZGYsIHBhdGllbnRfaWQgPT0gIjExIiAmIHNpdGVfaWQgPT0gImxlZnRfb3Zhcnlfc2l0ZV8yIiksXSRzaXRlX2lkIDwtICJsZWZ0X292YXJ5X3NpdGVfMyIKICB9CiAgCiAgc2FtcGxlX3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KHNhbXBsZV90YWJsZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgInNpdGVfaWQiKSkpCiAgCiAgZGYgPC0gcGx5cjo6am9pbihkZiwgc2FtcGxlX3N1YnNldCkKICBpZiAobnJvdyhkZikgPiAwKSB7CiAgICBkZiRpc19hbmNlc3RyYWwgPC0gMAogICAgZGYkaXNfYW5jZXN0cmFsW2RmJHNpdGVfaWQgPT0gImFuY2VzdHJhbCJdIDwtIDEKICB9CiAgCiAgI2NvbG5hbWVzKHByb3BfdGFibGUpW2l0aF9jb2xzXSA8LSBtYWtlLnVuaXF1ZShkZiRjb25kZW5zZWRfaWQpCiAgCiAgcHJvcF9maW5hbCA8LSBtZWx0KHByb3BfdGFibGUsIGlkLnZhcnMgPSBjKCdzaWduYXR1cmUnKSwgbWVhc3VyZS52YXJzID0gc2FtcGxlX2NvbHMsCiAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLm5hbWUgPSAib2xkX2lkIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKICBpZiAoZXh0ZXJuYWwpIHsKICAgIHByb3BfZmluYWxfbWVyZ2VkIDwtIHBseXI6OmpvaW4oZGYsIHByb3BfZmluYWwsIGJ5PWMoIm9sZF9pZCIpLCB0eXBlPSdmdWxsJykKICB9IGVsc2UgewogICAgcHJvcF9maW5hbF9tZXJnZWQgPC0gcGx5cjo6am9pbihkZiwgcHJvcF9maW5hbCwgYnk9Yygib2xkX2lkIiksIHR5cGU9J2lubmVyJykKICB9CiAgCiAgcHJvcF9maW5hbF9tZXJnZWQgPC0gcHJvcF9maW5hbF9tZXJnZWQgJT4lIG11dGF0ZShuZXdfaWQgPSBpZmVsc2Uoc2l0ZV9pZCAlaW4lIGMoImFuY2VzdHJhbCIsICJyZXNpZHVhbCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IHBhc3RlKHBhdGllbnRfaWQsIHNpdGVfaWQsIHNlcD0iXyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gY29uZGVuc2VkX2lkKSkKICBuYV9pZHggPC0gaXMubmEocHJvcF9maW5hbF9tZXJnZWQkbmV3X2lkKQogIHByb3BfZmluYWxfbWVyZ2VkJG5ld19pZFtuYV9pZHhdIDwtIGFzLmNoYXJhY3Rlcihwcm9wX2ZpbmFsX21lcmdlZCRvbGRfaWRbbmFfaWR4XSkKICAKICByZXN1bHQgPC0gbGlzdChwcm9wPXByb3BfZmluYWxfbWVyZ2VkLCBzaWdwbG90PXNpZ3NfcGF0aCkKICByZXR1cm4ocmVzdWx0KQp9CgpzdW1fdG90YWxfdmFyaWFudF9jb3VudHMgPC0gZnVuY3Rpb24odmFyaWFudF9zYW1wbGUpIHsKICB2YXJpYW50X3NhbXBsZSAlPiUgZ3JvdXBfYnkoY29uZGVuc2VkX2lkLCBwYXRpZW50X2lkKSAlPiUgCiAgICBzdW1tYXJpc2Uoc252X2NvdW50PXN1bShzbnZfY291bnQpLCBia3B0X2NvdW50PXN1bShia3B0X2NvdW50KSkKfQoKY29tcHV0ZV9tdXRzaWdfY291bnRzIDwtIGZ1bmN0aW9uKGRmKSB7CiAgZGYgPC0gZGYgJT4lIG11dGF0ZShpc19zdiA9IGFzLm51bWVyaWMoc3RyX2RldGVjdChzaWduYXR1cmUsICJeU1YiKSksCiAgICAgICAgICAgICAgICBzaWdjb3VudCA9IGlmZWxzZShhcy5jaGFyYWN0ZXIoc2l0ZV9pZCkgPT0gImFuY2VzdHJhbCIsCiAgICAgICAgICAgICAgICAgIHllcyA9IGlmZWxzZShpc19zdiwgeWVzID0gYW5jZXN0cmFsX2JrcHQqcHJvcG9ydGlvbiwgbm8gPSBhbmNlc3RyYWxfc252KnByb3BvcnRpb24pLAogICAgICAgICAgICAgICAgICBubyA9IGlmZWxzZShpc19zdiwgeWVzPWJrcHRfY291bnQgKiBwcm9wb3J0aW9uLCBubyA9IHNudl9jb3VudCAqIHByb3BvcnRpb24pKSkKICByZXR1cm4oZGYpCn0KCmNyZWF0ZV9tdXRzaWdfbWF0cml4IDwtIGZ1bmN0aW9uKGRmLCBjb2wgPSAicHJvcG9ydGlvbiIpIHsKICBtYXQgPC0gYWNhc3QoZGYsIG5ld19pZCB+IHNpZ25hdHVyZSwgZnVuLmFnZ3JlZ2F0ZSA9IG1lYW4sIHZhbHVlLnZhciA9IGNvbCkKICByZXR1cm4obWF0KQp9CmBgYAoKYGBge3J9CnZhcmlhbnRfcmVzIDwtIHN1bW1hcml6ZV92YXJpYW50X2NvdW50cyhtYXN0ZXJfdmFyaWFudF9maWxlLCBtYXN0ZXJfYnJlYWtwb2ludF9maWxlLCBkYl9wYXRoKQp2YXJpYW50X3NhbXBsZSA8LSB2YXJpYW50X3JlcyRzYW1wbGUKdmFyaWFudF9wYXRpZW50IDwtIHZhcmlhbnRfcmVzJHBhdGllbnQKCnZhcmlhbnRfc2FtcGxlX3N1bSA8LSBzdW1fdG90YWxfdmFyaWFudF9jb3VudHModmFyaWFudF9zYW1wbGUpCgppaGNfdGFibGUgPC0gZnJlYWQoaWhjX3RhYmxlX3BhdGgpCmloY190YWJsZV9zdWJzZXQgPC0gc3Vic2V0KGloY190YWJsZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsIHRpbHR5cGVzKSkKaWhjX3RhYmxlX3NsaWRlIDwtIGZyZWFkKGloY190YWJsZV9zbGlkZV9wYXRoKQpgYGAKCiMjIFNhbXBsZSBsZXZlbAoKIyMjIFNpZ25hdHVyZXMKCiFbXShgciBtbWN0bV9zYW1wbGVfc2lncGxvdGApCgoKIyMjIFJlc3VsdHMKCmBgYHtyfQpwcm9wc19zYW1wbGUgPC0gcmVhZF9tdXRzaWdfb3V0cHV0KG1tY3RtX3NhbXBsZV9yZXN1bHRfZGlyLCBzYW1wbGVzKQpwcm9wc19zYW1wbGVfcHJvcCA8LSBzdWJzZXQocHJvcHNfc2FtcGxlJHByb3AsIHBhdGllbnRfaWQgIT0gIjExIikKCnByb3BzX3NhbXBsZV9tYXQgPC0gY3JlYXRlX211dHNpZ19tYXRyaXgocHJvcHNfc2FtcGxlX3Byb3AsIGNvbCA9ICJwcm9wb3J0aW9uIikKCnByb3BzX3NhbXBsZV9tYXRfc2NhbGVkIDwtIGNsaXBfdmFsdWVzKHNjYWxlKHByb3BzX3NhbXBsZV9tYXQpLCAyLCAtMikKc2FtcGxlX2hlYXQgPC0gcGhlYXRtYXAocHJvcHNfc2FtcGxlX21hdF9zY2FsZWQsIGZvbnRzaXplX3JvdyA9IDUsIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiKQpgYGAKCmBgYHtyfQpwcm9wc19zYW1wbGVfbWVyZ2VkIDwtIFJlZHVjZShmdW5jdGlvbih4LHkpIHBseXI6OmpvaW4oeCx5KSxsaXN0KHByb3BzX3NhbXBsZV9wcm9wLCB2YXJpYW50X3NhbXBsZV9zdW0sIHZhcmlhbnRfcGF0aWVudCwgaWhjX3RhYmxlX3N1YnNldCkpCgpwcm9wc19zYW1wbGVfbWVyZ2VkIDwtIGNvbXB1dGVfbXV0c2lnX2NvdW50cyhwcm9wc19zYW1wbGVfbWVyZ2VkKQpgYGAKCmBgYHtyfQpDT0wgPC0gIkVfQ0Q4X2RlbnNpdHkiCm1lYXN1cmUgPC0gInNpZ2NvdW50IgoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkocHJvcHNfc2FtcGxlX21lcmdlZCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGRmIDwtIGRmWyFpcy5uYShkZlssQ09MXSksXQogIGNvcnJlcyA8LSBzdW1tYXJ5KGxtZXIoYXMuZm9ybXVsYShwYXN0ZTAobWVhc3VyZSwgIiB+ICIsIENPTCwgIiArICgxfHBhdGllbnRfaWQpIikpLCBkZikpCiAgCiAgcHZhbCA8LSB1bm5hbWUoY29ycmVzJGNvZWZmaWNpZW50c1ssNV1bMl0pCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfc2FtcGxlX21lcmdlZCwgYWVzX3N0cmluZyh4PUNPTCwgeT1tZWFzdXJlKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gc2lnbmF0dXJlLCBzY2FsZXM9ImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpICsgCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQojIHggPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgbWVyZ2UoeCx5LCBieT1jKCJjb25kZW5zZWRfaWQiKSksIGxpc3Qocm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUodChwcm9wX21hdHJpeCkpLCB2YXIgPSAiY29uZGVuc2VkX2lkIiksIAojICAgICAgICAgICAgaWhjX3RhYmxlX3N1YnNldCkpCiMgeCRwYXRpZW50X2lkIDwtIG1hcF9pZCh4JGNvbmRlbnNlZF9pZCwgZnJvbSA9ICJjb25kZW5zZWRfaWQiLCB0bz0icGF0aWVudF9pZCIsIGRiX3BhdGgpCiMgeCA8LSBzdWJzZXQoeCwgcGF0aWVudF9pZCAhPSAiMTEiKQojIAojIGNvbHMgPC0gY29sbmFtZXMoeClbIWNvbG5hbWVzKHgpICVpbiUgYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKV0KIyBybWF0IDwtIG1hdHJpeChucm93PWxlbmd0aChjb2xzKSxuY29sPWxlbmd0aChjb2xzKSkKIyBwbWF0IDwtIG1hdHJpeChucm93PWxlbmd0aChjb2xzKSxuY29sPWxlbmd0aChjb2xzKSkKIyAKIyBmb3IgKGkgaW4gMTpsZW5ndGgoY29scykpIHsKIyAgIGZvciAoaiBpbiBpOmxlbmd0aChjb2xzKSkgewojICAgICBpZiAoaSAhPSBqKSB7CiMgICAgICAgY29sMSA8LSBjb2xzW2ldCiMgICAgICAgY29sMiA8LSBjb2xzW2pdCiMgICAgICAgZm9ybXVsYSA8LSBwYXN0ZTAoImAiLCBjb2wyLCAiYCIsICJ+IiwgImAiLCBjb2wxLCAiYCIsICIrICgxfHBhdGllbnRfaWQpIiwgc2VwPSIgIikKIyAgICAgICByZXMgPC0gc3VtbWFyeShsbWVyKGZvcm11bGEsIHgpKQojICAgICAgIHB2YWwgPC0gdHJ5Q2F0Y2goewojICAgICAgICAgdW5uYW1lKHJlcyRjb2VmZmljaWVudHNbLDVdWzJdKQojICAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewojICAgICAgICAgTkEKIyAgICAgICB9KQojICAgICAgIHIgPC0gdW5uYW1lKHJlcyRjb2VmZmljaWVudHNbLDFdWzJdKQojICAgICAgIHJtYXRbaSxqXSA8LSBybWF0W2osaV0gPC0gcgojICAgICAgIHBtYXRbaSxqXSA8LSBwbWF0W2osaV0gPC0gcHZhbAojICAgICB9IGVsc2UgewojICAgICAgIHJtYXRbaSxqXSA8LSAxCiMgICAgICAgcG1hdFtpLGpdIDwtIE5BCiMgICAgIH0KIyAgIH0KIyB9CgpgYGAKCmBgYHtyfQojIHJlc21hdCA8LSBsb2cxMChwbWF0KSotc2lnbihybWF0KQojIHJlc21hdFtyZXNtYXQgPT0gSW5mXSA8LSBOQQojIHJvd25hbWVzKHJlc21hdCkgPC0gY29sbmFtZXMocmVzbWF0KSA8LSBjb2xzCiMgCiMgcGhlYXRtYXAocmVzbWF0LCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYocG1hdCwgMyksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBGQUxTRSwgZm9udHNpemUgPSA1KQpgYGAKCmBgYHtyfQojIHhtYXQgPC0geCAlPiUgc2VsZWN0KC1vbmVfb2YoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIikpCiMgCiMgY29ybWF0IDwtIGNvcnIudGVzdCh4bWF0LCBtZXRob2Q9InNwZWFybWFuIiwgYWRqdXN0PSJmZHIiKQojIHBoZWF0bWFwKGNvcm1hdCRyLCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYoY29ybWF0JHAsIDMpLCBmb250c2l6ZSA9IDUpCmBgYAoKCiMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsIChzYW1wbGVzKQoKIyMjIFNpZ25hdHVyZXMKCiFbXShgciBtbWN0bV9zYW1wbGVfYWRfc2lncGxvdGApCgojIyMgUmVzdWx0cwoKYGBge3J9CnByb3BzX2FkIDwtIHJlYWRfbXV0c2lnX291dHB1dChtbWN0bV9zYW1wbGVfYWRfcmVzdWx0X2Rpciwgc2FtcGxlcykKcHJvcHNfYWRfcHJvcCA8LSBzdWJzZXQocHJvcHNfYWQkcHJvcCwgcGF0aWVudF9pZCAhPSAiMTEiKQoKcHJvcHNfYWRfbWF0IDwtIGNyZWF0ZV9tdXRzaWdfbWF0cml4KHByb3BzX2FkX3Byb3AsIGNvbCA9ICJwcm9wb3J0aW9uIikKCnByb3BzX2FkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMoc2NhbGUocHJvcHNfYWRfbWF0KSwgMiwgLTIpCnBhdGllbnRfaGVhdCA8LSBwaGVhdG1hcChwcm9wc19hZF9tYXRfc2NhbGVkLCBmb250c2l6ZV9yb3cgPSA1LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCmBgYHtyfQpwcm9wc19hZF9tZXJnZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHkpLGxpc3QocHJvcHNfYWRfcHJvcCwgc3Vic2V0KHZhcmlhbnRfc2FtcGxlLCBpc19hbmNlc3RyYWwgPT0gMCksIHZhcmlhbnRfcGF0aWVudCwgaWhjX3RhYmxlX3N1YnNldCkpCgpwcm9wc19hZF9tZXJnZWQgPC0gY29tcHV0ZV9tdXRzaWdfY291bnRzKHByb3BzX2FkX21lcmdlZCkKYGBgCgpgYGB7cn0KQ09MIDwtICJFX0NEOF9kZW5zaXR5IgptZWFzdXJlIDwtICJzaWdjb3VudCIKCnByb3BzX2FkX21lcmdlZF9kZXNjZW5kYW50IDwtIHN1YnNldChwcm9wc19hZF9tZXJnZWQsIHNpdGVfaWQgIT0gImFuY2VzdHJhbCIpCgpwdmFscyA8LSBzZXROYW1lcyhkZHBseShwcm9wc19hZF9tZXJnZWRfZGVzY2VuZGFudCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGRmIDwtIGRmWyFpcy5uYShkZlssQ09MXSksXQogIGNvcnJlcyA8LSBzdW1tYXJ5KGxtZXIoYXMuZm9ybXVsYShwYXN0ZTAobWVhc3VyZSwgIiB+ICIsIENPTCwgIiArICgxfHBhdGllbnRfaWQpIikpLCBkZikpCiAgCiAgcHZhbCA8LSB1bm5hbWUoY29ycmVzJGNvZWZmaWNpZW50c1ssNV1bMl0pCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfYWRfbWVyZ2VkX2Rlc2NlbmRhbnQsIGFlc19zdHJpbmcoeD1DT0wsIHk9bWVhc3VyZSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPXBhdGllbnRfaWQpKSArIAogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHNpZ25hdHVyZSwgc2NhbGVzPSJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKSArIAogIHRoZW1lX2J3KCkKYGBgCgpXaGF0IHdlIGNhbiBhbHNvIHNlZSBmcm9tIHRoZXNlIHBsb3RzIGlzIHRoYXQgbm90IHJlc29sdmluZyBTTlYtNyAodGhlIG5vaXNlIGNsdXN0ZXIpIGlzIGdvaW5nIHRvIGJlIGEgcHJvYmxlbS4gVGhlIHNpZ25hdHVyZSBwcm9wb3J0aW9uIHZhbHVlcyBmcm9tIHRoYXQgY2x1c3RlciBhcmUgZmFpcmx5IHNpbWlsYXIgdG8gdGhvc2UgZnJvbSBTTlYtNiwgdGhlIEFQT0JFQyBzaWduYXR1cmUuIAoKCiMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsIChwYXRpZW50cykKCiMjIyBTaWduYXR1cmVzCgohW10oYHIgbW1jdG1fcGF0aWVudF9hZF9zaWdwbG90YCkKCiMjIyBSZXN1bHRzCgpgYGB7cn0KcHJvcHNfcGF0aWVudF9hZCA8LSByZWFkX211dHNpZ19vdXRwdXQobW1jdG1fcGF0aWVudF9hZF9yZXN1bHRfZGlyLCBzYW1wbGVzKQpwcm9wc19wYXRpZW50X2FkX3Byb3AgPC0gc3Vic2V0KHByb3BzX3BhdGllbnRfYWQkcHJvcCwgcGF0aWVudF9pZCAhPSAiMTEiKQoKcHJvcHNfcGF0aWVudF9hZF9tYXQgPC0gY3JlYXRlX211dHNpZ19tYXRyaXgocHJvcHNfcGF0aWVudF9hZF9wcm9wLCBjb2wgPSAicHJvcG9ydGlvbiIpCgpwcm9wc19wYXRpZW50X2FkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMoc2NhbGUocHJvcHNfcGF0aWVudF9hZF9tYXQpLCAyLCAtMikKcGF0aWVudF9hZF9oZWF0IDwtIHBoZWF0bWFwKHByb3BzX3BhdGllbnRfYWRfbWF0X3NjYWxlZCwgZm9udHNpemVfcm93ID0gNSwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIikKYGBgCgpgYGB7cn0KcHJvcHNfcGF0aWVudF9hZF9tZXJnZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHkpLGxpc3QocHJvcHNfcGF0aWVudF9hZF9wcm9wLCBzdWJzZXQodmFyaWFudF9zYW1wbGUsIGlzX2FuY2VzdHJhbCA9PSAwKSwgdmFyaWFudF9wYXRpZW50KSkKCnByb3BzX3BhdGllbnRfYWRfbWVyZ2VkIDwtIGNvbXB1dGVfbXV0c2lnX2NvdW50cyhwcm9wc19wYXRpZW50X2FkX21lcmdlZCkKYGBgCgoKIyMgQ29ycmVsYXRpb24gbWF0cmljZXMKCmBgYHtyfQppaGNfbWF0IDwtIGFzLmRhdGEuZnJhbWUoaWhjX3RhYmxlX3N1YnNldCAlPiUgZHBseXI6OnNlbGVjdCgtY29uZGVuc2VkX2lkKSkKcm93bmFtZXMoaWhjX21hdCkgPC0gaWhjX3RhYmxlX3N1YnNldCRjb25kZW5zZWRfaWQKCiNUT1RBTF9USUxfQ09MUyA8LSBjKCJUX0NEOF9kZW5zaXR5IiwgIlRfQ0Q0X2RlbnNpdHkiLCAiVF9DRDIwX2RlbnNpdHkiLCAiVF9QbGFzbWFfZGVuc2l0eSIpCiNpaGNfbWF0IDwtIHN1YnNldChpaGNfbWF0LCBzZWxlY3Q9VE9UQUxfVElMX0NPTFMpCmBgYAoKYGBge3J9CmNvbXB1dGVfaWhjX211dHNpZ19jb3IgPC0gZnVuY3Rpb24oaWhjX21hdCwgbXV0c2lnX21hdCwgcGF0aWVudF9zdW1tYXJ5ID0gRkFMU0UsIGFuY2VzdHJhbCA9IEZBTFNFKSB7CiAgaWYgKHBhdGllbnRfc3VtbWFyeSkgewogICAgaWhjX21hdCA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZShpaGNfbWF0KSwgImlkIikKICAgIG11dHNpZ19tYXQgPC0gcm93bmFtZXNfdG9fY29sdW1uKGFzLmRhdGEuZnJhbWUobXV0c2lnX21hdCksICJpZCIpCiAgICAKICAgIGlmIChhbmNlc3RyYWwpIHsKICAgICAgbXV0c2lnX21hdCA8LSBzdWJzZXQobXV0c2lnX21hdCwgc3RyX2RldGVjdChpZCwgImFuY2VzdHJhbCIpKQogICAgfSBlbHNlIHsKICAgICAgbXV0c2lnX21hdCA8LSBzdWJzZXQobXV0c2lnX21hdCwgIXN0cl9kZXRlY3QoaWQsICJhbmNlc3RyYWwiKSkKICAgIH0KICAgIAogICAgaWhjX21hdCRwYXRpZW50X2lkIDwtIHN0cl9leHRyYWN0KGloY19tYXQkaWQsICJeWzAtOV0rIikKICAgIG11dHNpZ19tYXQkcGF0aWVudF9pZCA8LSBzdHJfZXh0cmFjdChtdXRzaWdfbWF0JGlkLCAiXlswLTldKyIpCiAgICAKICAgIGloY19tYXQgPC0gaWhjX21hdCAlPiUgZHBseXI6OnNlbGVjdCgtaWQpICU+JSBncm91cF9ieShwYXRpZW50X2lkKSAlPiUgc3VtbWFyaXNlX2VhY2goZnVucyhtZWFuKC4sIG5hLnJtPVRSVUUpKSkgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAicGF0aWVudF9pZCIpCiAgICBtdXRzaWdfbWF0IDwtIG11dHNpZ19tYXQgJT4lIGRwbHlyOjpzZWxlY3QoLWlkKSAlPiUgZ3JvdXBfYnkocGF0aWVudF9pZCkgJT4lIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVhbiguLCBuYS5ybT1UUlVFKSkpICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInBhdGllbnRfaWQiKQogICAgCiAgICBpaGNfbWF0IDwtIGFzLm1hdHJpeChpaGNfbWF0KQogICAgbXV0c2lnX21hdCA8LSBhcy5tYXRyaXgobXV0c2lnX21hdCkKICB9CiAgCiAgaW50ZXJzZWN0X3NhbXBsZXMgPC0gaW50ZXJzZWN0KHJvd25hbWVzKG11dHNpZ19tYXQpLCByb3duYW1lcyhpaGNfbWF0KSkKICBtdXRzaWcgPC0gbXV0c2lnX21hdFtpbnRlcnNlY3Rfc2FtcGxlcywsZHJvcD1GQUxTRV0KICBpaGMgPC0gaWhjX21hdFtpbnRlcnNlY3Rfc2FtcGxlcywsZHJvcD1GQUxTRV0KICAKICBwbWF0IDwtIG1hdHJpeChucm93PW5jb2wobXV0c2lnKSwgbmNvbD1uY29sKGloYykpCiAgcm1hdCA8LSBtYXRyaXgobnJvdz1uY29sKG11dHNpZyksIG5jb2w9bmNvbChpaGMpKQogIHJvd25hbWVzKHBtYXQpIDwtIHJvd25hbWVzKHJtYXQpIDwtIGNvbG5hbWVzKG11dHNpZykKICBjb2xuYW1lcyhwbWF0KSA8LSBjb2xuYW1lcyhybWF0KSA8LSBjb2xuYW1lcyhpaGMpCiAgCiAgZm9yIChpIGluIDE6bmNvbChtdXRzaWcpKSB7CiAgICBmb3IgKGogaW4gMTpuY29sKGloYykpIHsKICAgICAgY29ycmVzIDwtIGNvci50ZXN0KG11dHNpZ1ssaV0sIGloY1ssal0sIG1ldGhvZD0ic3BlYXJtYW4iKQogICAgICBwbWF0W2ksal0gPC0gY29ycmVzJHAudmFsdWUKICAgICAgcm1hdFtpLGpdIDwtIGNvcnJlcyRlc3RpbWF0ZQogICAgfQogIH0KICAKICByZXR1cm4obGlzdChwPXBtYXQsIHI9cm1hdCkpCn0KYGBgCgpGb3IgYWRqdXN0ZWQgcC12YWx1ZXMganVzdCBydW4gYHAuYWRqdXN0Lm1hdGAgb24gdGhlIHAtdmFsdWUgbGFiZWxzLiAKCiMjIyBTYW1wbGUtbGV2ZWwKCmBgYHtyfQppaGNfc2FtcGxlX2NvciA8LSBjb21wdXRlX2loY19tdXRzaWdfY29yKGloY19tYXQsIHByb3BzX3NhbXBsZV9tYXQpCgpwaGVhdG1hcChpaGNfc2FtcGxlX2NvciRyLCBkaXNwbGF5X251bWJlcnMgPSBzaWduaWYoaWhjX3NhbXBsZV9jb3IkcCwgMykpCgppaGNfc2FtcGxlX2Nvcl9zdW1tYXJ5IDwtIGNvbXB1dGVfaWhjX211dHNpZ19jb3IoaWhjX21hdCwgcHJvcHNfc2FtcGxlX21hdCwgcGF0aWVudF9zdW1tYXJ5ID0gVFJVRSkKCnBoZWF0bWFwKGloY19zYW1wbGVfY29yX3N1bW1hcnkkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19zYW1wbGVfY29yX3N1bW1hcnkkcCwgMykpCmBgYAoKIyMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsCgpgYGB7cn0KaWhjX2FkX2NvciA8LSBjb21wdXRlX2loY19tdXRzaWdfY29yKGloY19tYXQsIHByb3BzX2FkX21hdCkKCnBoZWF0bWFwKGloY19hZF9jb3IkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19hZF9jb3IkcCwgMykpCgppaGNfYWRfY29yX3N1bW1hcnkgPC0gY29tcHV0ZV9paGNfbXV0c2lnX2NvcihpaGNfbWF0LCBwcm9wc19hZF9tYXQsIHBhdGllbnRfc3VtbWFyeSA9IFRSVUUpCgpwaGVhdG1hcChpaGNfYWRfY29yX3N1bW1hcnkkciwgZGlzcGxheV9udW1iZXJzID0gc2lnbmlmKGloY19hZF9jb3Jfc3VtbWFyeSRwLCAzKSkKCmloY19hZF9jb3Jfc3VtbWFyeV9hbmNlc3RyYWwgPC0gY29tcHV0ZV9paGNfbXV0c2lnX2NvcihpaGNfbWF0LCBwcm9wc19hZF9tYXQsIHBhdGllbnRfc3VtbWFyeSA9IFRSVUUsIGFuY2VzdHJhbCA9IFRSVUUpCgpwaGVhdG1hcChpaGNfYWRfY29yX3N1bW1hcnlfYW5jZXN0cmFsJHIsIGRpc3BsYXlfbnVtYmVycyA9IHNpZ25pZihpaGNfYWRfY29yX3N1bW1hcnlfYW5jZXN0cmFsJHAsIDMpKQpgYGAKCk5vdGU6IHAtdmFsdWVzIHNob3duIGFyZSB1bmNvcnJlY3RlZC4gUGF0aWVudC1zdW1tYXJpemVkIGNvcnJlbGF0aW9ucyBhcmUgaW5zaWduaWZpY2FudCBhZnRlciBGRFIgY29ycmVjdGlvbi4gCgojIyBDbHVzdGVyLWxldmVsIGFuYWx5c2lzCgpGaW5kaW5nIGNvcnJlbGF0aW9ucyBhdCB0aGUgbGV2ZWwgb2YgaW5kaXZpZHVhbCBzaWduYXR1cmVzIGNhbiBiZSBkaWZmaWN1bHQuIEV2ZW4gbW9yZXNvIGJlY2F1c2Ugc29tZSBwdWJsaXNoZWQgc2lnbmF0dXJlcyBhcmUgY29tYmluYXRpb25zIG9mIHRoZXNlIHNpZ25hdHVyZXMgLS0gZS5nLiBTVi0zIGFuZCBTVi02IGFyZSBib3RoIGZvbGRiYWNrIHNpZ25hdHVyZXMgaW4gdGhlIGFuY2VzdHJhbC1kZXNjZW5kYW50IGFuYWx5c2lzLiAKCkhlbmNlLCB3ZSBjYW4gbG9vayBhdCB0aGUgbGV2ZWwgb2YgY2x1c3RlcnMgZnJvbSBvdXIgaGVhdG1hcHMuIAoKYGBge3J9Cm1ha2VfY2x1c3Rlcl9mcmFtZSA8LSBmdW5jdGlvbihjbHVzdGVycykgewogIGNsdXN0cyA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZShjbHVzdGVycyksICJuZXdfaWQiKQogIGNvbG5hbWVzKGNsdXN0cylbMl0gPC0gImNsdXN0ZXIiCiAgY2x1c3RzJGNsdXN0ZXIgPC0gZmFjdG9yKGNsdXN0cyRjbHVzdGVyKQogIHJldHVybihjbHVzdHMpCn0KCk5DTFVTVCA8LSAyCnNlbGVjdGVkX2NvbHMgPC0gYygibmV3X2lkIiwgImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgIm9sZF9pZCIsICJjbHVzdGVyIiwgdGlsdHlwZXMpCmBgYAoKIyMjIFNhbXBsZS1sZXZlbAoKYGBge3J9CmNsdXN0ZXJzIDwtIGN1dHJlZShzYW1wbGVfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpzYW1wbGVfY2x1c3RzIDwtIG1ha2VfY2x1c3Rlcl9mcmFtZShjbHVzdGVycykKCnByb3BzX3NhbXBsZV9tZXJnZWRfY2x1c3RzIDwtIGpvaW4ocHJvcHNfc2FtcGxlX21lcmdlZCwgc2FtcGxlX2NsdXN0cykKc2FtcGxlX2RmIDwtIHVuaXF1ZShzdWJzZXQocHJvcHNfc2FtcGxlX21lcmdlZF9jbHVzdHMsIHNlbGVjdD1zZWxlY3RlZF9jb2xzKSkKCnNhbXBsZV9kZl9tZWx0ZWQgPC0gbWVsdChzYW1wbGVfZGYsIGlkLnZhcnMgPSBjb2xuYW1lcyhzYW1wbGVfZGYpWyFjb2xuYW1lcyhzYW1wbGVfZGYpICVpbiUgdGlsdHlwZXNdLCBtZWFzdXJlLnZhcnMgPSB0aWx0eXBlcywgdmFyaWFibGUubmFtZSA9ICJ0aWx0eXBlIiwgdmFsdWUubmFtZSA9ICJkZW5zaXR5IikKYGBgCgpgYGB7cn0KcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoc2FtcGxlX2RmX21lbHRlZCwgLih0aWx0eXBlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QoZGVuc2l0eSB+IGNsdXN0ZXIsIGRmKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygidGlsdHlwZSIsICJwLnZhbHVlIikpCgoKZ2dwbG90KHNhbXBsZV9kZl9tZWx0ZWQsIGFlcyh4PWNsdXN0ZXIsIHk9ZGVuc2l0eSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHRpbHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCgpgYGB7cn0KZGl2ZXJzaXR5X2NvbHVtbiA8LSAib2JzZXJ2ZWREaXZlcnNpdHlfbWVhbiIKCmRpdmVyc2l0eV9maWxlcyA8LSBsaXN0KHRjcj10Y3JfZGl2ZXJzaXR5X2ZpbGUsIGJjcj1iY3JfZGl2ZXJzaXR5X2ZpbGUpCgpkaXZlcnNpdHkgPC0gbGFwcGx5KG5hbWVzKGRpdmVyc2l0eV9maWxlcyksIGZ1bmN0aW9uKHNlZ21lbnQpIHsKICBmIDwtIGRpdmVyc2l0eV9maWxlc1tbc2VnbWVudF1dCiAgeGNyX2RpdmVyc2l0eSA8LSByZWFkX3hjcl9kaXZlcnNpdHlfZmlsZShmLCBkYl9wYXRoKQogIAogIGRmIDwtIHN1YnNldCh4Y3JfZGl2ZXJzaXR5LCBzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCBkaXZlcnNpdHlfY29sdW1uKSkKICBjb2xuYW1lcyhkZikgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKGRmKSwgZnJvbT1kaXZlcnNpdHlfY29sdW1uLCB0bz1zZWdtZW50KQogIHJldHVybihkZikKfSkKbmFtZXMoZGl2ZXJzaXR5KSA8LSBuYW1lcyhkaXZlcnNpdHlfZmlsZXMpCgp4Y3JfZGl2ZXJzaXR5IDwtIFJlZHVjZShwbHlyOjpqb2luLCBkaXZlcnNpdHkpCgpYQ1JfVkFSUyA8LSBjKCJ0Y3IiLCAiYmNyIikKYGBgCgoKYGBge3J9Cm1vbHN1YnR5cGVzIDwtIGZyZWFkKG1vbGVjdWxhcl9zdWJ0eXBlX2ZpbGUpCm1vbHN1YnR5cGVzIDwtIHNldE5hbWVzKG1vbHN1YnR5cGVzLCBjKCJjb25kZW5zZWRfaWQiLCAic3VidHlwZSIpKQoKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHkgPC0gZnJlYWQocHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfZmlsZSkKcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfc3Vic2V0IDwtIHN1YnNldChwcm9wb3J0aW9uX3N1YmNsb25hbGl0eSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwcm9wb3J0aW9uX3N1YmNsb25hbCIpKQoKZXhwcnMgPC0gZnJlYWQobmFub3N0cmluZ19kYXRhX3BhdGgpCmxhYmVscyA8LSBmcmVhZChuYW5vc3RyaW5nX2Fubm90YXRpb25zX3BhdGgpCgpjZWxsdHlwZV9tYXRyaXggPC0gY3JlYXRlX2NlbGx0eXBlX21hdHJpeChleHBycywgbGFiZWxzLCBkYl9wYXRoKQpwYXRod2F5X21hdHJpeCA8LSBjcmVhdGVfcGF0aHdheV9tYXRyaXgoZXhwcnMsIGxhYmVscywgZGJfcGF0aCkKCmNlbGx0eXBlX2RmIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQoY2VsbHR5cGVfbWF0cml4KSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQpwYXRod2F5X2RmIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQocGF0aHdheV9tYXRyaXgpKSwgdmFyID0gImNvbmRlbnNlZF9pZCIpCgpOQU5PU1RSSU5HX1ZBUlMgPC0gYyhyb3duYW1lcyhjZWxsdHlwZV9tYXRyaXgpLCByb3duYW1lcyhwYXRod2F5X21hdHJpeCkpCmBgYAoKCgpgYGB7cn0KY29sbmFtZXMoc2FtcGxlX2NsdXN0cykgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKHNhbXBsZV9jbHVzdHMpLCAibmV3X2lkIiwgImNvbmRlbnNlZF9pZCIpCgppaGNfc3RhYmlsaXplX3N1YnNldCA8LSBzdGFiaWxpemVfaWhjX3ZhcmlhbmNlcyhpaGNfdGFibGVfc2xpZGUsIGloY190YWJsZSwgdGlsdHlwZXMpCgpkYXRhX21hdHJpY2VzIDwtIGxpc3Qoc2FtcGxlX2NsdXN0cywgaWhjX3N0YWJpbGl6ZV9zdWJzZXQsIHhjcl9kaXZlcnNpdHksIGNlbGx0eXBlX2RmLCBwYXRod2F5X2RmLCBwcm9wb3J0aW9uX3N1YmNsb25hbGl0eV9zdWJzZXQpCmRhdGFfbWF0cmljZXMgPC0gbGFwcGx5KGRhdGFfbWF0cmljZXMsIGZ1bmN0aW9uKHgpIHsKICB4ICU+JSBkcGx5cjo6c2VsZWN0KC1vbmVfb2YoInBhdGllbnRfaWQiKSkKfSkKCmNvbWJpbmVkX2RmIDwtIFJlZHVjZShmdW5jdGlvbih4LCB5KSBwbHlyOjpqb2luKHgseSx0eXBlPSdmdWxsJyksIGRhdGFfbWF0cmljZXMpCmNvbWJpbmVkX2RmIDwtIHN1YnNldChjb21iaW5lZF9kZiwgIWlzLm5hKGNsdXN0ZXIpICYgIWNvbmRlbnNlZF9pZCAlaW4lIGMoIjdfQnJuTSIpKQoKY29tYmluZWRfbWF0IDwtIHN1YnNldChjb21iaW5lZF9kZiwgc2VsZWN0PS1jKGNvbmRlbnNlZF9pZCwgY2x1c3RlcikpCnJvd25hbWVzKGNvbWJpbmVkX21hdCkgPC0gY29tYmluZWRfZGYkY29uZGVuc2VkX2lkCmBgYAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTh9CiNyZWZfZGVuZHJvZ3JhbSA8LSBwcnVuZShhcy5kZW5kcm9ncmFtKHNhbXBsZV9oZWF0JHRyZWVfcm93KSwgIjdfQnJuTSIpCgpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0KSkKCmNsdXN0ZXJfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9Yyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQoKU0lHUyA8LSBjKCJTVi0zIiwgIlNOVi01IiwgIlNWLTEiLCAiU1YtMiIsICJTTlYtMSIpCnNpZ19hbm5vdGF0aW9ucyA8LSByb3duYW1lc190b19jb2x1bW4oZGF0YS5mcmFtZShwcm9wc19zYW1wbGVfbWF0WyxTSUdTXSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQoKIyMgVGFrZSBsb2dhcml0aG1zIHRvIH52YXJpYW5jZSBzdGFiaWxpemUKdmFyaWFudF9jb3VudF9hbm5vdGF0aW9ucyA8LSB2YXJpYW50X3NhbXBsZSAlPiUgZ3JvdXBfYnlfKC5kb3RzPSJjb25kZW5zZWRfaWQiKSAlPiUgCiAgc3VtbWFyaXNlKHNudl9jb3VudD1sb2coc3VtKHNudl9jb3VudCkpLCBia3B0X2NvdW50PWxvZyhzdW0oYmtwdF9jb3VudCkpKSAlPiUKICBzdWJzZXQoc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJzbnZfY291bnQiLCAiYmtwdF9jb3VudCIpKQoKcm93X2Fubm90YXRpb25zIDwtIFJlZHVjZShwbHlyOjpqb2luLCBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIG1vbHN1YnR5cGVzLCBzaWdfYW5ub3RhdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYW50X2NvdW50X2Fubm90YXRpb25zKSkKcm93X2Fubm90YXRpb25zIDwtIHJvd19hbm5vdGF0aW9ucyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJjb25kZW5zZWRfaWQiKQoKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBzY2FsZShjb21iaW5lZF9tYXQpCgojIGhjcyA8LSBsYXBwbHkodW5pcXVlKGNvbWJpbmVkX2RmJGNsdXN0ZXIpLCBmdW5jdGlvbihjbHVzdCkgewojICAgaWRzIDwtIHN1YnNldChjb21iaW5lZF9kZiwgY2x1c3RlciA9PSBjbHVzdCkkY29uZGVuc2VkX2lkCiMgICBkaXN0cyA8LSBkaXN0KGNvbWJpbmVkX21hdF9zY2FsZWRbaWRzLF0sIG1ldGhvZCA9ICJldWNsaWRlYW4iKQojICAgI2Rpc3RzIDwtIHByb3h5OjpkaXN0KGNvbWJpbmVkX21hdF9zY2FsZWRbaWRzLF0sIG1ldGhvZCA9IGZ1bmN0aW9uKHgseSkgcGFpcndpc2VfZGlzdCh4LHksbWV0aG9kPSJjYW5iZXJyYSIpKQojICAgaGNsdXN0KGRpc3RzLCBtZXRob2QgPSAid2FyZC5EIikKIyB9KQojIG1pbl9oZWlnaHQgPC0gbWF4KHNhcHBseShoY3MsIGZ1bmN0aW9uKHgpIG1heCh1bmlxdWUoY29waGVuZXRpYyh4KSkpKSkqMS4xCiMgCiMgaGMgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgbWVyZ2UoYXMuZGVuZHJvZ3JhbSh4KSxhcy5kZW5kcm9ncmFtKHkpLGhlaWdodCA9IG1pbl9oZWlnaHQpLCBoY3MpCiMgaGMgPC0gYXMuZGVuZHJvZ3JhbShyZWZfZGVuZHJvZ3JhbSkKIyBoYyA8LSBhcy5kZW5kcm9ncmFtKHNhbXBsZV9oZWF0MiR0cmVlX3JvdykKIyAKIyBoYSA8LSBIZWF0bWFwQW5ub3RhdGlvbihyb3dfYW5ub3RhdGlvbnNbcm93bmFtZXMoY29tYmluZWRfbWF0X3NjYWxlZCksXSwgd2hpY2g9InJvdyIpCiMgCiMgSGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9tYXRfc2NhbGVkLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IGhjMiwgY2x1c3Rlcl9jb2x1bW5zID0gVFJVRSwgc3BsaXQgPSAyLCBjbHVzdGVyaW5nX21ldGhvZF9jb2x1bW5zID0gIndhcmQuRDIiKSArIGhhCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9tYXRfc2NhbGVkLCAyLCAtMilbc2FtcGxlX29yZGVyLF0sIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2KQpgYGAKCldoYXQgd2UgY2FuIHNlZSBpczoKCiogSC1IUkQgY2x1c3RlciBpcyByZWxhdGl2ZWx5IGhvbW9nZW5lb3VzLgoqIExvb2tzIGxpa2UgdGhlcmUgYXJlIHR3byBjbHVzdGVycyB3aXRoaW4gdGhlIEgtRkJJIGdyb3VwIC0tIG9uZSBjaGFyYWN0ZXJpemVkIGJ5IGhpZ2ggbGV2ZWxzIG9mIGltbXVuZSBhY3Rpdml0eSAoY3l0b3RveGljaXR5LCBldGMuKSBhbmQgb25lIGNoYXJhY3Rlcml6ZWQgYnkgbG93IGxldmVscy4gCgpUaGlzIGlzIGEgcHJldHR5IHNpZ25pZmljYW50IGZpbmRpbmcuIAoKQWRkaXRpb25hbGx5LCBhbiBpbnRlcmVzdGluZyB0aGluZyBpcyB0aGF0IHRoZSBwYXRpZW50cy9zYW1wbGVzIHdpdGggdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbnMgb2YgZm9sZGJhY2tzIC0tIHBhdGllbnRzIDIsIDMsIGFuZCA5IC0tIGFyZSBhY3R1YWxseSB0aGUgb25lcyB3aXRoIGhpZ2ggaW1tdW5lIHJlc3BvbnNlIGluIHRoZSBmb2xkYmFjayBncm91cCEgU3VnZ2VzdGl2ZSB0aGF0IHBlcmhhcHMgZm9sZGJhY2sgaW52ZXJzaW9ucyBjYW4gY3JlYXRlIG5lb2VwaXRvcGVzLiBPZiBjb3Vyc2UgdmVyeSBwcmVsaW1pbmFyeSBhbmQgbG93IHNhbXBsZSBzaXplIHRob3VnaC4gCgpJZiB5b3UncmUgd29uZGVyaW5nIHdoeSB0aGVyZSdzIG5vIGRlbmRyb2dyYW0gb24gdGhlIHZlcnRpY2FsIGF4aXMgaXQncyBiZWNhdXNlIHRoZSBwbG90dGluZyBmdW5jdGlvbnMgSSdtIGN1cnJlbnRseSB1c2luZyBkb24ndCBhbGxvdyBmb3Igc2VsZi1zcGVjaWZpZWQgZGVuZHJvZ3JhbXMuIFRyeWluZyB0byBtYWtlIG9uZSB0aGF0IGxldHMgbWUgZG8gc28gYnV0IGl0J3MgdGFraW5nIGEgYml0IG9mIGFjcm9iYXRpY3MgYW5kIEkndmUgd2FzdGVkIGEgbG90IG9mIHRpbWUgYWxyZWFkeSAuLi4gCgpUbyBzZWUgSUNHQyB2YWxpZGF0aW9uLCBnbyB0byB0aGF0IHNlY3Rpb24uIEknbGwgYWRkIGEgbGluayBsYXRlciAuLi4KCiMjIyMgVENSL0JDUiBkaXZlcnNpdHkKCmBgYHtyfQpjb21iaW5lZF9kZiRwYXRpZW50X2lkIDwtIG1hcF9pZChjb21iaW5lZF9kZiRjb25kZW5zZWRfaWQsIGZyb20gPSAiY29uZGVuc2VkX2lkIiwgdG89InBhdGllbnRfaWQiLCBkYl9wYXRoKQpjb21iaW5lZF9kZl94Y3IgPC0gbWVsdChjb21iaW5lZF9kZiwgaWQudmFycyA9IGMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgImNsdXN0ZXIiKSwgbWVhc3VyZS52YXJzID0gWENSX1ZBUlMsIHZhcmlhYmxlLm5hbWUgPSAidHlwZSIsIHZhbHVlLm5hbWUgPSAidmFsdWUiKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoY29tYmluZWRfZGZfeGNyLCAuKHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdCh2YWx1ZSB+IGNsdXN0ZXIsIGRmKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygidHlwZSIsICJwLnZhbHVlIikpCgoKZ2dwbG90KGNvbWJpbmVkX2RmX3hjciwgYWVzKHg9Y2x1c3RlciwgeT12YWx1ZSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCiMjIyMgQ2VsbHR5cGVzIGFuZCBwYXRod2F5cwoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KY29tYmluZWRfZGYkcGF0aWVudF9pZCA8LSBtYXBfaWQoY29tYmluZWRfZGYkY29uZGVuc2VkX2lkLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCkKY29tYmluZWRfZGZfbmFub3N0cmluZyA8LSBtZWx0KGNvbWJpbmVkX2RmLCBpZC52YXJzID0gYygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiLCAiY2x1c3RlciIpLCBtZWFzdXJlLnZhcnMgPSBOQU5PU1RSSU5HX1ZBUlMsIHZhcmlhYmxlLm5hbWUgPSAidHlwZSIsIHZhbHVlLm5hbWUgPSAidmFsdWUiKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoY29tYmluZWRfZGZfbmFub3N0cmluZywgLih0eXBlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QodmFsdWUgfiBjbHVzdGVyLCBkZikKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInR5cGUiLCAicC52YWx1ZSIpKQoKCmdncGxvdChjb21iaW5lZF9kZl9uYW5vc3RyaW5nLCBhZXMoeD1jbHVzdGVyLCB5PXZhbHVlKSkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgIGdlb21faml0dGVyKGFlcyhjb2xvdXI9cGF0aWVudF9pZCksIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gdHlwZSwgc2NhbGVzID0gImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKIyMjIEFuY2VzdHJhbC1kZXNjZW5kYW50IGxldmVsCgpgYGB7cn0KY2x1c3RlcnMgPC0gY3V0cmVlKHBhdGllbnRfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpwYXRpZW50X2NsdXN0cyA8LSBtYWtlX2NsdXN0ZXJfZnJhbWUoY2x1c3RlcnMpCgpwcm9wc19hZF9tZXJnZWRfY2x1c3RzIDwtIGpvaW4ocHJvcHNfYWRfbWVyZ2VkLCBwYXRpZW50X2NsdXN0cykKcGF0aWVudF9kZiA8LSB1bmlxdWUoc3Vic2V0KHByb3BzX2FkX21lcmdlZF9jbHVzdHMsIHNlbGVjdD1zZWxlY3RlZF9jb2xzKSkKCnBhdGllbnRfZGZfbWVsdGVkIDwtIG1lbHQocGF0aWVudF9kZiwgaWQudmFycyA9IGNvbG5hbWVzKHBhdGllbnRfZGYpWyFjb2xuYW1lcyhwYXRpZW50X2RmKSAlaW4lIHRpbHR5cGVzXSwgbWVhc3VyZS52YXJzID0gdGlsdHlwZXMsIHZhcmlhYmxlLm5hbWUgPSAidGlsdHlwZSIsIHZhbHVlLm5hbWUgPSAiZGVuc2l0eSIpCmBgYAoKYGBge3J9CnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KHBhdGllbnRfZGZfbWVsdGVkLCAuKHRpbHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdChkZW5zaXR5IH4gY2x1c3RlciwgZGYpCiAgCiAgcHZhbCA8LSBjb3JyZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCn0pLCBjKCJ0aWx0eXBlIiwgInAudmFsdWUiKSkKCgpnZ3Bsb3QocGF0aWVudF9kZl9tZWx0ZWQsIGFlcyh4PWNsdXN0ZXIsIHk9ZGVuc2l0eSkpICsgZ2VvbV9ib3hwbG90KCkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArICBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFsX3BhdGllbnQpICsgZmFjZXRfd3JhcCh+IHRpbHR5cGUsIHNjYWxlcyA9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCgpIZW5jZSwgdGhlIGZvbGRiYWNrIGdyb3VwIChjbHVzdGVyIDIpIGhhcyBsb3dlciBDRDgrIGFuZCBDRDQrIGRlbnNpdGllcyB0aGFuIHRoZSBIUkQgZ3JvdXAsIGFzIGV4cGVjdGVkLiAKCkEgY2F2ZWF0IGlzIHRoYXQgcC12YWx1ZXMgYXJlIGNvbXB1dGVkIHdpdGggV2lsY294b24sIGlycmVzcGVjdGl2ZSBvZiBwYXRpZW50IG51bWJlci4gV2UgY2FuJ3QgcmVhbGx5IG9wdCBmb3IgYSBub25wYXJhbWV0cmljIG5lc3RlZCByYW5rcyB0ZXN0IGJlY2F1c2UgdmVyeSBmZXcgcGF0aWVudHMgKDQpIGFyZSBhY3R1YWxseSBwcmVzZW50IGluIGJvdGggY2x1c3RlcnMuIAoKIyMgQW5jZXN0cmFsIGFuYWx5c2lzCgpBbmNlc3RyYWwgdmFyaWFudHMgbWF5IGhhdmUgZGlmZmVyZW50IHByb3BlcnRpZXMgZnJvbSBkZXNjZW5kYW50IHZhcmlhbnRzLiAKCiMjIyBTYW1wbGUtc3BlY2lmaWMKCkhlcmUgd2UnbGwganVzdCBuYWl2ZWx5IGFsbG93IGZvciBzdWJjbG9uYWwgdmFyaWFudHMgdG8gYmUgY291bnRlZCBtdWx0aXBsZSB0aW1lcy4gCgpgYGB7cn0KcHJvcHNfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCA8LSBhcy5mYWN0b3IocHJvcHNfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCkKCnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KHByb3BzX2FkX21lcmdlZCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aWxjb3gudGVzdChwcm9wb3J0aW9uIH4gaXNfYW5jZXN0cmFsLCBkZikKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QocHJvcHNfYWRfbWVyZ2VkLCBhZXMoeD1pc19hbmNlc3RyYWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2ppdHRlcihhZXMoY29sb3VyPXBhdGllbnRfaWQpLCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXIod2lkdGg9MC4yLCBoZWlnaHQ9MCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiBzaWduYXR1cmUsIHNjYWxlcz0gImZyZWUiKSArIAogIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKUC12YWx1ZXMgYXJlIHVuY29ycmVjdGVkLiAKClVuc3VycHJpc2luZ2x5LCBhbmNlc3RyYWwgc2FtcGxlcyBoYXZlIHNpZ25pZmljYW50bHkgbW9yZSBvZiBTTlYtNCwgdGhlIGFnZSBzaWduYXR1cmUuIEluc2lnbmlmaWNhbnQsIGJ1dCB0aGV5IGFsc28gaGF2ZSBsZXNzIFNWLTEsIHdoaWNoIGlzIG9uZSBvZiB0aGUgQlJDQSdzIChzbWFsbCBkZWxldGlvbnMpLiAKCiMjIyBVbmlvbgoKSGVyZSB3ZSdsbCBhY3R1YWxseSB0YWtlIHRoZSB1bmlvbiBvZiBzdWJjbG9uYWwgdmFyaWFudHMgZm9yIGVhY2ggcGF0aWVudCBzbyB3ZSBkb24ndCBvdmVyY291bnQuIAoKYGBge3J9CnByb3BzX3BhdGllbnRfYWRfbWVyZ2VkJGlzX2FuY2VzdHJhbCA8LSBhcy5mYWN0b3IocHJvcHNfcGF0aWVudF9hZF9tZXJnZWQkaXNfYW5jZXN0cmFsKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkocHJvcHNfcGF0aWVudF9hZF9tZXJnZWQsIC4oc2lnbmF0dXJlKSwgZnVuY3Rpb24oeCkgewogIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICBjb3JyZXMgPC0gd2lsY294LnRlc3QocHJvcG9ydGlvbiB+IGlzX2FuY2VzdHJhbCwgZGYsIHBhaXJlZD1UUlVFKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygic2lnbmF0dXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChwcm9wc19wYXRpZW50X2FkX21lcmdlZCwgYWVzKHg9aXNfYW5jZXN0cmFsLCB5PXByb3BvcnRpb24pKSArIGdlb21fYm94cGxvdCgpICsgZ2VvbV9qaXR0ZXIoYWVzKGNvbG91cj1wYXRpZW50X2lkKSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyKHdpZHRoPTAuMiwgaGVpZ2h0PTApKSArIHRoZW1lX2J3KCkgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBmYWNldF93cmFwKH4gc2lnbmF0dXJlLCBzY2FsZXM9ICJmcmVlIikgKyAKICBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKQpgYGAKCkFzaWRlIGZyb20gYWdlIGFuZCBIUkQgd2hpY2ggd2VyZSBkZXNjcmliZWQgaW4gTWNQaGVyc29uIGV0IGFsLiwgdGhlcmUncyBhZGRpdGlvbmFsbHkgdGhlIFNWLTMgc2lnbmF0dXJlIC0tIGEgdHJhbnNsb2NhdGlvbiBzaWduYXR1cmUuIEltcGx5aW5nIHRoYXQgdHJhbnNsb2NhdGlvbnMgYXJlIGVhcmx5IChhbmNlc3RyYWwpIGV2ZW50cyBpbiBIR1NDIC0tIHBlcmhhcHMgdGhpcyBpcyBpbnRlcmVzdGluZz8gSSBoYXZlIHRvIGRvIGEgbGl0ZXJhdHVyZSBzZWFyY2guIAoKCmBgYHtyfQphbmNfZGVzY19wYXRpZW50IDwtIGRjYXN0KHByb3BzX3BhdGllbnRfYWRfbWVyZ2VkLCBmb3JtdWxhID0gcGF0aWVudF9pZCArIHNpZ25hdHVyZSB+IGlzX2FuY2VzdHJhbCwgdmFsdWUudmFyID0gInByb3BvcnRpb24iKQoKcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoYW5jX2Rlc2NfcGF0aWVudCwgLihzaWduYXR1cmUpLCBmdW5jdGlvbih4KSB7CiAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogIGNvcnJlcyA8LSB3aXRoKGRmLCBjb3IudGVzdChgMGAsIGAxYCwgbWV0aG9kPSJzcGVhcm1hbiIpKQogIAogIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogIHJldHVybihhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpKQp9KSwgYygic2lnbmF0dXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChhbmNfZGVzY19wYXRpZW50LCBhZXMoeD1gMWAsIHk9YDBgKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGZhY2V0X3dyYXAofiBzaWduYXR1cmUsIHNjYWxlcz0gImZyZWUiKSArIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpCmBgYAoKIyMgSUNHQyB2YWxpZGF0aW9uCgojIyMgU2lnbmF0dXJlcwoKIVtdKGByIG1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3RgKQoKIyMjIEZCSSBzdWJjbHVzdGVycwoKYGBge3J9CnNwZWNpbWVuX2RhdGEgPC0gZnJlYWQoaWNnY19zcGVjaW1lbl9maWxlKQppY2djX3N1YnR5cGVzIDwtIGZyZWFkKGljZ2Nfc3VidHlwZV9maWxlKQppY2djX2V4cHJfbWF0IDwtIGZyZWFkKGljZ2NfZXhwcl9tYXRfZmlsZSkKaWNnY19leHByX2RmIDwtIGljZ2NfZXhwcl9tYXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGNvbHVtbl90b19yb3duYW1lcygiaWNnY19kb25vcl9pZCIpICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oIk5hbWUiKQppY2djX2NlbGx0eXBlX21hdHJpeCA8LSBjcmVhdGVfcGF0aHdheV9tYXRyaXgoaWNnY19leHByX2RmLCBsYWJlbHMsIGRiX3BhdGgsIGNvbnZlcnRfaWRzID0gRkFMU0UpCgppY2djX2ltbXVuZSA8LSByb3duYW1lc190b19jb2x1bW4oYXMuZGF0YS5mcmFtZSh0KGljZ2NfY2VsbHR5cGVfbWF0cml4KSksIHZhciA9ICJpY2djX2Rvbm9yX2lkIikKCiMgY3l0b3RveGljX21hcmtlcnMgPC0gYygiUFJGMSIsICJHWk1BIiwgIkhMQS1BIiwgIkhMQS1CIiwgIkhMQS1DIiwgIkdaTUsiLCAiR1pNTSIsICJHWk1IIiwgIkdOTFkiLCAiR1pNQiIpCiMgaWNnY19pbW11bmUgPC0gYXMuZGF0YS5mcmFtZShzdWJzZXQoaWNnY19leHByX21hdCwgc2VsZWN0PWMoImljZ2NfZG9ub3JfaWQiLCBjeXRvdG94aWNfbWFya2VycykpKQojIGZvciAoaSBpbiAyOm5jb2woaWNnY19pbW11bmUpKSB7CiMgIGljZ2NfaW1tdW5lWyxpXSA8LSBsb2cxMChpY2djX2ltbXVuZVssaV0pCiMgfQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEyfQpwcm9wc19vdl9jb21iaW5lZCA8LSByZWFkX211dHNpZ19vdXRwdXQobW1jdG1fb3ZfY29tYmluZWRfcmVzdWx0X2Rpciwgc2FtcGxlcywgZXh0ZXJuYWwgPSBUUlVFKQpwcm9wc19vdl9jb21iaW5lZF9wcm9wIDwtIHByb3BzX292X2NvbWJpbmVkJHByb3AKCnByb3BzX292X2NvbWJpbmVkX21hdCA8LSBjcmVhdGVfbXV0c2lnX21hdHJpeChwcm9wc19vdl9jb21iaW5lZF9wcm9wLCBjb2wgPSAicHJvcG9ydGlvbiIpCgojIHByb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShhcHBseShwcm9wc19vdl9jb21iaW5lZF9tYXQsIDIsIGZ1bmN0aW9uKHgpIHsKIyAgICN4IDwtIGxvZ2l0KHgpCiMgICAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkKIyB9KSkKCnByb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gc2NhbGUocHJvcHNfb3ZfY29tYmluZWRfbWF0KQoKI3Byb3BzX292X2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gY2xpcF92YWx1ZXMocHJvcHNfb3ZfY29tYmluZWRfbWF0X3NjYWxlZCwgMywgLTMpCiNjbGlwX3ZhbHVlcyhzY2FsZShwcm9wc19vdl9jb21iaW5lZF9tYXQpLCAyLCAtMikKCm92X2NvbWJpbmVkX2hlYXQgPC0gcGhlYXRtYXAocHJvcHNfb3ZfY29tYmluZWRfbWF0X3NjYWxlZCwgZm9udHNpemVfcm93ID0gNiwgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKYGBge3J9Ck5DTFVTVCA8LSAzCgpjbHVzdGVycyA8LSBjdXRyZWUob3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdywgTkNMVVNUKQpvdl9jb21iaW5lZF9jbHVzdHMgPC0gbWFrZV9jbHVzdGVyX2ZyYW1lKGNsdXN0ZXJzKQoKY29sbmFtZXMoaWNnY19pbW11bmUpIDwtIG1hcHZhbHVlcyhjb2xuYW1lcyhpY2djX2ltbXVuZSksICJpY2djX2Rvbm9yX2lkIiwgIm5ld19pZCIpCgpkYXRhX21hdHJpY2VzIDwtIGxpc3Qob3ZfY29tYmluZWRfY2x1c3RzLCBpY2djX2ltbXVuZSkKZGF0YV9tYXRyaWNlcyA8LSBsYXBwbHkoZGF0YV9tYXRyaWNlcywgZnVuY3Rpb24oeCkgewogIHggJT4lIGRwbHlyOjpzZWxlY3QoLW9uZV9vZigicGF0aWVudF9pZCIpKQp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCiNjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsIGNsdXN0ZXIgPT0gMSkKCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhuZXdfaWQsIGNsdXN0ZXIpKQpyb3duYW1lcyhjb21iaW5lZF9tYXQpIDwtIGNvbWJpbmVkX2RmJG5ld19pZApgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzYW1wbGVfb3JkZXIgPC0gb3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdyRsYWJlbHNbb3ZfY29tYmluZWRfaGVhdCR0cmVlX3JvdyRvcmRlcl0Kc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIHJvd25hbWVzKGNvbWJpbmVkX21hdCkpCgpjbHVzdGVyX2Fubm90YXRpb25zIDwtIHN1YnNldChjb21iaW5lZF9kZiwgc2VsZWN0PWMobmV3X2lkLCBjbHVzdGVyKSkKClNJR1MgPC0gYygiU1YtOCIsICJTTlYtMSIsICJTVi00IiwgIlNWLTciLCAiU1YtNSIsICJTTlYtNyIsICJTVi0xIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHByb3BzX292X2NvbWJpbmVkX21hdFssU0lHU10sIGNoZWNrLm5hbWVzID0gRkFMU0UpLCB2YXIgPSAibmV3X2lkIikKCnN1YnR5cGVfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2Nfc3VidHlwZXMsIHNlbGVjdD1jKCJpY2djX2Rvbm9yX2lkIiwgInN1YnR5cGUiLCAibm1mX3N1YnR5cGUiKSkKY29sbmFtZXMoc3VidHlwZV9hbm5vdGF0aW9ucylbMV0gPC0gIm5ld19pZCIKCnJvd19hbm5vdGF0aW9ucyA8LSBSZWR1Y2UocGx5cjo6am9pbiwgbGlzdChjbHVzdGVyX2Fubm90YXRpb25zLCBzaWdfYW5ub3RhdGlvbnMsIHN1YnR5cGVfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gIm5ld19pZCIpCgpzZWxlY3Rfcm93cyA8LSByb3duYW1lcyhzdWJzZXQocm93X2Fubm90YXRpb25zLCBgU1YtNGAgPCAxKSkKbm90eCA8LSBzdWJzZXQoc3BlY2ltZW5fZGF0YSwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIHNlbGVjdF9yb3dzKQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgbm90eCkKCiNvcmRlcl9mYmkgPC0gb3JkZXIocm93X2Fubm90YXRpb25zW3Jvd25hbWVzKGNvbWJpbmVkX21hdF9zY2FsZWQpLF0kYFNWLThgKQoKI2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gc2NhbGUoY29tYmluZWRfbWF0KQpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIGFzLmRhdGEuZnJhbWUoYXBwbHkoY29tYmluZWRfbWF0LCAyLCBmdW5jdGlvbih4KSAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkpKQoKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkW3NhbXBsZV9vcmRlcixdCm90aGVyX21hdCA8LSBwcm9wc19vdl9jb21iaW5lZF9tYXRfc2NhbGVkW3NhbXBsZV9vcmRlcixdCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjYmluZChjb21iaW5lZF9tYXRfc2NhbGVkLCBvdGhlcl9tYXQpLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKCiNyb3dtZWFuIDwtIHJvd01lYW5zKGNvbWJpbmVkX21hdF9zY2FsZWQpCiNhIDwtIG1lcmdlKHJvd19hbm5vdGF0aW9ucywgYXMuZGF0YS5mcmFtZShyb3dtZWFuKSwgYnk9InJvdy5uYW1lcyIpCiNnZ3Bsb3Qoc3Vic2V0KGEsIGNsdXN0ZXIgIT0gMSksIGFlcyh4PXJvd21lYW4gPiBtZWRpYW4ocm93bWVhbiksIHkgPSBgU05WLTdgKSkgKyBnZW9tX2JveHBsb3QoKSArIHRoZW1lX2J3KCkKYGBgCgpgYGB7cn0KY2x1c3RlcnMgPC0gc2V0TmFtZXMoY2x1c3Rlcl9hbm5vdGF0aW9ucywgYygiaWNnY19kb25vcl9pZCIsICJjbHVzdGVyIikpCmBgYAoKIyMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uCgpgYGB7cn0KaWNnY19leHByX21lbHRlZCA8LSBmcmVhZChpY2djX2V4cHJfcmF3X2ZpbGUpCmljZ2NfZXhwcl9jYXN0ZWRfbWF0cml4IDwtIGV4cHJlc3Npb25fZGZfdG9fbWF0cml4KGljZ2NfZXhwcl9tZWx0ZWQsIHN1bW1hcml6ZV9vdmVyID0gImljZ2NfZG9ub3JfaWQiLCBtZWFzdXJlX3ZhciA9ICJyYXdfcmVhZF9jb3VudCIpCgppY2djX2NvdW50cyA8LSBpY2djX2V4cHJfY2FzdGVkX21hdHJpeCAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKHZhciA9ICJpY2djX2Rvbm9yX2lkIikgCmljZ2NfY291bnRzX2ZpbHRlcmVkIDwtIGljZ2NfY291bnRzW2NsdXN0ZXJzJGljZ2NfZG9ub3JfaWQsXQppY2djX2NvdW50c19maWx0ZXJlZCA8LSBpY2djX2NvdW50c19maWx0ZXJlZCAlPiUgdCAlPiUgYXMuZGF0YS5mcmFtZQoKZGdlIDwtIERHRUxpc3QoY291bnRzID0gaWNnY19jb3VudHNfZmlsdGVyZWQpCmRnZSA8LSBjYWxjTm9ybUZhY3RvcnMoZGdlKQoKbGlicmFyeV9zaXplcyA8LSBjb2xTdW1zKGljZ2NfY291bnRzX2ZpbHRlcmVkKQpsaWJyYXJ5X3NpemVzCm1heChsaWJyYXJ5X3NpemVzKS9taW4obGlicmFyeV9zaXplcykKCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofjAgKyBmYWN0b3IoY2x1c3RlcnMkY2x1c3RlcikpCmNvbG5hbWVzKGRlc2lnbikgPC0gcGFzdGUwKCJjbHVzdCIsIDE6TkNMVVNUKQpjb250cmFzdC5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cyhjbHVzdDEtY2x1c3QzLCBjbHVzdDItKGNsdXN0MStjbHVzdDMpLzIsY2x1c3QyLWNsdXN0MSwgbGV2ZWxzPWRlc2lnbikKYGBgCgojIyMjIGxpbW1hLXRyZW5kCgpXZSBzdHJhZGRsZSBjbG9zZSB0byB0aHJlc2hvbGQgZm9yIGRvaW5nIHRoaXMgYnV0IGxldCdzIGRvIGl0IGFueXdheXMuIAoKYGBge3J9CmZpdF90cmVuZCA8LSBybmFzZXFfREVfaW5pdGlhbF9maXQoZGdlLCBkZXNpZ24sIHR5cGUgPSAibGltbWFfdHJlbmQiKQpgYGAKCmBgYHtyfQpmaXRfdHJlbmRfY29udHJhc3RzIDwtIGNvbnRyYXN0cy5maXQoZml0X3RyZW5kLCBjb250cmFzdC5tYXRyaXgpCmZpdF90cmVuZF9jb250cmFzdHMgPC0gZUJheWVzKGZpdF90cmVuZF9jb250cmFzdHMpCgpocmRfZmJpX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gKGNsdXN0MSArIGNsdXN0MykvMiIsIGFkanVzdC5tZXRob2QgPSAiQkgiLCBudW1iZXIgPSBJbmYsIHNvcnQ9InAiKQpmYmlfaW1tdW5lX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QxIC0gY2x1c3QzIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKaHJkX2ZiaV9oaWdoaW1tdW5lX2dlbmVzIDwtIHRvcFRhYmxlKGZpdF90cmVuZF9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gY2x1c3QxIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKYGBgCgpgYGB7cn0KaHJkX2ZiaV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfZ2VuZXMsIGdzX3R5cGUgPSAiZ28iKQpmYmlfaW1tdW5lX3BhdGh3YXlzIDwtIHBhdGh3YXlfYW5hbHlzaXMoZmJpX2ltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJnbyIpCmhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJnbyIpCmBgYAoKIyMjIyMgSFJEIHZzLiBGQkkgc2FtcGxlcyAoY2x1c3QyIHZzLiBjbHVzdDErY2x1c3QzKQoKYGBge3J9CmRhdGF0YWJsZShocmRfZmJpX3BhdGh3YXlzLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zPWxpc3QocGFnZUxlbmd0aD01LCBzY3JvbGxYPVRSVUUsZG9tPSdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSkpCmBgYAoKCgojIyMjIyBIaWdoIGltbXVuZSBGQkkgdnMuIGxvdyBpbW11bmUgRkJJIChjbHVzdDEgdnMuIGNsdXN0MykKCmBgYHtyfQpkYXRhdGFibGUoZmJpX2ltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIyBIUkQgdnMuIGhpZ2ggaW1tdW5lIEZCSSAoY2x1c3QyIHZzLiBjbHVzdDEpCgpgYGB7cn0KZGF0YXRhYmxlKGhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCiMjIyMgdm9vbQoKV2UgaGF2ZSBncmVhdGVyIHRoYW4gMy1mb2xkIHZhcmlhYmlsaXR5IGluIGxpYnJhcnkgc2l6ZSwgc28gd2UncmUgYmV0dGVyIG9mZiB1c2luZyB0aGUgdm9vbSBtZXRob2QgZm9yIERFIGFuYWx5c2lzLiAKCmBgYHtyfQpmaXRfdm9vbSA8LSBybmFzZXFfREVfaW5pdGlhbF9maXQoZGdlLCBkZXNpZ24sIHR5cGUgPSAidm9vbSIpCmBgYAoKYGBge3J9CmZpdF92b29tX2NvbnRyYXN0cyA8LSBjb250cmFzdHMuZml0KGZpdF92b29tLCBjb250cmFzdC5tYXRyaXgpCmZpdF92b29tX2NvbnRyYXN0cyA8LSBlQmF5ZXMoZml0X3Zvb21fY29udHJhc3RzKQoKaHJkX2ZiaV9nZW5lcyA8LSB0b3BUYWJsZShmaXRfdm9vbV9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gKGNsdXN0MSArIGNsdXN0MykvMiIsIGFkanVzdC5tZXRob2QgPSAiQkgiLCBudW1iZXIgPUluZiwgc29ydD0icCIpCmZiaV9pbW11bmVfZ2VuZXMgPC0gdG9wVGFibGUoZml0X3Zvb21fY29udHJhc3RzLCBjb2VmID0gImNsdXN0MSAtIGNsdXN0MyIsIG51bWJlciA9IEluZiwgc29ydD0icCIpCmhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcyA8LSB0b3BUYWJsZShmaXRfdm9vbV9jb250cmFzdHMsIGNvZWYgPSAiY2x1c3QyIC0gY2x1c3QxIiwgbnVtYmVyID0gSW5mLCBzb3J0PSJwIikKYGBgCgoKYGBge3IsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFfQpocmRfZmJpX3BhdGh3YXlzIDwtIHBhdGh3YXlfYW5hbHlzaXMoaHJkX2ZiaV9nZW5lcywgZ3NfdHlwZSA9ICJrZWdnIikKZmJpX2ltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGZiaV9pbW11bmVfZ2VuZXMsIGdzX3R5cGUgPSAia2VnZyIpCmhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cyA8LSBwYXRod2F5X2FuYWx5c2lzKGhyZF9mYmlfaGlnaGltbXVuZV9nZW5lcywgZ3NfdHlwZSA9ICJrZWdnIikKIyBwdi5vdXQubGlzdCA8LSBzYXBwbHkocGF0aC5pZHMyLCBmdW5jdGlvbihwaWQpIHBhdGh2aWV3KAojICAgICAgICAgICAgICAgICAgICAgICBnZW5lLmRhdGEgPSAgZXhwLmZjLCBwYXRod2F5LmlkID0gcGlkLAojICAgICAgICAgICAgICAgICAgICAgICBzcGVjaWVzID0gImhzYSIsIG91dC5zdWZmaXg9b3V0LnN1ZmZpeCkpCmBgYAoKKipOb3RlOioqIEJSQ0ExLCBCUkNBMiwgYW5kIFJQQSBhcmUgYWxzbyBkb3ducmVndWxhdGVkIGluIHRoZSBIUkQgZ3JvdXAgLS0gdGhlIGVudGlyZSBIUi1hc3NvY2lhdGVkIGdlbmUgY2x1c3RlciBpc24ndCBzaWduaWZpY2FudGx5IGRvd25yZWd1bGF0ZWQgdGhvdWdoLiAKCk9uZSBvZiB0aGUga2V5IHJlYXNvbnMgd2h5IHRoZSBlbnRpcmUgSFIgcGF0aHdheSBpc24ndCBzaWduaWZpY2FudGx5IGRvd25yZWd1bGF0ZWQgaXMgYmVjYXVzZSBQb2xRIGlzIHBhcnQgb2YgaXQgYW5kIFBvbFEgaXMgdXByZWd1bGF0ZWQuIE9mIG5vdGUsIFBvbFEgcHJvbW90ZXMgTU1FSiwgcHJvdmlkaW5nIGZ1cnRoZXIgZXZpZGVuY2UgZm9yIE1NRUogYWN0aXZpdHkgaW4gRkJJIHR1bW91cnMuIAoKIyMjIyMgSFJEIHZzLiBGQkkgc2FtcGxlcyAoY2x1c3QyIHZzLiBjbHVzdDErY2x1c3QzKQoKYGBge3J9CmRhdGF0YWJsZShocmRfZmJpX3BhdGh3YXlzLCBleHRlbnNpb25zID0gIkJ1dHRvbnMiLCBvcHRpb25zPWxpc3QocGFnZUxlbmd0aD01LCBzY3JvbGxYPVRSVUUsZG9tPSdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSkpCmBgYAoKCgojIyMjIyBIaWdoIGltbXVuZSBGQkkgdnMuIGxvdyBpbW11bmUgRkJJIChjbHVzdDEgdnMuIGNsdXN0MykKCmBgYHtyfQpkYXRhdGFibGUoZmJpX2ltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIyBIUkQgdnMuIGhpZ2ggaW1tdW5lIEZCSSAoY2x1c3QyIHZzLiBjbHVzdDEpCgpgYGB7cn0KZGF0YXRhYmxlKGhyZF9mYmlfaGlnaGltbXVuZV9wYXRod2F5cywgZXh0ZW5zaW9ucyA9ICJCdXR0b25zIiwgb3B0aW9ucz1saXN0KHBhZ2VMZW5ndGg9NSwgc2Nyb2xsWD1UUlVFLGRvbT0nQmZydGlwJyxidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykpKQpgYGAKCgojIyMjIFNvbWUgRE5BIHJlcGFpciBnZW5lcwoKYGBge3J9CnYgPC0gdm9vbShpY2djX2NvdW50c19maWx0ZXJlZCwgZGVzaWduLCBub3JtYWxpemU9InF1YW50aWxlIiwgcGxvdD1GQUxTRSkKYGBgCgpgYGB7cn0KaWNnY19leHByX2RmX3dpZGUgPC0gdiRFICU+JSB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oImljZ2NfZG9ub3JfaWQiKQoKaWNnY19leHByX2RmX21lbHRlZCA8LSByZXNoYXBlMjo6bWVsdChpY2djX2V4cHJfZGZfd2lkZSwgaWQudmFycyA9IGMoImljZ2NfZG9ub3JfaWQiKSwgbWVhc3VyZS52YXJzID0gY29sbmFtZXMoaWNnY19leHByX2RmX3dpZGUpWzI6bmNvbChpY2djX2V4cHJfZGZfd2lkZSldLCB2YXJpYWJsZS5uYW1lID0gIk5hbWUiLCB2YWx1ZS5uYW1lID0gImV4cHJlc3Npb24iKQoKaWNnY19leHByX2RmX21lbHRlZF9sYWJlbGVkIDwtIHBseXI6OmpvaW4oaWNnY19leHByX2RmX21lbHRlZCwgY2x1c3RlcnMpCgpSRVBBSVJfR0VORVMgPC0gYygiQlJDQTEiLCAiQlJDQTIiLCAiUE9MUSIsICJGRU4xIiwgIlhSQ0MxIiwgIlBBUlAxIiwgIkxJRzMiKQoKaWNnY19leHByX2RmX21lbHRlZF9sYWJlbGVkX2ZpbHRlcmVkIDwtIHN1YnNldChpY2djX2V4cHJfZGZfbWVsdGVkX2xhYmVsZWQsIE5hbWUgJWluJSBSRVBBSVJfR0VORVMpCgpnZ3Bsb3Qoc3Vic2V0KGljZ2NfZXhwcl9kZl9tZWx0ZWRfbGFiZWxlZF9maWx0ZXJlZCwgIWlzLm5hKGNsdXN0ZXIpKSwgYWVzKHg9Y2x1c3RlciwgeT1leHByZXNzaW9uKSkgKyBnZW9tX2JveHBsb3QoKSArIGdlb21faml0dGVyKHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcih3aWR0aD0wLjIsIGhlaWdodD0wKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIGZhY2V0X3dyYXAofiBOYW1lLCBzY2FsZXMgPSAiZnJlZSIpCmBgYAoKIyMjIFN1cnZpdmFsCgpgYGB7cn0KaWNnY19jbGluaWNhbCA8LSByZWFkX2Rvbm9yX3NwZWNpbWVuX3N1cnZpdmFsKGljZ2NfZG9ub3JfZmlsZSwgaWNnY19zcGVjaW1lbl9maWxlKQoKaWNnY19jbGluaWNhbF9sYWJlbGVkIDwtIHBseXI6OmpvaW4oY2x1c3RlcnMsIGljZ2NfY2xpbmljYWwpCgppY2djX2NsaW5pY2FsX2xhYmVsZWQkU3Vydk9iaiA8LSB3aXRoKGljZ2NfY2xpbmljYWxfbGFiZWxlZCwgU3Vydihkb25vcl9zdXJ2aXZhbF90aW1lLCBkb25vcl92aXRhbF9zdGF0dXMgPT0gImRlY2Vhc2VkIikpCmBgYAoKU3Vydml2YWwgYnkgRkJJIHN0YXR1cyAuLi4KCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIgIT0gMiwgZGF0YSA9IGljZ2NfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gaWNnY19jbGluaWNhbF9sYWJlbGVkKQpgYGAKCiMjIFRDR0EgdmFsaWRhdGlvbgoKV2hpbGUgd2UgY2FuJ3QgZGlyZWN0bHkgZG8gbXV0YXRpb24gc2lnbmF0dXJlIGFuYWx5c2lzIHdpdGggTU1DVE0gb24gZXhvbWUgZGF0YSAoSSBzdXBwb3NlIHRoaXMgaXMgdGhlb3JldGljYWxseSBwb3NzaWJsZSBidXQgd2UnZCBwcm9iYWJseSBiZSByZXN0cmljdGVkIGluIHRoZSB0eXBlcyBvZiBldmVudHMgd2UgY2FuIGNhbGwsIGxpa2UgbG9uZyBTVnMpLCB3ZSBjYW4gbG9vayBhdCB0aGUgRkJJLUhMQU1QIGZpbmRpbmcgZnJvbSBZaWthbidzIHBhcGVyIGFuZCBzZWUgaWYgdGhhdCBsaW5lcyB1cCB3aXRoIGltbXVuZSBzaWduYXR1cmVzLiAKCmBgYHtyfQp0Y2dhX2V4cHJfbWF0IDwtIHJlYWRfdGNnYV9leHBycyh0Y2dhX2V4cHJfbWF0X2ZpbGUpCnRjZ2Ffb3ZfYW5ub3RhdGlvbiA8LSBmcmVhZCh0Y2dhX292X2Fubm90YXRpb25fZmlsZSkKI3RjZ2FfZmJpX2hsYW1wX3Byb3BvcnRpb25zIDwtIGZyZWFkKHRjZ2FfZmJpX2hsYW1wX3Byb3BvcnRpb25zX2ZpbGUpCmBgYAoKYGBge3J9CnRjZ2FfZXhwcl9kZiA8LSB0Y2dhX2V4cHJfbWF0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInRjZ2Ffc2FtcGxlX2lkIikgJT4lIHQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiTmFtZSIpCnRjZ2FfY2VsbHR5cGVfbWF0cml4IDwtIGNyZWF0ZV9wYXRod2F5X21hdHJpeCh0Y2dhX2V4cHJfZGYsIGxhYmVscywgZGJfcGF0aCwgY29udmVydF9pZHMgPSBGQUxTRSkKCnRjZ2FfaW1tdW5lIDwtIHJvd25hbWVzX3RvX2NvbHVtbihhcy5kYXRhLmZyYW1lKHQodGNnYV9jZWxsdHlwZV9tYXRyaXgpKSwgdmFyID0gInRjZ2Ffc2FtcGxlX2lkIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9MTZ9Cm1hdCA8LSB0Y2dhX2ltbXVuZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0Y2dhX3NhbXBsZV9pZCIpICU+JSBzY2FsZSAjJT4lIGNsaXBfdmFsdWVzKDIsIC0yKQpyb3dfYW5ub3RhdGlvbnMgPC0gdGNnYV9vdl9hbm5vdGF0aW9uICU+JSBhcy5kYXRhLmZyYW1lICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInRjZ2Ffc2FtcGxlX2lkIikKCnRjZ2FoZWF0IDwtIHBoZWF0bWFwKG1hdCwgYW5ub3RhdGlvbl9yb3cgPSByb3dfYW5ub3RhdGlvbnMsIGNsdXN0ZXJfcm93cyA9IFRSVUUsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRCIsIHNob3dfcm93bmFtZXMgPSBGQUxTRSkKCiN0Y2dhX2NsdXN0ZXJzIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKGNsdXN0ZXI9ZmFjdG9yKGN1dHJlZSh0Y2dhaGVhdCR0cmVlX3JvdywgMikpKSwgdmFyID0gInRjZ2Ffc2FtcGxlX2lkIikKCmNvbCA8LSAiQ3l0b3RveGljaXR5Igp0Y2dhX2NsdXN0ZXJzIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKGNsdXN0ZXI9bWF0Wyxjb2xdID4gbWVkaWFuKG1hdFssY29sXSkpLCAidGNnYV9zYW1wbGVfaWQiKSAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBhcy5mYWN0b3IoYXMubnVtZXJpYyhjbHVzdGVyKSkpCgojcm93X2Fubm90YXRpb25zIDwtIHBseXI6OmpvaW4odGNnYV9vdl9hbm5vdGF0aW9uLCB0Y2dhX2NsdXN0ZXJzKSAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJ0Y2dhX3NhbXBsZV9pZCIpCiNwaGVhdG1hcChtYXQsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBjbHVzdGVyX3Jvd3MgPSBUUlVFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQiLCBzaG93X3Jvd25hbWVzID0gRkFMU0UpCmBgYAoKCmBgYHtyfQpkZl9tZXJnZWQgPC0gcGx5cjo6am9pbih0Y2dhX2ltbXVuZSwgdGNnYV9vdl9hbm5vdGF0aW9uKQoKZXhwcl9tZWFzdXJlX3ZhcnMgPC0gY29sbmFtZXModGNnYV9pbW11bmUpWzI6bmNvbCh0Y2dhX2ltbXVuZSldCgpkZl9tZXJnZWRfbWVsdGVkIDwtIG1lbHQoZGZfbWVyZ2VkLCBpZC52YXJzID0gYygidGNnYV9zYW1wbGVfaWQiLCAiU3ViZ3JvdXAiLCAiTW9sZWN1bGFyU3VidHlwZSIsICJCUkNBLnN0YXR1cyIpLCBtZWFzdXJlLnZhcnMgPSBleHByX21lYXN1cmVfdmFycywgdmFyaWFibGUubmFtZSA9ICJNZWFzdXJlIiwgdmFsdWUubmFtZSA9ICJFeHByZXNzaW9uIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodCA9IDE4fQpwdmFscyA8LSBzZXROYW1lcyhkZHBseShzdWJzZXQoZGZfbWVyZ2VkX21lbHRlZCwgIWlzLm5hKFN1Ymdyb3VwKSksIC4oTWVhc3VyZSksIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgdGVzdHJlcyA8LSBrcnVza2FsLnRlc3QoRXhwcmVzc2lvbiB+IGZhY3RvcihTdWJncm91cCksIGRhdGEgPSBkZikKICAKICBwdmFsIDwtIHRlc3RyZXMkcC52YWx1ZQogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCn0pLCBjKCJNZWFzdXJlIiwgInAudmFsdWUiKSkKCmdncGxvdChzdWJzZXQoZGZfbWVyZ2VkX21lbHRlZCwgIWlzLm5hKFN1Ymdyb3VwKSksIGFlcyh4PVN1Ymdyb3VwLCB5PUV4cHJlc3Npb24pKSArIGdlb21fYm94cGxvdCgpICsgdGhlbWVfYncoKSArIGZhY2V0X3dyYXAofiBNZWFzdXJlLCBzY2FsZXMgPSAiZnJlZSIsIG5jb2w9MykgKyB0aGVtZV9QdWJsaWNhdGlvbigpICsgZ2VvbV90ZXh0KGRhdGE9cHZhbHMsIGFlcyh4PUluZiwgeT1JbmYsIGxhYmVsPXAudmFsdWUpLCBoanVzdD0xLjEsIHZqdXN0PTEuNSxzaXplPTMscGFyc2U9VFJVRSkgCmBgYAoKUC12YWx1ZXMgYXJlIHVuY29ycmVjdGVkLiBTbyBhZnRlciBjb3JyZWN0aW9uLCB3ZSBhcmVuJ3QgZ29pbmcgdG8gZ2V0IGFueXRoaW5nIHNpZ25pZmljYW50LiAKCkluIGNvbmNsdXNpb24sIHRoZSBpbW11bmUgc3ViZ3JvdXBzIGNvbnN0aXR1dGUgYSBuZXcgc3ViZ3JvdXBpbmcgaW5kZXBlbmRlbnQgb2YgRkJJLUhMQU1QLiAKCkkndmUgYWxzbyBkb25lIGNvcnJlbGF0aW9uIHRlc3RpbmcgdXNpbmcgdGhlIGxvZ1IgdmFsdWVzIHRoYXQgWWlrYW4ncyBwcm92aWRlZCBtZSAocmF0aGVyIHRoYW4gdGhlIGRpc2NyZXRlIGdyb3VwaW5ncyBvZiBubyBBTVAsIEZCSS1BTVAgbG93LCBhbmQgRkJJLUFNUCBoaWdoKSwgYnV0IHRoZSBjb3JyZWxhdGlvbnMgYXJlIHZlcnkgcG9vciAocmhvIHZhbHVlcyBvZiB+IC0wLjA1IG9yIHNvLCBwLXZhbHVlcyBvZiA+PTAuMykuIAoKIyMjIFN1cnZpdmFsCgoKYGBge3J9CnRjZ2FfY2xpbmljYWwgPC0gcmVhZF90Y2dhX2NsaW5pY2FsKHRjZ2FfZG9ub3JfZmlsZSwgdHlwZSA9ICJzeW5hcHNlMiIsIGZpbHRlciA9IFRSVUUsIHVuaXF1ZSA9IEZBTFNFKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkIDwtIFJlZHVjZShmdW5jdGlvbih4LHkpIHBseXI6OmpvaW4oeCx5LCB0eXBlID0gJ2Z1bGwnKSwgbGlzdCh0Y2dhX2NsdXN0ZXJzLCB0Y2dhX2NsaW5pY2FsLCB0Y2dhX292X2Fubm90YXRpb24pKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkJFN1cnZPYmogPC0gd2l0aCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIFN1cnYoaWZlbHNlKHZpdGFsX3N0YXR1cyA9PSAiRGVhZCIsIGRlYXRoX2RheXNfdG8sIGxhc3RfY29udGFjdF9kYXlzX3RvKSwgdml0YWxfc3RhdHVzID09ICJEZWFkIikpCgojdGNnYV9jbGluaWNhbF9sYWJlbGVkIDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ID09ICJOTyIgJiBhZGRpdGlvbmFsX2ltbXVub190aGVyYXB5ID09ICJOTyIgJiBhZGRpdGlvbmFsX3BoYXJtYWNldXRpY2FsX3RoZXJhcHkgPT0gIk5PIiAmICB0YXJnZXRlZF9tb2xlY3VsYXJfdGhlcmFweSA9PSAiTk8iICYgcmFkaWF0aW9uX3RoZXJhcHkgPT0gIk5PIikKI3RjZ2FfY2xpbmljYWxfbGFiZWxlZCA8LSBzdWJzZXQodGNnYV9jbGluaWNhbF9sYWJlbGVkLCBzdHJfZGV0ZWN0KHR1bW9yX3N0YWdlLCAiXklJSSIpKQpgYGAKCiMjIyMgRkJJLUFNUCBjb2xvY2FsaXphdGlvbgoKTGV0J3MgZmlyc3Qgc3RyYXRpZnkgYnkgWWlrYW4ncyBncm91cGluZ3M6CgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBTdWJncm91cCwgZGF0YSA9IHRjZ2FfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgpOb3RlOiBUaGUgbG9ncmFuayBwLXZhbHVlcyBpbiB0aGUgcGFwZXIgYXJlIGluY29ycmVjdCwgdGhlIG9uZSdzIEkndmUgY29tcHV0ZWQgYXJlIHRoZSByaWdodCBvbmVzLiBJbiB0aGUgcGFwZXIsIHRoZSBkZWF0aCBkYXlzIGZyb20gdGhlIGRlYWQgcGF0aWVudHMgd2VyZSBub3QgY29ycmVjdGx5IGNvbWJpbmVkIGludG8gdGhlIGNvbXB1dGF0aW9uLCBzbyBwLXZhbHVlcyB3ZXJlIGNvbXB1dGVkIHdpdGhvdXQgdGhlIGRlYWQgcGF0aWVudHMuIAoKIyMjIyBJbW11bmUgYWN0aXZpdHkKCldlJ2xsIG5vdyBzdHJhdGlmeSBieSBgY2x1c3RlcmAsIGEgdmFyaWFibGUgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciBvciBub3Qgd2UncmUgYWJvdmUgbWVkaWFuIGluIHRlcm1zIG9mIGByIGNvbGAuIEluIG90aGVyIHdvcmRzLCAxIG1lYW5zIHdlJ3JlIGhpZ2ggaW1tdW5lLCBhbmQgMCBtZWFucyB3ZSdyZSBsb3cgaW1tdW5lLiAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSB0Y2dhX2NsaW5pY2FsX2xhYmVsZWQpCmBgYAoKTG93IGltbXVuZSB0dW1vdXJzIHRyZW5kIHRvd2FyZHMgcG9vciBzdXJ2aXZhbCBidXQgdGhpcyBpcyBub3Qgc2lnbmlmaWNhbnQgYXQgYWxsLiAKCk5vdyBsZXQncyB0cnkgYnkgdHJlYXRpbmcgaW1tdW5lIGFjdGl2aXR5IGFzIGEgY29udGludW91cyB2YXJpYWJsZSwgY29udHJvbGxpbmcgZm9yIG90aGVyIGNvdmFyaWF0ZXM6CgpgYGB7cn0KdGNnYV9pbW11bmVfY29udGludW91cyA8LSAgdGNnYV9jZWxsdHlwZV9tYXRyaXggJT4lIHQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigidGNnYV9zYW1wbGVfaWQiKQoKdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMgPC0gcGx5cjo6am9pbih0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIHRjZ2FfaW1tdW5lX2NvbnRpbnVvdXMpCgpjb3hwaChTdXJ2T2JqIH4gQ3l0b3RveGljaXR5ICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMgKyB0dW1vcl9ncmFkZSArIHN0cl9leHRyYWN0KHR1bW9yX3N0YWdlLCAiW0lWXSsiKSArIGFkZGl0aW9uYWxfY2hlbW9fdGhlcmFweSArIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ICsgYWRkaXRpb25hbF9pbW11bm9fdGhlcmFweSwgdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMpCmBgYAoKSW5kZWVkLCBpbW11bmUgYWN0aXZpdHkgaXMgc2lnbmlmaWNhbnRseSBhc3NvY2lhdGVkIHdpdGggcHJvbG9uZ2VkIHN1cnZpdmFsIChuZWdhdGl2ZSBjb2VmZmljaWVudCA9IGZld2VyIGRlYXRoIGV2ZW50cykuIAoKIyMjIyBTdHJhdGlmaWNhdGlvbiBieSBmb2xkYmFjay1BTVAgc3RhdHVzCgpgYGB7cn0KZmJpX2hpZ2ggPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIpCmZiaV9sb3cgPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgU3ViZ3JvdXAgPT0gIkZCSS1BTVAgTG93IikKZmJpX25vIDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIFN1Ymdyb3VwID09ICJObyBBTVAiKQpmYmlfbm90aGlnaCA8LSBzdWJzZXQodGNnYV9jbGluaWNhbF9sYWJlbGVkLCBTdWJncm91cCAlaW4lIGMoIkZCSS1BTVAgTG93IiwgIk5vIEFNUCIpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSBmYmlfaGlnaCkKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX2xvdykKYGBgCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX25vKQpgYGAKCldoYXQncyBjdXJpb3VzIGFib3V0IHRoaXMgaXMgdGhhdCBpdCBzZWVtcyBvbmx5IHRoZSBubyBBTVAgZ3JvdXAgZ2V0cyBhbnkgYmVuZWZpdCBmcm9tIGhhdmluZyBhIGhpZ2ggaW1tdW5lIHJlc3BvbnNlLiBTbyB0aGVyZSdzIG5vIGJlbmVmaXQgd2l0aGluIHRoZSBmb2xkYmFjayBncm91cCBvZiBoaWdoL2xvdyBpbW11bmUgcmVzcG9uc2UuIApgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyLCBkYXRhID0gZmJpX25vdGhpZ2gpCmBgYAoKIyMjIyBTdHJhdGlmaWNhdGlvbiBieSBpbW11bmUgYWN0aXZpdHkKCmBgYHtyfQppbW11bmVfbG93IDwtIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQsIGNsdXN0ZXIgPT0gMCkKaW1tdW5lX2hpZ2ggPC0gc3Vic2V0KHRjZ2FfY2xpbmljYWxfbGFiZWxlZCwgY2x1c3RlciA9PSAxKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFN1Ymdyb3VwLCBkYXRhID0gaW1tdW5lX2hpZ2gpCgpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFN1Ymdyb3VwLCBkYXRhID0gaW1tdW5lX2xvdykKYGBgCgpMaWtld2lzZSwgdGhlIG9ubHkgYmVuZWZpdCBvZiBiZWluZyBub24tZm9sZGJhY2sgaXMgZGVyaXZlZCBmcm9tIHRoZSBpbW11bmUtaGlnaCBncm91cCAtLSB0aGVyZSdzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiBiZWluZyBmb2xkYmFjayBvciBub3QgaWYgeW91J3JlIGluIHRoZSBsb3cgaW1tdW5lIGdyb3VwLiAKCiMjIyMgTXVsdGl2YXJpYXRlIG1vZGVscwoKYGBge3J9CnNpbXBsZV9zdXJ2aXZhbF9hbmFseXNpcyhTdXJ2T2JqIH4gU3ViZ3JvdXAgKyBjbHVzdGVyLCBkYXRhID0gdGNnYV9jbGluaWNhbF9sYWJlbGVkKQpgYGAKCldlJ2xsIG5leHQgY29tYmluZWQgRkJJLUFNUCBsb3cgYW5kIE5vIEFNUCBpbnRvIGEgc2luZ2xlIGNhdGVnb3J5IGNhbGxlZCBGQkktTm90SGlnaC4gCgpgYGB7cn0KdGNnYV9jbGluaWNhbF9sYWJlbGVkJG5ld2dyb3VwIDwtIGlmZWxzZSh0Y2dhX2NsaW5pY2FsX2xhYmVsZWQkU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIsICJGQkktSGlnaCIsICJGQkktTm90SGlnaCIpCgpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IG5ld2dyb3VwICsgY2x1c3RlciwgZGF0YSA9IHRjZ2FfY2xpbmljYWxfbGFiZWxlZCkKYGBgCgoKTGV0J3MgYWxzbyBkbyBDb3ggcHJvcG9ydGlvbmFsIGhhemFyZHMgbW9kZWxzOgoKYGBge3J9Cm1vZDEgPC0gY294cGgoU3Vydk9iaiB+IEN5dG90b3hpY2l0eSArIFN1Ymdyb3VwICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMgKyB0dW1vcl9ncmFkZSArIHN0cl9leHRyYWN0KHR1bW9yX3N0YWdlLCAiW0lWXSsiKSArIGFkZGl0aW9uYWxfY2hlbW9fdGhlcmFweSArIGFkZGl0aW9uYWxfZHJ1Z190aGVyYXB5ICsgYWRkaXRpb25hbF9pbW11bm9fdGhlcmFweSwgdGNnYV9jbGluaWNhbF9sYWJlbGVkX2NvbnRpbnVvdXMpCmFub3ZhKG1vZDEpCmBgYAoKYGBge3J9Cm1vZDIgPC0gY294cGgoU3Vydk9iaiB+IEN5dG90b3hpY2l0eSArIFN1Ymdyb3VwICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMsIHRjZ2FfY2xpbmljYWxfbGFiZWxlZF9jb250aW51b3VzKQphbm92YShtb2QyKQpgYGAKCmBgYHtyfQptb2QzIDwtIGNveHBoKFN1cnZPYmogfiBDeXRvdG94aWNpdHkgKyAoU3ViZ3JvdXAgPT0gIkZCSS1BTVAgSGlnaCIpICsgYWdlX2F0X2luaXRpYWxfcGF0aG9sb2dpY19kaWFnbm9zaXMsIHN1YnNldCh0Y2dhX2NsaW5pY2FsX2xhYmVsZWRfY29udGludW91cywgIWlzLm5hKFN1Ymdyb3VwKSkpCmFub3ZhKG1vZDMpCmBgYAoKU28gdGhlIGVmZmVjdHMgb2YgaW1tdW5lIGFjdGl2aXR5IGFuZCBGQkkvSFJEIHN0YXR1cyBhcmUgY29sbGluZWFyLiAKCiMjIEZpbmFsIHJ1bgoKSW5zdGVhZCBvZiBvdmVyd3JpdGluZyB0aGUgcHJldmlvdXMsIHRoaXMgaXMgaXRzIG93biBzZWN0aW9uIHNpbmNlIHRoZSB1bmRlcmx5aW5nIGltcGxlbWVudGF0aW9uIGhhcyBjaGFuZ2VkIHN1YnN0YW50aWFsbHkuIAoKYGBge3J9CnZhcmlhbnRfdGFibGUgPC0gcmVhZF92YXJpYW50X2ZpbGUobWFzdGVyX3ZhcmlhbnRfZmlsZSwgZGJfcGF0aCkKYnJlYWtwb2ludF90YWJsZSA8LSByZWFkX3ZhcmlhbnRfZmlsZShtYXN0ZXJfYnJlYWtwb2ludF9maWxlLCBkYl9wYXRoKQoKc2lnX3Jlc3VsdHMgPC0gcmVhZF9zaWduYXR1cmVfZmlsZXMobW1jdG1fZmluYWxfcGF0aWVudF9kaXIsIHZhcmlhbnRfdGFibGUsIGJyZWFrcG9pbnRfdGFibGUsIGRiX3BhdGgpCgpzaWdfcmVzdWx0c19zbnYgPC0gc3Vic2V0KHNpZ19yZXN1bHRzJHNudiwgaXNfcHJlc2VudCA9PSAxKQpzaWdfcmVzdWx0c19zdiA8LSBzdWJzZXQoc2lnX3Jlc3VsdHMkc3YsIGlzX3ByZXNlbnQgPT0gMSkKc2lnX3Jlc3VsdHNfbm9uaXRoIDwtIHNpZ19yZXN1bHRzJG5vbml0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJzaWduYXR1cmUiKSAlPiUgdCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKQoKc2lnbmF0dXJlX2xhYmVscyA8LSBzdHJfZXh0cmFjdChjKGNvbG5hbWVzKHNpZ19yZXN1bHRzX3NudiksIGNvbG5hbWVzKHNpZ19yZXN1bHRzX3N2KSksICJTTj9WLVswLTldKyIpCnNpZ25hdHVyZV9sYWJlbHMgPC0gc2lnbmF0dXJlX2xhYmVsc1shaXMubmEoc2lnbmF0dXJlX2xhYmVscyldCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMgPC0gZnVuY3Rpb24oeCwgYnk9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gRkFMU0UpIHsKICBwcm9wcyA8LSBzdWJzZXQoeCwgc2VsZWN0PWMoYnksIGNvbG5hbWVzKHgpW2NvbG5hbWVzKHgpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSAlPiUgZ3JvdXBfYnlfKC5kb3RzPWJ5KSAlPiUgc3VtbWFyaXNlX2VhY2goZnVucyhzdW0pKQogIAogIGlmIChyZXBvcnRfY291bnQpIHsKICAgIGNvdW50cyA8LSBzdWJzZXQoeCwgc2VsZWN0PWMoYnksIGNvbG5hbWVzKHgpW2NvbG5hbWVzKHgpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSAlPiUgZ3JvdXBfYnlfKC5kb3RzPWJ5KSAlPiUgc3VtbWFyaXNlKG49bigpKQogICAgcHJvcHMgPC0gcGx5cjo6am9pbihwcm9wcyAlPiUgYXMuZGF0YS5mcmFtZSwgY291bnRzICU+JSBhcy5kYXRhLmZyYW1lKQogIH0KICBzdW1zIDwtIHJvd1N1bXMoc3Vic2V0KHByb3BzLCBzZWxlY3Q9Y29sbmFtZXMocHJvcHNbY29sbmFtZXMocHJvcHMpICVpbiUgc2lnbmF0dXJlX2xhYmVsc10pKSkKICBzaWdzIDwtIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhwcm9wcykpCiAgZm9yIChzaWcgaW4gc2lncykgewogICAgcHJvcHNbLHNpZ10gPC0gcHJvcHNbLHNpZ10vc3VtcwogIH0KICByZXR1cm4ocHJvcHMpCn0KYGBgCgojIyMgU2lnbmF0dXJlcwoKIVtdKGByIG1tY3RtX2ZpbmFsX3BhdGllbnRfc2lncGxvdGApCgojIyMgU2FtcGxlIHNpZ25hdHVyZSBwcm9wb3J0aW9ucwoKVGhlc2UgcHJvcG9ydGlvbnMgd29uJ3QgYmUgZXhhY3RseSBlcXVhbCB0byB0aGUgdG9waWMgcHJvcG9ydGlvbnMgdGhhdCB0aGUgbW9kZWwgb3V0cHV0cyAodGhleSBhcmUgdmFyaWF0aW9uYWwgZXN0aW1hdGVzKSwgYnV0IHdlJ3JlIGFjdHVhbGx5IGRvaW5nIHByZXR0eSBkYXJuIHdlbGwuIAoKVE9ETzogTWFrZSBhIFFDIHBsb3QuIEZyb20gYSBnbGFuY2UgaXQgc2VlbXMgdGhhdCBwcm9wb3J0aW9uIGVycm9yIGlzIHVzdWFsbHkgd2l0aGluIDEtNSUgcmVsYXRpdmUgZXJyb3IuIAoKYGBge3J9CnNhbXBsZV9wcm9wc19zbnYgPC0gc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhzaWdfcmVzdWx0c19zbnYsIGJ5PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiksIHNpZ25hdHVyZV9sYWJlbHMpCnNhbXBsZV9wcm9wc19zdiA8LSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKHNpZ19yZXN1bHRzX3N2LCBieT1jKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIpLCBzaWduYXR1cmVfbGFiZWxzKQoKcGF0aWVudF9wcm9wc19zbnYgPC0gc2lnX3Jlc3VsdHNfc252ICU+JSBzdWJzZXQoc2VsZWN0PWMoInBhdGllbnRfaWQiLCAiY2hyb20iLCAiY29vcmQiLCAicmVmIiwgImFsdCIsIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnYpKSkpICU+JSB1bmlxdWUgJT4lIHN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMoYnk9YygicGF0aWVudF9pZCIpLCBzaWduYXR1cmVfbGFiZWxzKQpwYXRpZW50X3Byb3BzX3N2IDwtIHNpZ19yZXN1bHRzX3N2ICU+JSBzdWJzZXQoc2VsZWN0PWMoInBhdGllbnRfaWQiLCAicHJlZGljdGlvbl9pZCIsIGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhzaWdfcmVzdWx0c19zdikpKSkgJT4lIHVuaXF1ZSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiksIHNpZ25hdHVyZV9sYWJlbHMpCmBgYAoKYGBge3J9CnNhbXBsZV9wcm9wcyA8LSBwbHlyOjpqb2luKHNhbXBsZV9wcm9wc19zbnYgJT4lIGFzLmRhdGEuZnJhbWUsIHNhbXBsZV9wcm9wc19zdiAlPiUgYXMuZGF0YS5mcmFtZSkKc2FtcGxlX3Byb3BzIDwtIHJiaW5kLmZpbGwoc2FtcGxlX3Byb3BzLCBzaWdfcmVzdWx0c19ub25pdGgpCgpzYW1wbGVfcHJvcHNfbWF0IDwtIHNhbXBsZV9wcm9wcyAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCJjb25kZW5zZWRfaWQiKSAlPiUgc3Vic2V0KHNlbGVjdD1jKGNvbG5hbWVzKHNhbXBsZV9wcm9wcylbY29sbmFtZXMoc2FtcGxlX3Byb3BzKSAlaW4lIHNpZ25hdHVyZV9sYWJlbHNdKSkKCnNhbXBsZV9wcm9wc19tYXRfc2NhbGVkIDwtIHNjYWxlKHNhbXBsZV9wcm9wc19tYXQpCiNbLGMoIlNOVi0yIiwgIlNOVi01IiwgIlNWLTMiLCAiU1YtNiIsICJTVi04IiwgIlNWLTEiLCAiU1YtNyIsICJTVi01IiwgIlNOVi0zIildCnNhbXBsZV9wcm9wc19oZWF0IDwtIHBoZWF0bWFwKHNhbXBsZV9wcm9wc19tYXRfc2NhbGVkLCBmb250c2l6ZV9yb3cgPSA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKYGBgCgpgYGB7cn0KcGF0aWVudF9wcm9wcyA8LSBwbHlyOjpqb2luKHBhdGllbnRfcHJvcHNfc252ICU+JSBhcy5kYXRhLmZyYW1lLCBwYXRpZW50X3Byb3BzX3N2ICU+JSBhcy5kYXRhLmZyYW1lKQpwYXRpZW50X3Byb3BzIDwtIHJiaW5kLmZpbGwocGF0aWVudF9wcm9wcywgc2lnX3Jlc3VsdHNfbm9uaXRoICU+JSBwbHlyOjpyZW5hbWUoYygiY29uZGVuc2VkX2lkIj0icGF0aWVudF9pZCIpKSkKCnBhdGllbnRfcHJvcHNfbWF0IDwtIHBhdGllbnRfcHJvcHMgJT4lIGNvbHVtbl90b19yb3duYW1lcygicGF0aWVudF9pZCIpICU+JSBzdWJzZXQoc2VsZWN0PWMoY29sbmFtZXMocGF0aWVudF9wcm9wcylbY29sbmFtZXMocGF0aWVudF9wcm9wcykgJWluJSBzaWduYXR1cmVfbGFiZWxzXSkpCgpwYXRpZW50X3Byb3BzX21hdF9zY2FsZWQgPC0gc2NhbGUocGF0aWVudF9wcm9wc19tYXQpCiNbLGMoIlNOVi0yIiwgIlNOVi01IiwgIlNWLTMiLCAiU1YtNiIsICJTVi04IiwgIlNWLTEiLCAiU1YtNyIsICJTVi01IiwgIlNOVi0zIildCnBhdGllbnRfcHJvcHNfaGVhdCA8LSBwaGVhdG1hcChwYXRpZW50X3Byb3BzX21hdF9zY2FsZWQsIGZvbnRzaXplX3JvdyA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iKQpgYGAKCiMjIyMgSVRIIGNvaG9ydAoKYGBge3J9CnNhbXBsZV9jbHVzdHMgPC0gYXMuZGF0YS5mcmFtZShjdXRyZWUoc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3csIDQpKSAlPiUgc2V0TmFtZXMoYygiY2x1c3RlciIpKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKQoKaWhjX3N0YWJpbGl6ZV9zdWJzZXQgPC0gc3RhYmlsaXplX2loY192YXJpYW5jZXMoaWhjX3RhYmxlX3NsaWRlLCBpaGNfdGFibGUsIHRpbHR5cGVzKQoKZGF0YV9tYXRyaWNlcyA8LSBsaXN0KHNhbXBsZV9jbHVzdHMsIGloY19zdGFiaWxpemVfc3Vic2V0LCB4Y3JfZGl2ZXJzaXR5LCBjZWxsdHlwZV9kZiwgcGF0aHdheV9kZiwgcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHlfc3Vic2V0KQpkYXRhX21hdHJpY2VzIDwtIGxhcHBseShkYXRhX21hdHJpY2VzLCBmdW5jdGlvbih4KSB7CiAgeCAlPiUgZHBseXI6OnNlbGVjdCgtb25lX29mKCJwYXRpZW50X2lkIikpCn0pCgpjb21iaW5lZF9kZiA8LSBSZWR1Y2UoZnVuY3Rpb24oeCwgeSkgcGx5cjo6am9pbih4LHksdHlwZT0nZnVsbCcpLCBkYXRhX21hdHJpY2VzKQpjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsICFpcy5uYShjbHVzdGVyKSAmICFjb25kZW5zZWRfaWQgJWluJSBjKCI3X0Jybk0iKSkKY29tYmluZWRfZGYgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBjb25kZW5zZWRfaWQgJWluJSBzdWJzZXQoc2FtcGxlcywgcHJvamVjdF9jb2RlID09ICJJVEgiKSRjb25kZW5zZWRfaWQpCgpjb21iaW5lZF9tYXQgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9LWMoY29uZGVuc2VkX2lkLCBjbHVzdGVyKSkKcm93bmFtZXMoY29tYmluZWRfbWF0KSA8LSBjb21iaW5lZF9kZiRjb25kZW5zZWRfaWQKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OH0KI3JlZl9kZW5kcm9ncmFtIDwtIHBydW5lKGFzLmRlbmRyb2dyYW0oc2FtcGxlX2hlYXQkdHJlZV9yb3cpLCAiN19Ccm5NIikKCnNhbXBsZV9vcmRlciA8LSBzYW1wbGVfcHJvcHNfaGVhdCR0cmVlX3JvdyRsYWJlbHNbc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckb3JkZXJdCnNhbXBsZV9vcmRlciA8LSBpbnRlcnNlY3Qoc2FtcGxlX29yZGVyLCByb3duYW1lcyhjb21iaW5lZF9tYXQpKQoKY2x1c3Rlcl9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD1jKGNvbmRlbnNlZF9pZCwgY2x1c3RlcikpCgpTSUdTIDwtIGMoIlNWLTMiLCAiU05WLTUiLCAiU1YtNiIsICJTVi04IiwgIlNOVi0yIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHNhbXBsZV9wcm9wc19tYXRbLFNJR1NdLCBjaGVjay5uYW1lcyA9IEZBTFNFKSwgdmFyID0gImNvbmRlbnNlZF9pZCIpCgojIyBUYWtlIGxvZ2FyaXRobXMgdG8gfnZhcmlhbmNlIHN0YWJpbGl6ZQp2YXJpYW50X2NvdW50X2Fubm90YXRpb25zIDwtIHZhcmlhbnRfc2FtcGxlICU+JSBncm91cF9ieV8oLmRvdHM9ImNvbmRlbnNlZF9pZCIpICU+JSAKICBzdW1tYXJpc2Uoc252X2NvdW50PWxvZyhzdW0oc252X2NvdW50KSksIGJrcHRfY291bnQ9bG9nKHN1bShia3B0X2NvdW50KSkpICU+JQogIHN1YnNldChzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgInNudl9jb3VudCIsICJia3B0X2NvdW50IikpCgpyb3dfYW5ub3RhdGlvbnMgPC0gUmVkdWNlKHBseXI6OmpvaW4sIGxpc3QoY2x1c3Rlcl9hbm5vdGF0aW9ucywgbW9sc3VidHlwZXMsIHNpZ19hbm5vdGF0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhbnRfY291bnRfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNvbmRlbnNlZF9pZCIpCnJvd19hbm5vdGF0aW9uc19pdGggPC0gcm93X2Fubm90YXRpb25zCgpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIHNjYWxlKGNvbWJpbmVkX21hdCkKCnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNvbWJpbmVkX21hdF9zY2FsZWQsIDIsIC0yKVtzYW1wbGVfb3JkZXIsXSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBmb250c2l6ZSA9IDYpCgpjb21iaW5lZF9tYXRfc2NhbGVkX2l0aCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkCmBgYAoKIyMjIyBJQ0dDIHZhbGlkYXRpb24KCmBgYHtyfQppY2djX2ZiaV9zdGF0dXMgPC0gZnJlYWQod2FuZ19pY2djX2ZiaV9zdGF0dXNfZmlsZSkKY29sbmFtZXMoaWNnY19mYmlfc3RhdHVzKSA8LSBtYXB2YWx1ZXMoY29sbmFtZXMoaWNnY19mYmlfc3RhdHVzKSwgZnJvbSA9IGMoIkNhc2VfSUQiKSwgdG8gPSBjKCJjb25kZW5zZWRfaWQiKSkKYGBgCgpgYGB7cn0KY29sbmFtZXMoaWNnY19pbW11bmUpIDwtIG1hcHZhbHVlcyhjb2xuYW1lcyhpY2djX2ltbXVuZSksICJuZXdfaWQiLCAiY29uZGVuc2VkX2lkIikKCmRhdGFfbWF0cmljZXMgPC0gbGlzdChzYW1wbGVfY2x1c3RzLCBpY2djX2ltbXVuZSkKZGF0YV9tYXRyaWNlcyA8LSBsYXBwbHkoZGF0YV9tYXRyaWNlcywgZnVuY3Rpb24oeCkgewogIHggJT4lIGRwbHlyOjpzZWxlY3QoLW9uZV9vZigicGF0aWVudF9pZCIpKQp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCiNjb21iaW5lZF9kZiA8LSBzdWJzZXQoY29tYmluZWRfZGYsIGNsdXN0ZXIgPT0gMSkKCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQpyb3duYW1lcyhjb21iaW5lZF9tYXQpIDwtIGNvbWJpbmVkX2RmJGNvbmRlbnNlZF9pZApgYGAKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04fQpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9wcm9wc19oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0KSkKCmNsdXN0ZXJfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGNvbWJpbmVkX2RmLCBzZWxlY3Q9Yyhjb25kZW5zZWRfaWQsIGNsdXN0ZXIpKQoKU0lHUyA8LSBjKCJTVi0zIiwgIlNOVi01IiwgIlNWLTYiLCAiU1YtOCIsICJTTlYtMiIpCnNpZ19hbm5vdGF0aW9ucyA8LSByb3duYW1lc190b19jb2x1bW4oZGF0YS5mcmFtZShzYW1wbGVfcHJvcHNfbWF0WyxTSUdTXSwgY2hlY2submFtZXMgPSBGQUxTRSksIHZhciA9ICJjb25kZW5zZWRfaWQiKQoKc3VidHlwZV9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoaWNnY19zdWJ0eXBlcywgc2VsZWN0PWMoImljZ2NfZG9ub3JfaWQiLCAic3VidHlwZSIsICJubWZfc3VidHlwZSIpKQpjb2xuYW1lcyhzdWJ0eXBlX2Fubm90YXRpb25zKVsxXSA8LSAiY29uZGVuc2VkX2lkIgoKaWNnY19mYmlfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2NfZmJpX3N0YXR1cywgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJQYXRjaCBldCBhbC4gQ2xhc3MiLCAiUGF0Y2ggZXQgYWwuIE1vbGVjdWxhciBTdWJncm91cCIsICJCUkNBLnN0YXR1cyIsICJTdWJncm91cCIpKQoKcm93X2Fubm90YXRpb25zIDwtIFJlZHVjZShwbHlyOjpqb2luLCBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIHNpZ19hbm5vdGF0aW9ucywgc3VidHlwZV9hbm5vdGF0aW9ucywgaWNnY19mYmlfYW5ub3RhdGlvbnMpKQpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImNvbmRlbnNlZF9pZCIpCnJvd19hbm5vdGF0aW9uc19ub2l0aCA8LSByb3dfYW5ub3RhdGlvbnMKCiNzZWxlY3Rfcm93cyA8LSByb3duYW1lcyhzdWJzZXQocm93X2Fubm90YXRpb25zLCBgU1YtNGAgPCAxKSkKbm90eCA8LSBzdWJzZXQoc3BlY2ltZW5fZGF0YSwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKI3NhbXBsZV9vcmRlciA8LSBpbnRlcnNlY3Qoc2FtcGxlX29yZGVyLCBzZWxlY3Rfcm93cykKc2FtcGxlX29yZGVyIDwtIGludGVyc2VjdChzYW1wbGVfb3JkZXIsIG5vdHgpCgojb3JkZXJfZmJpIDwtIG9yZGVyKHJvd19hbm5vdGF0aW9uc1tyb3duYW1lcyhjb21iaW5lZF9tYXRfc2NhbGVkKSxdJGBTVi04YCkKCiNjb21iaW5lZF9tYXRfc2NhbGVkIDwtIHNjYWxlKGNvbWJpbmVkX21hdCkKY29tYmluZWRfbWF0X3NjYWxlZCA8LSBhcy5kYXRhLmZyYW1lKGFwcGx5KGNvbWJpbmVkX21hdCwgMiwgZnVuY3Rpb24oeCkgKHgtbWVkaWFuKHgsbmEucm09VFJVRSkpL21hZCh4LG5hLnJtPVRSVUUpKSkKCmNvbWJpbmVkX21hdF9zY2FsZWQgPC0gY29tYmluZWRfbWF0X3NjYWxlZFtzYW1wbGVfb3JkZXIsXQpvdGhlcl9tYXQgPC0gc2FtcGxlX3Byb3BzX21hdF9zY2FsZWRbc2FtcGxlX29yZGVyLF0KCnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNiaW5kKGNvbWJpbmVkX21hdF9zY2FsZWQsIG90aGVyX21hdCksIDIsIC0yKSwgY2x1c3Rlcl9yb3dzID0gRkFMU0UsIGNsdXN0ZXJfY29scyA9IFRSVUUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBmb250c2l6ZSA9IDYsIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiKQoKY29tYmluZWRfbWF0X3NjYWxlZF9ub2l0aCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkCmBgYAoKCmBgYHtyfQppY2djX2NsaW5pY2FsX2xhYmVsZWQgPC0gUmVkdWNlKGZ1bmN0aW9uKHgseSkgcGx5cjo6am9pbih4LHksdHlwZT0nZnVsbCcpLCBsaXN0KHNldE5hbWVzKGNsdXN0ZXJfYW5ub3RhdGlvbnMsIGMoImljZ2NfZG9ub3JfaWQiLCAiY2x1c3RlciIpKSwgaWNnY19jbGluaWNhbCwgc2V0TmFtZXMoc3Vic2V0KGljZ2NfZmJpX2Fubm90YXRpb25zLCBzZWxlY3Q9YygiY29uZGVuc2VkX2lkIiwgIkJSQ0Euc3RhdHVzIiwgIlN1Ymdyb3VwIikpLCBjKCJpY2djX2Rvbm9yX2lkIiwgIkJSQ0Euc3RhdHVzIiwgIldhbmdfc3ViZ3JvdXAiKSkpKQoKaWNnY19jbGluaWNhbF9sYWJlbGVkJFN1cnZPYmogPC0gd2l0aChpY2djX2NsaW5pY2FsX2xhYmVsZWQsIFN1cnYoZG9ub3Jfc3Vydml2YWxfdGltZSwgZG9ub3Jfdml0YWxfc3RhdHVzID09ICJkZWNlYXNlZCIpKQpgYGAKClN1cnZpdmFsIGJ5IEZCSSBzdGF0dXMgLi4uCgpgYGB7cn0Kc2ltcGxlX3N1cnZpdmFsX2FuYWx5c2lzKFN1cnZPYmogfiBjbHVzdGVyICE9IDEsIGRhdGEgPSBzdWJzZXQoaWNnY19jbGluaWNhbF9sYWJlbGVkLCBjbHVzdGVyICE9IDQpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IGNsdXN0ZXIsIGRhdGEgPSBzdWJzZXQoaWNnY19jbGluaWNhbF9sYWJlbGVkLCBjbHVzdGVyICE9IDQpKQpgYGAKCmBgYHtyfQpzaW1wbGVfc3Vydml2YWxfYW5hbHlzaXMoU3Vydk9iaiB+IFdhbmdfc3ViZ3JvdXAsIGRhdGEgPSBpY2djX2NsaW5pY2FsX2xhYmVsZWQpCmBgYAoKIyMjIyBDb21iaW5lZAoKIyMjIyMgU2FtcGxlLWxldmVsCgpgYGB7cn0KY29tYmluZWRfbWF0X3NjYWxlZF9hbGwgPC0gcmJpbmQuZmlsbChjb21iaW5lZF9tYXRfc2NhbGVkX2l0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKSwgY29tYmluZWRfbWF0X3NjYWxlZF9ub2l0aCAlPiUgYXMuZGF0YS5mcmFtZSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjb25kZW5zZWRfaWQiKSkgJT4lIGNvbHVtbl90b19yb3duYW1lcygiY29uZGVuc2VkX2lkIikKCnJvd19hbm5vdGF0aW9uc19hbGwgPC0gcmJpbmQuZmlsbChyb3dfYW5ub3RhdGlvbnNfaXRoICU+JSByb3duYW1lc190b19jb2x1bW4oImNvbmRlbnNlZF9pZCIpLCByb3dfYW5ub3RhdGlvbnNfbm9pdGggJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY29uZGVuc2VkX2lkIikpICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoImNvbmRlbnNlZF9pZCIpCgpzYW1wbGVfb3JkZXIgPC0gc2FtcGxlX3Byb3BzX2hlYXQkdHJlZV9yb3ckbGFiZWxzW3NhbXBsZV9wcm9wc19oZWF0JHRyZWVfcm93JG9yZGVyXQpzYW1wbGVfb3JkZXIgPC0gaW50ZXJzZWN0KHNhbXBsZV9vcmRlciwgcm93bmFtZXMoY29tYmluZWRfbWF0X3NjYWxlZF9hbGwpKQoKI2NvbWJpbmVkX21hdF9zY2FsZWQgPC0gYXMuZGF0YS5mcmFtZShhcHBseShjb21iaW5lZF9tYXRfYWxsLCAyLCBmdW5jdGlvbih4KSAoeC1tZWRpYW4oeCxuYS5ybT1UUlVFKSkvbWFkKHgsbmEucm09VFJVRSkpKQpjb21iaW5lZF9tYXRfc2NhbGVkIDwtIGNvbWJpbmVkX21hdF9zY2FsZWRfYWxsW3NhbXBsZV9vcmRlcixdCgpuYW5vc3RyaW5nX3ZhcnMgPC0gcm93bmFtZXMoaWNnY19jZWxsdHlwZV9tYXRyaXgpCmNvbWJpbmVkX21hdF9zY2FsZWRfc3Vic2V0IDwtIHN1YnNldChjb21iaW5lZF9tYXRfc2NhbGVkLCBzZWxlY3Q9bmFub3N0cmluZ192YXJzKQpjb21iaW5lZF9tYXRfc2NhbGVkX3N1YnNldCA8LSBjb21iaW5lZF9tYXRfc2NhbGVkX3N1YnNldFshYXBwbHkoY29tYmluZWRfbWF0X3NjYWxlZF9zdWJzZXQsIDEsIGZ1bmN0aW9uKHgpIGFsbChpcy5uYSh4KSkpLF0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTJ9CnBoZWF0bWFwKGNsaXBfdmFsdWVzKGNvbWJpbmVkX21hdF9zY2FsZWRfc3Vic2V0LCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9uc19hbGwsIGZvbnRzaXplID0gNiwgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKRG9lc24ndCByZWFsbHkgY2x1c3RlciBjb25zaXN0ZW50bHkgYmV0d2VlbiBJQ0dDIGFuZCBvdXIgc2FtcGxlcy4gCgpUT0RPOiBCYXRjaCBjb3JyZWN0IElDR0MgYW5kIG91ciBkYXRhIHRvZ2V0aGVyIChJIHRoaW5rIEkgYWxyZWFkeSBkaWQgdGhpcyBzb21ld2hlcmUpIC0tIGFuZCBub3JtYWxpemUgdGhlIGZpbmFsIG1hdHJpeCBpbiBvbmUgc3RlcCByYXRoZXIgdGhhbiBub3JtYWxpemluZyBzZXBhcmF0ZWx5IGZvciBlYWNoIHN1YmNvaG9ydCBhbmQgY29tYmluaW5nIHRob3NlIHRvZ2V0aGVyLiBPdGhlcndpc2Ugd2UgY291bGQgYWx3YXlzIGJlIHN1YmplY3QgdG8gdGhlIHNjZW5hcmlvIHdoZXJlIHRoZSBJVEggY29ob3J0IG1heSBiZSBza2V3ZWQgaW4gaW1tdW5lIHJlc3BvbnNlIChlaXRoZXIgYWxsIHJlbGF0aXZlbHkgbG93IG9yIGhpZ2gpIGNvbXBhcmVkIHRvIHRoZSBJQ0dDIGNvaG9ydC4gCgpUT0RPOiBVc2UgYWJzb2x1dGUgY291bnRzIG9mIG11dGF0aW9ucyBmb3IgZWFjaCBzaWduYXR1cmUsIGFuZCBjbHVzdGVyIGJhc2VkIG9uIHRob3NlLiBJdCBtYXkgYmUgdGhhdCBzYW1wbGVzIHdpdGggdG9vIGxvdy9oaWdoIG11dGF0aW9uIGNvdW50cyBhcmUgbm90IGNsdXN0ZXJpbmcgcHJvcGVybHkuIAoKIyMjIyMgUGF0aWVudC1sZXZlbAoKYGBge3J9CiNpdGhfaWNnY19iYXRjaF9jb3JyZWN0ZWRfZXhwcmVzc2lvbl9maWxlIDwtICJ+L3NoYWhsYWIvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2l0aF9pY2djX21lcmdlZF9iYy50c3YiCgppdGhfaWNnY19leHByZXNzaW9uIDwtIGZyZWFkKGl0aF9pY2djX2JhdGNoX2NvcnJlY3RlZF9leHByZXNzaW9uX2ZpbGUpCgppdGhfaWNnY19jZWxsdHlwZV9leHByIDwtIGNyZWF0ZV9jZWxsdHlwZV9tYXRyaXgoaXRoX2ljZ2NfZXhwcmVzc2lvbiwgbGFiZWxzLCBkYl9wYXRoLCBjb252ZXJ0X2lkcyA9IEZBTFNFKQppdGhfaWNnY19wYXRod2F5X2V4cHIgPC0gY3JlYXRlX3BhdGh3YXlfbWF0cml4KGl0aF9pY2djX2V4cHJlc3Npb24sIGxhYmVscywgZGJfcGF0aCwgY29udmVydF9pZHMgPSBGQUxTRSkKCmljZ2Nfc3BlY2ltZW5fZGF0YSA8LSBmcmVhZChpY2djX3NwZWNpbWVuX2ZpbGUpCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9leHByZXNzaW9uX2J5X3BhdGllbnQgPC0gZnVuY3Rpb24oZXhwcikgewogIHByaW1hcnlfc3BlY2ltZW5fZGF0IDwtIHN1YnNldChpY2djX3NwZWNpbWVuX2RhdGEsIHN0cl9kZXRlY3Qoc3BlY2ltZW5fdHlwZSwgIlByaW1hcnkiKSkKICAKICBub3R4IDwtIHN1YnNldChwcmltYXJ5X3NwZWNpbWVuX2RhdCwgc3RyX2RldGVjdChzcGVjaW1lbl90eXBlLCAiUHJpbWFyeSIpICYgc3BlY2ltZW5fZG9ub3JfdHJlYXRtZW50X3R5cGUgPT0gIm5vIHRyZWF0bWVudCIpJGljZ2NfZG9ub3JfaWQKICBwcmltYXJ5X3NwZWNpbWVuX2RhdCA8LSBzdWJzZXQocHJpbWFyeV9zcGVjaW1lbl9kYXQsIGljZ2NfZG9ub3JfaWQgJWluJSBub3R4KQogIAogIHggPC0gc2V0TmFtZXMobWVsdChhcy5tYXRyaXgoZXhwcikpLCBjKCJOYW1lIiwgInNhbXBsZSIsICJleHByIikpCiAgeCRwYXRpZW50X2lkIDwtIG1hcF9pZChhcy5jaGFyYWN0ZXIoeCRzYW1wbGUpLCBmcm9tID0gImNvbmRlbnNlZF9pZCIsIHRvPSJwYXRpZW50X2lkIiwgZGJfcGF0aCkKICBpZHggPC0gaXMubmEoeCRwYXRpZW50X2lkKQogIGRvbm9yX2xhYmVscyA8LSBkZl9hc19tYXAocHJpbWFyeV9zcGVjaW1lbl9kYXQsIGFzLmNoYXJhY3Rlcih4JHNhbXBsZVtpZHhdKSwgZnJvbSA9ICJpY2djX3NwZWNpbWVuX2lkIix0byA9ICJpY2djX2Rvbm9yX2lkIikKICB4JHBhdGllbnRfaWRbaWR4XSA8LSBkb25vcl9sYWJlbHMKICB4IDwtIHN1YnNldCh4LCAhaXMubmEocGF0aWVudF9pZCkpCiAgeF9zdW0gPC0geCAlPiUgZ3JvdXBfYnkoTmFtZSwgcGF0aWVudF9pZCkgJT4lIHN1bW1hcmlzZShleHByPW1lYW4oZXhwciwgbmEucm09VFJVRSkpCiAgCiAgcmVzIDwtIGRjYXN0KHhfc3VtLCBmb3JtdWxhID0gTmFtZSB+IHBhdGllbnRfaWQsIHZhbHVlLnZhciA9ICJleHByIikKICByZXR1cm4ocmVzKQp9CmBgYAoKYGBge3J9Ck5DTFVTVCA8LSAzCmNsdXN0ZXJzIDwtIGN1dHJlZShwYXRpZW50X3Byb3BzX2hlYXQkdHJlZV9yb3csIE5DTFVTVCkKcGF0aWVudF9jbHVzdHMgPC0gbWFrZV9jbHVzdGVyX2ZyYW1lKGNsdXN0ZXJzKQoKY2x1c3Rlcl9hbm5vdGF0aW9ucyA8LSBzdWJzZXQocGx5cjo6cmVuYW1lKHBhdGllbnRfY2x1c3RzLCBjKCduZXdfaWQnPSdwYXRpZW50X2lkJykpLCBzZWxlY3Q9YyhwYXRpZW50X2lkLCBjbHVzdGVyKSkKCml0aF9pY2djX2NlbGx0eXBlX2V4cHJfcGF0aWVudCA8LSBzdW1tYXJpemVfZXhwcmVzc2lvbl9ieV9wYXRpZW50KGl0aF9pY2djX2NlbGx0eXBlX2V4cHIpCml0aF9pY2djX3BhdGh3YXlfZXhwcl9wYXRpZW50IDwtIHN1bW1hcml6ZV9leHByZXNzaW9uX2J5X3BhdGllbnQoaXRoX2ljZ2NfcGF0aHdheV9leHByKQoKZXhwcl9kYXQgPC0gaXRoX2ljZ2NfcGF0aHdheV9leHByX3BhdGllbnQKY29tYmluZWRfcGF0aWVudF9tYXQgPC0gZXhwcl9kYXQgJT4lIGFzLmRhdGEuZnJhbWUgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAiTmFtZSIpICU+JSAKICB0ICU+JSBhcy5kYXRhLmZyYW1lICU+JSByb3duYW1lc190b19jb2x1bW4oInBhdGllbnRfaWQiKQoKZGF0YV9tYXRyaWNlcyA8LSBsaXN0KGNsdXN0ZXJfYW5ub3RhdGlvbnMsIGNvbWJpbmVkX3BhdGllbnRfbWF0KQpkYXRhX21hdHJpY2VzIDwtIGxhcHBseShkYXRhX21hdHJpY2VzLCBmdW5jdGlvbih4KSB7CiAgeAp9KQoKY29tYmluZWRfZGYgPC0gUmVkdWNlKGZ1bmN0aW9uKHgsIHkpIHBseXI6OmpvaW4oeCx5LHR5cGU9J2lubmVyJyksIGRhdGFfbWF0cmljZXMpCmNvbWJpbmVkX21hdCA8LSBzdWJzZXQoY29tYmluZWRfZGYsIHNlbGVjdD0tYyhjbHVzdGVyKSkKcm93bmFtZXMoY29tYmluZWRfbWF0KSA8LSBOVUxMCmNvbWJpbmVkX21hdCA8LSBjb21iaW5lZF9tYXQgJT4lIGNvbHVtbl90b19yb3duYW1lcygicGF0aWVudF9pZCIpCgpTSUdTIDwtIGMoIlNWLTMiLCAiU05WLTUiLCAiU1YtNiIsICJTVi04IiwgIlNOVi0yIikKc2lnX2Fubm90YXRpb25zIDwtIHJvd25hbWVzX3RvX2NvbHVtbihkYXRhLmZyYW1lKHBhdGllbnRfcHJvcHNfbWF0WyxTSUdTXSwgY2hlY2submFtZXMgPSBGQUxTRSksIHZhciA9ICJwYXRpZW50X2lkIikKCnN1YnR5cGVfYW5ub3RhdGlvbnMgPC0gc3Vic2V0KGljZ2Nfc3VidHlwZXMsIHNlbGVjdD1jKCJpY2djX2Rvbm9yX2lkIiwgInN1YnR5cGUiLCAibm1mX3N1YnR5cGUiKSkKY29sbmFtZXMoc3VidHlwZV9hbm5vdGF0aW9ucylbMV0gPC0gInBhdGllbnRfaWQiCgppY2djX2ZiaV9zdGF0dXNfcGF0aWVudCA8LSBmcmVhZCh3YW5nX2ljZ2NfZmJpX3N0YXR1c19maWxlKQpjb2xuYW1lcyhpY2djX2ZiaV9zdGF0dXNfcGF0aWVudCkgPC0gbWFwdmFsdWVzKGNvbG5hbWVzKGljZ2NfZmJpX3N0YXR1c19wYXRpZW50KSwgZnJvbSA9IGMoIkNhc2VfSUQiKSwgdG8gPSBjKCJwYXRpZW50X2lkIikpCgppY2djX2ZiaV9hbm5vdGF0aW9ucyA8LSBzdWJzZXQoaWNnY19mYmlfc3RhdHVzX3BhdGllbnQsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgIlBhdGNoIGV0IGFsLiBDbGFzcyIsICJQYXRjaCBldCBhbC4gTW9sZWN1bGFyIFN1Ymdyb3VwIiwgIkJSQ0Euc3RhdHVzIiwgIlN1Ymdyb3VwIikpCgpyb3dfYW5ub3RhdGlvbnMgPC0gUmVkdWNlKHBseXI6OmpvaW4sIGxpc3QoY2x1c3Rlcl9hbm5vdGF0aW9ucywgc2lnX2Fubm90YXRpb25zLCBzdWJ0eXBlX2Fubm90YXRpb25zLCBpY2djX2ZiaV9hbm5vdGF0aW9ucykpCgpyb3dfYW5ub3RhdGlvbnMgPC0gcm93X2Fubm90YXRpb25zICU+JSBjb2x1bW5fdG9fcm93bmFtZXMoInBhdGllbnRfaWQiKQoKY29tYmluZWRfcGF0aWVudF9tYXRfc2NhbGVkIDwtIGNvbWJpbmVkX21hdCAlPiUgc2NhbGUKCnBhdGllbnRfb3JkZXIgPC0gcGF0aWVudF9wcm9wc19oZWF0JHRyZWVfcm93JGxhYmVsc1twYXRpZW50X3Byb3BzX2hlYXQkdHJlZV9yb3ckb3JkZXJdCnBhdGllbnRfb3JkZXIgPC0gaW50ZXJzZWN0KHBhdGllbnRfb3JkZXIsIHJvd25hbWVzKGNvbWJpbmVkX3BhdGllbnRfbWF0X3NjYWxlZCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTEwfQpwaGVhdG1hcChjbGlwX3ZhbHVlcyhjb21iaW5lZF9wYXRpZW50X21hdF9zY2FsZWRbcGF0aWVudF9vcmRlcixdLCAyLCAtMiksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLCBjbHVzdGVyX2NvbHMgPSBUUlVFLCBhbm5vdGF0aW9uX3JvdyA9IHJvd19hbm5vdGF0aW9ucywgZm9udHNpemUgPSA2LCBjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY29ycmVsYXRpb24iLCBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIikKYGBgCgoKCiMjIyBBbmNlc3RyYWwtZGVzY2VuZGFudCBzaWduYXR1cmUgcHJvcG9ydGlvbnMKCmBgYHtyfQphbmNkZXNjX3Byb3BzX3NudiA8LSB1bmlxdWUoc3Vic2V0KHNpZ19yZXN1bHRzX3Nudiwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAiaXNfYW5jZXN0cmFsIiwgImNocm9tIiwgImNvb3JkIiwgInJlZiIsICJhbHQiLCBzaWduYXR1cmVfbGFiZWxzW3NpZ25hdHVyZV9sYWJlbHMgJWluJSBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnYpXSkpKSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiwgImlzX2FuY2VzdHJhbCIpLCBzaWduYXR1cmVfbGFiZWxzKQphbmNkZXNjX3Byb3BzX3N2IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc3YsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgInByZWRpY3Rpb25faWQiLCAiaXNfYW5jZXN0cmFsIiwgc2lnbmF0dXJlX2xhYmVsc1tzaWduYXR1cmVfbGFiZWxzICVpbiUgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc3YpXSkpKSAlPiUgc3VtbWFyaXplX3NpZ25hdHVyZV9wcm9wb3J0aW9ucyhieT1jKCJwYXRpZW50X2lkIiwgImlzX2FuY2VzdHJhbCIpLCBzaWduYXR1cmVfbGFiZWxzKQoKYW5jZGVzY19wcm9wcyA8LSBwbHlyOjpqb2luKGFuY2Rlc2NfcHJvcHNfc252ICU+JSBhcy5kYXRhLmZyYW1lLCBhbmNkZXNjX3Byb3BzX3N2ICU+JSBhcy5kYXRhLmZyYW1lKQoKYW5jZGVzY19wcm9wc19tYXQgPC0gc3Vic2V0KGFuY2Rlc2NfcHJvcHMsIHNlbGVjdD1jb2xuYW1lcyhhbmNkZXNjX3Byb3BzKVtjb2xuYW1lcyhhbmNkZXNjX3Byb3BzKSAlaW4lIHNpZ25hdHVyZV9sYWJlbHNdKQpgYGAKCmBgYHtyfQphbmNkZXNjX3Byb3BzX3NjYWxlZCA8LSBzY2FsZShhbmNkZXNjX3Byb3BzX21hdCkKcm93bmFtZXMoYW5jZGVzY19wcm9wc19zY2FsZWQpIDwtIHdpdGgoYW5jZGVzY19wcm9wcywgcGFzdGUocGF0aWVudF9pZCwgaXNfYW5jZXN0cmFsLCBzZXA9Il8iKSkKCnBoZWF0bWFwKGFuY2Rlc2NfcHJvcHNfc2NhbGVkLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIpCmBgYAoKYGBge3J9CmFuY2Rlc2NfcHJvcHNfbWVsdGVkIDwtIG1lbHQoYW5jZGVzY19wcm9wcywgaWQudmFycyA9IGMoInBhdGllbnRfaWQiLCAiaXNfYW5jZXN0cmFsIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChjb2xuYW1lcyhhbmNkZXNjX3Byb3BzKSwgc2lnbmF0dXJlX2xhYmVscyksIHZhcmlhYmxlLm5hbWUgPSAic2lnbmF0dXJlIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKCnB2YWxzIDwtIHNldE5hbWVzKGRkcGx5KGFuY2Rlc2NfcHJvcHNfbWVsdGVkLCAuKHNpZ25hdHVyZSksIGZ1bmN0aW9uKHgpIHsKICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgY29ycmVzIDwtIHdpbGNveC50ZXN0KHByb3BvcnRpb24gfiBpc19hbmNlc3RyYWwsIGRmLCBwYWlyZWQ9VFJVRSkKICAKICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoUCk9PXAsIGxpc3QocD1mb3JtYXQocHZhbCwgZGlnaXRzPTMpKSkKICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKfSksIGMoInNpZ25hdHVyZSIsICJwLnZhbHVlIikpCgpnZ3Bsb3QoYW5jZGVzY19wcm9wc19tZWx0ZWQsIGFlcyh4PWZhY3Rvcihpc19hbmNlc3RyYWwpLCB5PXByb3BvcnRpb24pKSArIGdlb21fYm94cGxvdCgpICsgZmFjZXRfd3JhcCh+IHNpZ25hdHVyZSwgc2NhbGVzPSJmcmVlIikgKyBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKSArIAp0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKQpgYGAKCiMjIyBPcmlnaW4gbm9kZSBzaWduYXR1cmUgcHJvcG9ydGlvbnMKCioqTm90ZSoqOiBXZSBjYW5ub3QgZ2V0IG5vZGUtc3BlY2lmaWMgcmVhcnJhbmdlbWVudCBzaWduYXR1cmVzLiAKCioqTm90ZSoqOiBUaGVzZSBub2RlIGxhYmVscyBETyBOT1QgY29ycmVzcG9uZCB0byBub2RlIGxhYmVscyB3aXRoaW4gdGhlIGNsb25lIHBoeWxvZ2VueTsgdGhleSBjb3JyZXNwb25kIHRvIG5vZGVzIHdpdGhpbiB0aGUgRG9sbG8gbW9kZWwuIAoKYGBge3J9Cm5vZGVfcHJvcHNfc252IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc252LCBzZWxlY3Q9YygicGF0aWVudF9pZCIsICJvcmlnaW5fbm9kZSIsICJjaHJvbSIsICJjb29yZCIsICJyZWYiLCAiYWx0Iiwgc2lnbmF0dXJlX2xhYmVsc1tzaWduYXR1cmVfbGFiZWxzICVpbiUgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252KV0pKSkgJT4lIHN1bW1hcml6ZV9zaWduYXR1cmVfcHJvcG9ydGlvbnMoYnk9YygicGF0aWVudF9pZCIsICJvcmlnaW5fbm9kZSIpLCBzaWduYXR1cmVfbGFiZWxzLCByZXBvcnRfY291bnQgPSBUUlVFKQpgYGAKYGBge3J9Cm5vZGVfcHJvcHNfbWF0IDwtIHN1YnNldChub2RlX3Byb3BzX3Nudiwgc2VsZWN0PWNvbG5hbWVzKG5vZGVfcHJvcHNfc252KVtjb2xuYW1lcyhub2RlX3Byb3BzX3NudikgJWluJSBzaWduYXR1cmVfbGFiZWxzXSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MTB9Cm5vZGVfcHJvcHNfc2NhbGVkIDwtIHNjYWxlKG5vZGVfcHJvcHNfbWF0KQpyb3duYW1lcyhub2RlX3Byb3BzX3NjYWxlZCkgPC0gd2l0aChub2RlX3Byb3BzX3NudiwgcGFzdGUocGF0aWVudF9pZCwgb3JpZ2luX25vZGUsIHNlcD0iXyIpKQoKcm93X2Fubm90YXRpb25zIDwtIHN1YnNldChub2RlX3Byb3BzX3Nudiwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibiIpKQpyb3dfYW5ub3RhdGlvbnMkbiA8LSBsb2cyKHJvd19hbm5vdGF0aW9ucyRuKQpyb3dfYW5ub3RhdGlvbnMkcGF0aWVudF9pZCA8LSBmYWN0b3JfaWQocm93X2Fubm90YXRpb25zJHBhdGllbnRfaWQsIHR5cGUgPSAicGF0aWVudF9pZCIsIGRiX3BhdGgpCnJvd25hbWVzKHJvd19hbm5vdGF0aW9ucykgPC0gcm93bmFtZXMobm9kZV9wcm9wc19zY2FsZWQpCgpjbHVzdGVyaW5nX2NvbG91cnMgPC0gbGlzdChwYXRpZW50X2lkID0gcGFsX3BhdGllbnQpCgpwaGVhdG1hcChjbGlwX3ZhbHVlcyhub2RlX3Byb3BzX3NjYWxlZCwgMiwgLTIpLCBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiZXVjbGlkZWFuIiwgY2x1c3RlcmluZ19tZXRob2QgPSAid2FyZC5EMiIsIGZvbnRzaXplX3JvdyA9IDUsIGFubm90YXRpb25fcm93ID0gcm93X2Fubm90YXRpb25zLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGNsdXN0ZXJpbmdfY29sb3VycykKYGBgCgpMb29rcyBsaWtlIHRoZXJlIGFyZSBzaW1pbGFyIHNlbGVjdGlvbiBwcmVzc3VyZXMgYWN0aW5nIGF0IGVhY2ggcGFydCBvZiB0aGUgc2FtcGxlIHBoeWxvZ2VueSAtLSBpLmUuIG11dGF0aW9uIHNpZ25hdHVyZXMgYXJlICdyZWxhdGl2ZWx5JyBjb25zaXN0ZW50IHdpdGhpbiBwYXRpZW50cyB0aHJvdWdob3V0IHRpbWUuIAoKIyMjIENsb25hbCBwaHlsb2dlbnkgYnJhbmNoLXNwZWNpZmljIHNpZ25hdHVyZSBwcm9wb3J0aW9ucwoKTk9URTogVEhFIExBQkVMUyBPTiBUSEVTRSBUUkVFUyBNQVkgTk9UIEJFIFRIRSBTQU1FIEFTIFRIT1NFIElOIFRIRSBNQVBTQ0FQRVMgKHVnaCkuIAoKYGBge3J9CnRyZWVfYnJhbmNoX2RhdGEgPC0gcmVhZF9jbG9uZV90cmVlX2RhdGEoY2xvbmVfdHJlZV9maWxlLCBjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUsIGNsb25lX3ByZXZhbGVuY2VfZmlsZSwgZGJfcGF0aCkKdHJlZXMgPC0gbGFwcGx5KHRyZWVfYnJhbmNoX2RhdGEsIGZ1bmN0aW9uKHgpIHgkdHJlZSkKYnJhbmNoX2xlbmd0aHMgPC0gcmJpbmQuZmlsbChsYXBwbHkodHJlZV9icmFuY2hfZGF0YSwgZnVuY3Rpb24oeCkgeCRicmFuY2hfZGF0KSkKYGBgCgpgYGB7cn0Kc252X2NsdXN0ZXIgPC0gcmJpbmQuZmlsbChsYXBwbHkoc2VxX2Fsb25nKHNudl9jbHVzdGVyX2ZpbGVzKSwgZnVuY3Rpb24oaSkgewogIGYgPC0gc252X2NsdXN0ZXJfZmlsZXNbW2ldXQogIHBhdGllbnRfaWQgPC0gc252X2NsdXN0ZXJfcGF0aWVudHNbW2ldXQogIHNudl9jbHVzdGVyIDwtIHJlYWRfc252X2NsdXN0ZXIoZiwgY2xvbmVfYnJhbmNoX2xlbmd0aF9maWxlLCBkYl9wYXRoKQogIHJldHVybihzbnZfY2x1c3RlcikKfSkpCgpzbnZfY2x1c3RlciA8LSBwbHlyOjpqb2luKHNudl9jbHVzdGVyLCBicmFuY2hfbGVuZ3RocykKYGBgCgpgYGB7cn0Kc252X2NsdXN0ZXJfZmlsdGVyZWQgPC0gc3Vic2V0KHNudl9jbHVzdGVyLCAhaXMubmEobGFiZWwpKQoKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIgPC0gcGx5cjo6am9pbihzaWdfcmVzdWx0c19zbnYsIHNudl9jbHVzdGVyX2ZpbHRlcmVkKQpgYGAKCmBgYHtyfQpicmFuY2hfcHJvcHNfc252IDwtIHVuaXF1ZShzdWJzZXQoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIsIHNlbGVjdD1jKCJwYXRpZW50X2lkIiwgImxhYmVsIiwgImNocm9tIiwgImNvb3JkIiwgInJlZiIsICJhbHQiLCBzaWduYXR1cmVfbGFiZWxzW3NpZ25hdHVyZV9sYWJlbHMgJWluJSBjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnZfY2x1c3RlcildKSkpICU+JSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKGJ5PWMoInBhdGllbnRfaWQiLCAibGFiZWwiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gVFJVRSkKYGBgCgpgYGB7cn0KYnJhbmNoX3Byb3BzX3Nudl9tZWx0ZWQgPC0gbWVsdChicmFuY2hfcHJvcHNfc252LCBpZC52YXJzID0gYygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhicmFuY2hfcHJvcHNfc252KSksIHZhcmlhYmxlLm5hbWUgPSAic2lnbmF0dXJlIiwgdmFsdWUubmFtZSA9ICJwcm9wb3J0aW9uIikKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9MjV9CmdncGxvdChicmFuY2hfcHJvcHNfc252X21lbHRlZCwgYWVzKHg9bGFiZWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9iYXIoYWVzKGZpbGw9c2lnbmF0dXJlKSwgc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfd3JhcCh+IHBhdGllbnRfaWQsIG5jb2w9MSwgc2NhbGVzID0gImZyZWVfeCIpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBnZW9tX3RleHQoZGF0YT11bmlxdWUoc3Vic2V0KGJyYW5jaF9wcm9wc19zbnZfbWVsdGVkLCBzZWxlY3Q9YygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIikpKSwgYWVzKHg9bGFiZWwsIHk9MSwgbGFiZWw9biksdmp1c3Q9LS4yLHN0YXQ9ImlkZW50aXR5IikgKyB5bGltKGMoMCwgMS4yKSkKYGBgCgpXaGF0IGlmIHdlIHVzZSBNQVAgYXNzaWdubWVudHM/IChUaGlzIHdvdWxkIGFsbG93IHVzIHRvIGFwcGx5IGNoaS1zcXVhcmUgdGVzdHMuKSAKClRPRE86IEZpZ3VyZSBvdXQgaG93IHRvIGFwcGx5IHRlc3RzIGJldHdlZW4gayA+IDIgZ3JvdXBzIG9mIHByb3BvcnRpb25zLi4uIGluIG90aGVyIHdvcmRzLCBhIHRlc3Qgb2YgaG9tb2dlbmVpdHkuIAoKYGBge3J9CmlkX3ZhcnMgPC0gY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIpWyFjb2xuYW1lcyhzaWdfcmVzdWx0c19zbnZfY2x1c3RlcikgJWluJSBzaWduYXR1cmVfbGFiZWxzXQoKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXJfbWVsdGVkIDwtIG1lbHQoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIsIGlkLnZhcnMgPSBpZF92YXJzLCBtZWFzdXJlLnZhcnMgPSBpbnRlcnNlY3Qoc2lnbmF0dXJlX2xhYmVscywgY29sbmFtZXMoc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXIpKSwgdmFyaWFibGUubmFtZSA9ICJzaWduYXR1cmUiLCB2YWx1ZS5uYW1lID0gInByb3BvcnRpb24iKQptYXhlcyA8LSBzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQgJT4lIGdyb3VwX2J5XyguZG90cyA9IGlkX3ZhcnMpICU+JSBzdW1tYXJpc2UobWF4cHJvcD1tYXgocHJvcG9ydGlvbikpCnNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX21lbHRlZCA8LSBwbHlyOjpqb2luKHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX21lbHRlZCxtYXhlcykKc2lnX3Jlc3VsdHNfc252X2NsdXN0ZXJfbWVsdGVkJHByb3BvcnRpb25fbWFwIDwtIGlmZWxzZShzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQkcHJvcG9ydGlvbiA9PSBzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQkbWF4cHJvcCwgMSwgMCkKCnNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCA8LSBkY2FzdChzaWdfcmVzdWx0c19zbnZfY2x1c3Rlcl9tZWx0ZWQsIGZvcm11bGEgPSBwYXN0ZTAocGFzdGUoaWRfdmFycywgY29sbGFwc2U9IisiKSwgIn4gc2lnbmF0dXJlIiksIHZhbHVlLnZhciA9ICJwcm9wb3J0aW9uX21hcCIpCgpicmFuY2hfcHJvcHNfc252X21hcCA8LSB1bmlxdWUoc3Vic2V0KHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibGFiZWwiLCAiY2hyb20iLCAiY29vcmQiLCAicmVmIiwgImFsdCIsIHNpZ25hdHVyZV9sYWJlbHNbc2lnbmF0dXJlX2xhYmVscyAlaW4lIGNvbG5hbWVzKHNpZ19yZXN1bHRzX3Nudl9jbHVzdGVyX2Nhc3RlZCldKSkpICU+JSBzdW1tYXJpemVfc2lnbmF0dXJlX3Byb3BvcnRpb25zKGJ5PWMoInBhdGllbnRfaWQiLCAibGFiZWwiKSwgc2lnbmF0dXJlX2xhYmVscywgcmVwb3J0X2NvdW50ID0gVFJVRSkKCmJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCA8LSBtZWx0KGJyYW5jaF9wcm9wc19zbnZfbWFwLCBpZC52YXJzID0gYygicGF0aWVudF9pZCIsICJsYWJlbCIsICJuIiksIG1lYXN1cmUudmFycyA9IGludGVyc2VjdChzaWduYXR1cmVfbGFiZWxzLCBjb2xuYW1lcyhicmFuY2hfcHJvcHNfc252X21hcCkpLCB2YXJpYWJsZS5uYW1lID0gInNpZ25hdHVyZSIsIHZhbHVlLm5hbWUgPSAicHJvcG9ydGlvbiIpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD0yNX0KZ2dwbG90KGJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCwgYWVzKHg9bGFiZWwsIHk9cHJvcG9ydGlvbikpICsgZ2VvbV9iYXIoYWVzKGZpbGw9c2lnbmF0dXJlKSwgc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfd3JhcCh+IHBhdGllbnRfaWQsIG5jb2w9MSwgc2NhbGVzID0gImZyZWVfeCIpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBnZW9tX3RleHQoZGF0YT11bmlxdWUoc3Vic2V0KGJyYW5jaF9wcm9wc19zbnZfbWFwX21lbHRlZCwgc2VsZWN0PWMoInBhdGllbnRfaWQiLCAibGFiZWwiLCAibiIpKSksIGFlcyh4PWxhYmVsLCB5PTEsIGxhYmVsPW4pLHZqdXN0PS0uMixzdGF0PSJpZGVudGl0eSIpICsgeWxpbShjKDAsIDEuMikpCmBgYAoKIyMjIEFkanVzdGVkIGNsb25lIHRyZWVzCgpgYGB7cn0KdHJlZXNfYWdlIDwtIHRyZWVzCmFnZV9zaWduYXR1cmUgPC0gIlNOVi01IgoKYnJhbmNoX3Byb3BzX2RhdCA8LSBicmFuY2hfcHJvcHNfc252X21hcF9tZWx0ZWQKCnRyZWVfb2JqZWN0cyA8LSBsYXBwbHkoc2VxX2Fsb25nKHRyZWVzX2FnZSksIGZ1bmN0aW9uKGkpIHsKICB0cmVlIDwtIHRyZWVzX2FnZVtbaV1dCiAgdHJlZV9vbGQgPC0gdHJlZQogIHBhdGllbnQgPC0gbmFtZXModHJlZXNfYWdlKVtpXQogIGJyYW5jaF9wcm9wc19kYXRfc3ViIDwtIHN1YnNldChicmFuY2hfcHJvcHNfZGF0LCBwYXRpZW50X2lkID09IGFzLm51bWVyaWMocGF0aWVudCkgJiBzaWduYXR1cmUgPT0gYWdlX3NpZ25hdHVyZSkKICBicmFuY2hfcHJvcHNfZGF0X3N1YiRsZW5ndGggPC0gd2l0aChicmFuY2hfcHJvcHNfZGF0X3N1YiwgcHJvcG9ydGlvbipuKQogIAogIGVkZ2VfbGVuZ3RocyA8LSB0cmVlQGVkZ2UubGVuZ3RoCiAgaWR4IDwtIHdoaWNoKGVkZ2VfbGVuZ3RocyA9PSAwKQogIHRvX2xhYmVscyA8LSB0cmVlQGxhYmVsW3N0cl9leHRyYWN0KG5hbWVzKGVkZ2VfbGVuZ3RocyksICJbMC05XSskIildCiAgbGVuZ3RocyA8LSBkZl9hc19tYXAoYnJhbmNoX3Byb3BzX2RhdF9zdWIsIHVubmFtZSh0b19sYWJlbHMpLCBmcm9tID0gImxhYmVsIiwgdG89Imxlbmd0aCIpCiAgaWYgKGxlbmd0aChpZHgpID4gMCkgewogICAgbGVuZ3Roc1tpZHhdIDwtIDAKICB9CiAgdHJlZUBlZGdlLmxlbmd0aCA8LSBsZW5ndGhzCiAgbmFtZXModHJlZUBlZGdlLmxlbmd0aCkgPC0gbmFtZXMoZWRnZV9sZW5ndGhzKQogIAogIHJldHVybihsaXN0KGFsbD10cmVlX29sZCwgYWdlPXRyZWUpKQp9KQpuYW1lcyh0cmVlX29iamVjdHMpIDwtIG5hbWVzKHRyZWVzX2FnZSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTIwfQp0bXAgPC0gdW5saXN0KHRyZWVfb2JqZWN0cykKI2lnbm9yZSA8LSBsYXBwbHkoc2VxX2Fsb25nKHRtcCksIGZ1bmN0aW9uKGkpIHsKIyAgcHJpbnQocGxvdCh0bXBbW2ldXSwgc2hvdy5ub2RlLmxhYmVsID0gVFJVRSwgbWFpbiA9IG5hbWVzKHRtcClbaV0pKQojfSkKYGBgCgpgYGB7cn0KZmluZF9hbmNlc3RvcnMgPC0gZnVuY3Rpb24odHJlZSkgewogIHggPC0gcGh5bG9iYXNlOjo6LnBoeWxvNFRvRGF0YUZyYW1lKHRyZWUpCiAgcm9vdCA8LSBzdWJzZXQoeCwgbm9kZS50eXBlID09ICJyb290IikkbGFiZWwKICByb290X251bWJlciA8LSBuYW1lcyh3aGljaCh0cmVlQGxhYmVsID09IHJvb3QpKQogIGRpcmVjdF9kZXNjZW5kYW50cyA8LSBzdWJzZXQoeCwgYW5jZXN0b3IgPT0gYXMubnVtZXJpYyhyb290X251bWJlcikpJGxhYmVsCiAgaWYgKGxlbmd0aChkaXJlY3RfZGVzY2VuZGFudHMpID09IDEpIHsKICAgIHJldHVybihjKHJvb3QsIGRpcmVjdF9kZXNjZW5kYW50cykpCiAgfSBlbHNlIHsKICAgIHJldHVybihyb290KQogIH0KfQpgYGAKCmBgYHtyfQpwYXRpZW50cyA8LSB1bmlxdWUoYnJhbmNoX3Byb3BzX2RhdCRwYXRpZW50X2lkKQpyb290X2RhdGEgPC0gcmJpbmQuZmlsbChsYXBwbHkocGF0aWVudHMsIGZ1bmN0aW9uKHBhdCkgewogIHRyZWUgPC0gdHJlZXNbW2FzLmNoYXJhY3RlcihwYXQpXV0KICBhbmNlc3RvcnMgPC0gZmluZF9hbmNlc3RvcnModHJlZSkKICByYmluZC5maWxsKGxhcHBseShhbmNlc3RvcnMsIGZ1bmN0aW9uKHgpIHsKICAgIGRhdGEuZnJhbWUobGFiZWw9eCwgcGF0aWVudF9pZD1wYXQpCiAgfSkpCn0pKQpyb290X2RhdGEkbm9kZV90eXBlIDwtICJyb290IgpgYGAKCmBgYHtyfQpicmFuY2hfZGF0YV9hbm5vdGF0ZWQgPC0gcGx5cjo6am9pbihicmFuY2hfcHJvcHNfZGF0LCByb290X2RhdGEpCmJyYW5jaF9kYXRhX2Fubm90YXRlZCRub2RlX3R5cGVbaXMubmEoYnJhbmNoX2RhdGFfYW5ub3RhdGVkJG5vZGVfdHlwZSldIDwtICJkZXNjZW5kYW50IgoKcm9vdF9wcm9wb3J0aW9ucyA8LSBicmFuY2hfZGF0YV9hbm5vdGF0ZWQgJT4lIHN1YnNldChub2RlX3R5cGUgPT0gInJvb3QiKSAlPiUgZ3JvdXBfYnkocGF0aWVudF9pZCwgc2lnbmF0dXJlKSAlPiUgc3VtbWFyaXNlKHByb3BvcnRpb249d2VpZ2h0ZWQubWVhbihwcm9wb3J0aW9uLCB3ID0gbikpICU+JSBwbHlyOjpyZW5hbWUoYygncHJvcG9ydGlvbic9J3Jvb3RfcHJvcG9ydGlvbicpKQoKYnJhbmNoX2RhdGFfYW5ub3RhdGVkIDwtIHBseXI6OmpvaW4oYnJhbmNoX2RhdGFfYW5ub3RhdGVkLCByb290X3Byb3BvcnRpb25zKQpicmFuY2hfZGF0YV9hbm5vdGF0ZWQkcHJvcG9ydGlvbl9kaWZmIDwtIHdpdGgoYnJhbmNoX2RhdGFfYW5ub3RhdGVkLCBwcm9wb3J0aW9uIC0gcm9vdF9wcm9wb3J0aW9uKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoYnJhbmNoX2RhdGFfYW5ub3RhdGVkICU+JSBzdWJzZXQobm9kZV90eXBlID09ICJkZXNjZW5kYW50IiAmIG4gPiA0MCksIGFlcyh4PXByb3BvcnRpb25fZGlmZiwgZmlsbD1zaWduYXR1cmUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTAuMDIsIGFscGhhPTAuNCwgcG9zaXRpb249J2lkZW50aXR5JykgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKQpgYGA=