library(ithi.utils)
load_base_libs()

library(ithi.meta)
library(ithi.clones)

Colour palettes

pal_patient <- select_palette("patient")

Parameters

db_path <- snakemake@params$db

igpartition_outdir <- snakemake@input$igpartition_outdir
# igpartition_outdir <-
# '~/shahlab/pipeline_outputs/ith_immune/igpartition/run22'
ihc_table_file <- snakemake@input$ihc_table
tiltypes <- unlist(snakemake@params$tiltypes)

ith_stats_file <- snakemake@input$ith_stats_file
proportion_subclonality_file <- snakemake@input$subclonality

Description

This document describes the correlative analysis conducted using measures summarized from BCR phylogenetic data.

All measures are computed on 100 evenly-spaced samples from the posterior trace for each MCMC run. 2 MCMC runs are conducted for each BCR clonal family. 15 clonal families (randomly selected based on a reasonable set of criteria, listed below) are selected from each patient. Each clonal family can contain BCRs from multiple tumour sites.

Clonal family selection

Selection criteria:

  • Estimated size of 300-500 BCRs (I say estimated, because I don’t know the exact size of a clonal family prior to the final, seeded clustering step.)

Size estimation is done by taking the clonal family size from subsampling and naive (vsearch) clustering, and multiplying it by the ratio of total library size (for all samples corresponding to the patient in question) to downsampled library size. Since subsampling is done in a stratified manner, this is correct.

Note that even though 15 clonal families were run per patient, several of them may have failed somewhere along the pipeline. This can be due to: poor estimation of family sizes (families larger than 1200 BCRs are not run for computation time issues, and empty families yield no results), cluster failures (will have to rerun a few because of this). As far as I’m aware, all easily-resolved checks/fixes have been implemented.

If we absolutely need 15 per patient, I suggest rerunning more families. This can be easily done but takes time (~ 1 week per complete run).

Definitions

For the analysis below, there will often be two sets of plots. These are named (open the code chunks to reveal):

  • Mugration: Full phylogeographic model
  • Nongeographic: Normal phylogenetic model with no geographic information

There are arguments to why using mugration wouldn’t be ‘fair’. Obviously, incorporating geography as a discrete trait naturally leads to clustering by geographic location – this might introduce biases into the genealogical sorting analysis below. Hence why I also do the same analysis for the nongeographic trees.

Data

ihc_table <- fread(ihc_table_file)
ihc_subset <- subset(ihc_table, select = c("condensed_id", "patient_id", tiltypes))
ihc_means <- ihc_subset %>% group_by(patient_id) %>% select(-c(condensed_id)) %>% 
    summarise_all(mean)

ith_stats <- read_ith_stats(ith_stats_file, db_path, duplicates = FALSE)
proportion_subclonality <- fread(proportion_subclonality_file)
mugration_outdir <- file.path(igpartition_outdir, "stats/all/phylogeography/bayesian_mugration_standard_nonclock/1-end/partitioned_GTR")
nongeographic_outdir <- file.path(igpartition_outdir, "stats/all/phylogenetics/bayesian_nongeographic/1-end/partitioned")
rootfreq_outdir <- file.path(igpartition_outdir, "plots/phylogeography/bayesian_mugration_standard_nonclock/1-end/partitioned_GTR")
mugration_statfiles <- list.files(mugration_outdir, pattern = ".tsv", recursive = TRUE, 
    full.names = TRUE)
nongeographic_statfiles <- list.files(nongeographic_outdir, pattern = ".tsv", 
    recursive = TRUE, full.names = TRUE)

rootfreq_files <- list.files(rootfreq_outdir, pattern = ".txt", recursive = TRUE, 
    full.names = TRUE)

Genealogical sorting analysis

read_gsi_stats <- function(f) {
    obskey <- "observed"
    dat <- fread(f)
    dat <- subset(dat, median_gsi != -1)
    
    obs <- subset(dat, permutation == obskey)
    bkg <- subset(dat, permutation != obskey)
    
    bkg_mean <- bkg %>% group_by(group) %>% summarise(gsi_m = median(median_gsi, 
        na.rm = TRUE), gsi_s = mad(median_gsi, na.rm = TRUE))
    empirical <- dat %>% group_by(group) %>% summarise(pval = 1 - length(which(median_gsi[which(permutation != 
        obskey)] < median_gsi[permutation == obskey]))/(n() - 1))
    ## Not really Gaussian but visually looks ~close
    dat_summary <- merge(obs, bkg_mean, by = c("group"))
    dat_summary$z_score <- with(dat_summary, (median_gsi - gsi_m)/gsi_s)
    
    res <- merge(dat_summary, empirical, by = c("group"))
    res <- subset(res, select = -c(permutation))
    res <- subset(res, gsi_s != 0)
    
    return(res)
}
get_gsi_table <- function(statfiles) {
    gsi_stats <- rbind.fill(lapply(statfiles[str_detect(statfiles, "gsi_values")], 
        function(f) {
            df <- read_gsi_stats(f)
            
            patient_clust_rep <- str_extract(f, "[0-9]+\\/clust[0-9]+/[0-9]+")
            strs <- strsplit(patient_clust_rep, "/")[[1]]
            patient <- strs[1]
            clust <- strs[2]
            rep <- strs[3]
            
            if (nrow(df) != 0) {
                df <- cbind(patient_id = patient, clust = clust, rep = rep, 
                  df)
                df$condensed_id <- with(df, paste(patient_id, group, sep = "_"))
            }
            return(df)
        }))
    return(gsi_stats)
}
mugration_gsi_stats <- get_gsi_table(mugration_statfiles)

nongeographic_gsi_stats <- get_gsi_table(nongeographic_statfiles)
summarize_gsi_stats <- function(gsi_stats) {
    gsi_stats_avg <- gsi_stats %>% group_by(patient_id, clust, group, condensed_id) %>% 
        select(-c(rep)) %>% summarise_all(mean)
    gsi_summary <- gsi_stats_avg %>% group_by(condensed_id, patient_id, group) %>% 
        summarise(medgsi = median(median_gsi, na.rm = TRUE), z = median(z_score, 
            na.rm = TRUE), psig = length(which(pval < 0.05))/length(which(median_gsi != 
            -1)), pval = median(pval[which(median_gsi != -1)], na.rm = TRUE), 
            num = length(which(median_gsi != -1)))
    
    gsi_summary <- subset(gsi_summary, !is.na(z))
    return(gsi_summary)
}

gsi_ihc_correlation <- function(gsi_summary, gsi_measure = "medgsi", ihc, tiltypes) {
    gsi_ihc <- merge(gsi_summary, ihc, by = c("condensed_id", "patient_id"))
    gsi_ihc_melted <- melt(gsi_ihc, id.vars = c("condensed_id", "patient_id", 
        "medgsi", "z", "psig", "pval", "num"), measure.vars = tiltypes, variable.name = "tiltype", 
        value.name = "density")
    
    ## Filter for number of observations
    gsi_ihc_melted_filtered <- subset(gsi_ihc_melted, num >= 3)
    
    pvals <- setNames(ddply(gsi_ihc_melted_filtered, .(tiltype), function(x) {
        df <- as.data.frame(x)
        corres <- cor.test(df[, "density"], df[, gsi_measure], method = "spearman")
        
        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"))
    
    p <- ggplot(gsi_ihc_melted_filtered, aes_string(x = "density", y = gsi_measure)) + 
        geom_point(aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
        facet_wrap(~tiltype, scales = "free") + scale_color_manual(values = pal_patient) + 
        geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
            vjust = 1.5, size = 3, parse = TRUE)
    return(p)
}

gsi_ith_correlation <- function(gsi_summary, gsi_measure = "medgsi", ith, ith_measures) {
    gsi_ith <- merge(gsi_summary, ith, by = c("condensed_id", "patient_id"))
    gsi_ith_melted <- melt(gsi_ith, id.vars = c("condensed_id", "patient_id", 
        "medgsi", "z", "psig", "pval", "num"), measure.vars = ith_measures, 
        variable.name = "ithtype", value.name = "ith")
    
    ## Filter for number of observations
    gsi_ith_melted_filtered <- subset(gsi_ith_melted, num >= 3)
    
    pvals <- setNames(ddply(gsi_ith_melted_filtered, .(ithtype), function(x) {
        df <- as.data.frame(x)
        corres <- cor.test(df[, "ith"], df[, gsi_measure], method = "spearman")
        
        pval <- corres$p.value
        eq <- substitute(italic(P) == p, list(p = format(pval, digits = 3)))
        return(as.character(as.expression(eq)))
    }), c("ithtype", "p.value"))
    
    p <- ggplot(gsi_ith_melted_filtered, aes_string(x = "ith", y = gsi_measure)) + 
        geom_point(aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
        facet_wrap(~ithtype, scales = "free") + scale_color_manual(values = pal_patient) + 
        geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
            vjust = 1.5, size = 3, parse = TRUE)
    return(p)
}
mugration_gsi_summary <- summarize_gsi_stats(mugration_gsi_stats)
nongeographic_gsi_summary <- summarize_gsi_stats(nongeographic_gsi_stats)

GSI-IHC analysis

The results below show that samples with higher TIL densities have lower GSIs. Lower GSIs = less geographic exclusivity in phylogenies, i.e. less phylogenetic signal in geography, i.e. more mixing.

p <- gsi_ihc_correlation(mugration_gsi_summary, "medgsi", ihc_subset, tiltypes)
plot(p)

p <- gsi_ihc_correlation(nongeographic_gsi_summary, "medgsi", ihc_subset, tiltypes)
plot(p)

Note that this result is not significant when stratifying by patient (i.e. modeling patient as a random effect).

GSI-ITH analysis

As with all other analyses incorporating ITH this analysis is currently flawed as the ITH statistics are off.

p <- gsi_ith_correlation(mugration_gsi_summary, "medgsi", ith_stats, c("entropy", 
    "divergence"))
plot(p)

p <- gsi_ith_correlation(nongeographic_gsi_summary, "medgsi", ith_stats, c("entropy", 
    "divergence"))
plot(p)

However, to inspire some potentially misplaced hope, here’s what the analysis looks like using the naive measure ‘proportion subclonality’, which correlated very well with the old ITH measures:

proportion_subclonality <- subset(proportion_subclonality, patient_id != 15)

p <- gsi_ith_correlation(mugration_gsi_summary, "medgsi", proportion_subclonality, 
    c("proportion_subclonal", "mix"))
plot(p)

p <- gsi_ith_correlation(nongeographic_gsi_summary, "medgsi", proportion_subclonality, 
    c("proportion_subclonal", "mix"))
plot(p)

Note that patient 15 was deliberately excluded because it was the sample for which CN calls were bad (until Andrew reruns the proportion subclonality calculation I shouldn’t include it).

If this finding holds up, it would imply that more intratumoural heterogeneity = more BCR mixing within clonal families. In other words, ITH <=> heterogeneity within BCR families! Pretty cool! Beyond that, I’m not sure how to biologically interpret this.

Phylogenetic signal in readcounts

read_phylosignal_file <- function(f) {
    dat <- fread(f)
    cbind(var = c("mut_freqs", "readcount"), dat)
}

get_phylosignal_table <- function(statfiles) {
    phylosignal_stats <- rbind.fill(lapply(statfiles[str_detect(statfiles, "phylosignal")], 
        function(f) {
            df <- read_phylosignal_file(f)
            
            patient_clust_rep <- str_extract(f, "[0-9]+\\/clust[0-9]+/[0-9]+")
            strs <- strsplit(patient_clust_rep, "/")[[1]]
            patient <- strs[1]
            clust <- strs[2]
            rep <- strs[3]
            
            if (nrow(df) != 0) {
                df <- cbind(patient_id = patient, clust = clust, rep = rep, 
                  df)
            }
            return(df)
        }))
    return(phylosignal_stats)
}

summarize_phylosignal_stats <- function(phylosignal_stats) {
    phylosignal_stats_avg <- phylosignal_stats %>% group_by(patient_id, clust, 
        var) %>% select(-c(rep)) %>% summarise_all(mean)
    phylosignal_summary <- phylosignal_stats_avg %>% group_by(patient_id, var) %>% 
        summarise(Cmean = median(stat.Cmean, na.rm = TRUE), Cmean_p = median(pvalue.Cmean, 
            na.rm = TRUE), I = median(stat.I, na.rm = TRUE), I_p = median(pvalue.I, 
            na.rm = TRUE), num = n())
    
    return(phylosignal_summary)
}

phylosignal_ihc_correlation <- function(phylosignal_summary, phylosignal_measure = "Cmean", 
    ihc, tiltypes, variable = "readcount") {
    phylosignal_summary <- subset(phylosignal_summary, var == variable)
    phylosignal_ihc <- merge(phylosignal_summary, ihc, by = c("patient_id"))
    phylosignal_ihc_melted <- melt(phylosignal_ihc, id.vars = c("patient_id", 
        "Cmean", "Cmean_p", "I", "I_p", "num"), measure.vars = tiltypes, variable.name = "tiltype", 
        value.name = "density")
    
    ## Filter for number of observations
    phylosignal_ihc_melted_filtered <- subset(phylosignal_ihc_melted, num >= 
        3)
    
    pvals <- setNames(ddply(phylosignal_ihc_melted_filtered, .(tiltype), function(x) {
        df <- as.data.frame(x)
        corres <- cor.test(df[, "density"], df[, phylosignal_measure], method = "spearman")
        
        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"))
    
    p <- ggplot(phylosignal_ihc_melted_filtered, aes_string(x = "density", y = phylosignal_measure)) + 
        geom_point(aes(colour = patient_id)) + theme_bw() + theme_Publication() + 
        facet_wrap(~tiltype, scales = "free") + scale_color_manual(values = pal_patient) + 
        geom_text(data = pvals, aes(x = Inf, y = Inf, label = p.value), hjust = 1.1, 
            vjust = 1.5, size = 3, parse = TRUE)
    return(p)
}
mugration_phylosignal_stats <- get_phylosignal_table(mugration_statfiles)
nongeographic_phylosignal_stats <- get_phylosignal_table(nongeographic_statfiles)
mugration_phylosignal_summary <- summarize_phylosignal_stats(mugration_phylosignal_stats)
nongeographic_phylosignal_summary <- summarize_phylosignal_stats(nongeographic_phylosignal_stats)

Phylosignal-IHC analysis

p <- phylosignal_ihc_correlation(mugration_phylosignal_summary, "Cmean", ihc_means, 
    tiltypes, variable = "readcount")
plot(p)

p <- phylosignal_ihc_correlation(mugration_phylosignal_summary, "I", ihc_means, 
    tiltypes, variable = "readcount")
plot(p)

p <- phylosignal_ihc_correlation(mugration_phylosignal_summary, "Cmean", ihc_means, 
    tiltypes, variable = "mut_freqs")
plot(p)

p <- phylosignal_ihc_correlation(nongeographic_phylosignal_summary, "Cmean", 
    ihc_means, tiltypes, variable = "readcount")
plot(p)

p <- phylosignal_ihc_correlation(nongeographic_phylosignal_summary, "I", ihc_means, 
    tiltypes, variable = "readcount")
plot(p)

p <- phylosignal_ihc_correlation(nongeographic_phylosignal_summary, "Cmean", 
    ihc_means, tiltypes, variable = "mut_freqs")
plot(p)

So phylogenetic signal in readcounts correlates with CD20+ and CD4+ TIL densities. This is potentially evidence of selection occurring (clade-specific expansion) in samples with higher TIL densities, a finding that we might naively expect.

Note that I ran a few plot sets using the variable mut_freqs, which just refers to mutation frequency. We don’t expect phylogenetic signal for this parameter to correlate with any biological parameters, which is what we observe.

Phylosignal-ITH analysis

TODO.

Origin analysis

Description

Origin analysis = root frequency analysis. The goal of this analysis is to infer the geographical origin of each B-cell clonal family, equivalent to ancestral state analysis at the root node. These results can then be correlated with other known covariates.

Questions

Some really simple questions to ask include:

  • Do clonal families from the same patient consistently arise from the same geographic origin?
  • Is geographic origin restricted to certain anatomic sites?
  • Are geographic origin/migration patterns correlated with tumour clone migration patterns?

In the previous version of this analysis, I observed a pattern where, in high-immune patients, geographic origin tended to be consistent across clonal families. The same trend was not present for low-immune clusters. A hypothetical biological explanation for this was that, in high-immune patients, B-cell clonal families respond to the same/similar antigenic threats, and therefore arose from the same site where those antigens were initially presented. Whereas, in low-immune patients, it was possible that B-cell clonal families were not tumour-reactive and were instead ‘sentinels’ patrolling each tumour site.

We check if this pattern holds up.

Results

read_root_frequencies <- function(f, clust, rep) {
    freqs <- fread(f)
    cbind(clust = clust, rep = rep, freqs)
}

get_rf_table <- function(files) {
    patient_clust_rep <- str_extract(files, "[0-9]+\\/clust[0-9]+/[0-9]+")
    strs <- strsplit(patient_clust_rep, "/")
    patient <- sapply(strs, function(x) x[1])
    clust <- sapply(strs, function(x) x[2])
    rep <- sapply(strs, function(x) x[3])
    
    info <- data.frame(patient_id = patient, clust = clust, rep = rep, tablefile = files, 
        stringsAsFactors = FALSE)
    
    freq_tables <- info %>% group_by(patient_id) %>% do(rf = rbind.fill(lapply(seq_along(.$tablefile), 
        function(i) read_root_frequencies(.$tablefile[i], .$clust[i], .$rep[i]))))
    
    freq_tables$rf <- lapply(freq_tables$rf, function(x) {
        roworder <- order(as.numeric(str_extract(x$clust, "[0-9]+")), x$rep)
        x <- x[roworder, ]
    })
    freq_tables <- freq_tables[order(as.numeric(freq_tables$patient_id)), ]
    return(freq_tables)
}

plot_rf_table <- function(rf, annotation_cols = c("clust", "rep"), title) {
    dat <- as.data.frame(rf)[, (!colnames(rf) %in% annotation_cols)]
    
    annotation_row <- subset(rf, select = c(annotation_cols))
    annotation_colours <- list(clust = get_colour_palette(annotation_row$clust), 
        rep = c(`0` = "#000000", `1` = "#FFFFFF"))
    
    pheatmap(dat, cluster_rows = FALSE, cluster_cols = FALSE, annotation_row = annotation_row, 
        annotation_colors = annotation_colours, show_rownames = FALSE, main = title)
}
rf_table <- get_rf_table(rootfreq_files)
ignore <- lapply(1:nrow(rf_table), function(i) {
    patient_id <- rf_table$patient_id[i]
    plot_title <- paste("Patient", patient_id, sep = " ")
    plot_rf_table(rf_table$rf[[i]], title = plot_title)
})

It’s pretty obvious from these plots that this pattern doesn’t hold up anymore. The only patient for which we see consistent anatomic origin is patient 2, where nearly all clonal families arise from the right ovary. The results for patient 2 corroborate our previous analysis, but no other patients do. Patient 1 and 22, which are two immune-high patients that previously followed that trend, no longer do. Likewise, patient 15 is a new (ITH-3) patient that is extremely immune-high and does not follow the trend. Therefore our hypothesis is not supported by the new data.

LS0tCnRpdGxlOiAiQkNSIFBoeWxvZ2VuZXRpY3M6IENvcnJlbGF0aW9ucyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogdHJ1ZQpwYXJhbXM6CiAgcm1kOiAiYmNyX3BoeWxvX2NvcnJlbGF0aW9ucy5SbWQiCi0tLQogICAgICAgICAgICAgICAgICAgICAgICBgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMjIyMjIyMjIFNuYWtlbWFrZSBoZWFkZXIgIyMjIyMjIyMKbGlicmFyeShtZXRob2RzKQpTbmFrZW1ha2UgPC0gc2V0Q2xhc3MoCiAgICAiU25ha2VtYWtlIiwKICAgIHNsb3RzID0gYygKICAgICAgICBpbnB1dCA9ICJsaXN0IiwKICAgICAgICBvdXRwdXQgPSAibGlzdCIsCiAgICAgICAgcGFyYW1zID0gImxpc3QiLAogICAgICAgIHdpbGRjYXJkcyA9ICJsaXN0IiwKICAgICAgICB0aHJlYWRzID0gIm51bWVyaWMiLAogICAgICAgIGxvZyA9ICJsaXN0IiwKICAgICAgICByZXNvdXJjZXMgPSAibGlzdCIsCiAgICAgICAgY29uZmlnID0gImxpc3QiLAogICAgICAgIHJ1bGUgPSAiY2hhcmFjdGVyIgogICAgKQopCnNuYWtlbWFrZSA8LSBTbmFrZW1ha2UoCiAgICBpbnB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9vbGRfcHJvcG9ydGlvbl9zdWJjbG9uYWwudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9jbG9uYWxfbWVhc3VyZXMudHN2JywgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yL2loY190YWJsZS50c3YnLCAnUm1kL2Jjcl9waHlsb19jb3JyZWxhdGlvbnMuUm1kJywgJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL2lncGFydGl0aW9uL3J1bjIyJywgInN1YmNsb25hbGl0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaXRoL2NvbXBsZXRlL29sZF9wcm9wb3J0aW9uX3N1YmNsb25hbC50c3YnLCAiaXRoX3N0YXRzX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9jbG9uYWxfbWVhc3VyZXMudHN2JywgImloY190YWJsZSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3BhcGVyL3Jlc3VsdHMvdGFibGVzL3J1bjIvaWhjX3RhYmxlLnRzdicsICJub3RlYm9vayIgPSAnUm1kL2Jjcl9waHlsb19jb3JyZWxhdGlvbnMuUm1kJywgImlncGFydGl0aW9uX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvaWdwYXJ0aXRpb24vcnVuMjInKSwKICAgIG91dHB1dCA9IGxpc3QoJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYi9iY3JfcGh5bG9fY29ycmVsYXRpb25zLm5iLmh0bWwnKSwKICAgIHBhcmFtcyA9IGxpc3QoYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgJ2l0aGktYW5hbHlzaXMtYmNycGh5bG8tY29ycmVsYXRpb25zJywgInRpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JywgJ1RfQ0Q4X2RlbnNpdHknLCAnVF9DRDRfZGVuc2l0eScsICdUX0NEMjBfZGVuc2l0eScsICdUX1BsYXNtYV9kZW5zaXR5JyksICJkYiIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL21ldGFkYXRhL2RiL2ltbXVuZV9wcm9qZWN0LnNxbGl0ZTMnLCAibmFtZSIgPSAnaXRoaS1hbmFseXNpcy1iY3JwaHlsby1jb3JyZWxhdGlvbnMnKSwKICAgIHdpbGRjYXJkcyA9IGxpc3QoKSwKICAgIHRocmVhZHMgPSAxLAogICAgbG9nID0gbGlzdCgnL3NoYWhsYWIvYWx6aGFuZy9jbHVzdHRtcC9wYXBlcmFuYWx5c2lzMi9iY3JwaHlsb19jb3JyZWxhdGlvbnMubG9nJyksCiAgICByZXNvdXJjZXMgPSBsaXN0KCksCiAgICBjb25maWcgPSBsaXN0KCJub3RlYm9va19kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3dlYicsICJ4Y3JfY2xvbmVzX25vdGVib29rIiA9ICdSbWQveGNyX2Nsb25lc19hbmFseXNpcy5SbWQnLCAiYmNycGh5bG9fdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgInRpbHNfZm9yX2NsdXN0ZXIiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknKSwgImloY194Y3Jfc3RhdHNfbm90ZWJvb2siID0gJ1JtZC9paGNfeGNyX3N0YXRzLlJtZCcsICJpbmRleF9ub3RlYm9vayIgPSAnUm1kL2luZGV4LlJtZCcsICJ2X2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJWLmZhc3RhJywgImlncGFydGl0aW9uX291dGRpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvaWdwYXJ0aXRpb24vcnVuMjInLCAiaWNnY19ub3JtYWxpemVkX3JlYWRzX21hdHJpeCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvT1ZBVV9leHByX21hdHJpeC50c3YnLCAiZHJpdmVyX21hcCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3N1YnByb2plY3RzL2RyaXZlcnMvZGF0YS9nZW5lX2xpc3RfbWFwcGVkLmJlZCcsICJsaWJyYXJ5X3NpemVzIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvbGlicmFyeV9zaXplcy50c3YnLCAibmVvZWRpdGluZ19vdXRkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL25lb2VkaXRpbmcvcnVuMycsICJtYXN0ZXJfdmFyaWFudF9maWxlIiA9ICcvc2hhaGxhYi9hbWNwaGVyc29uL3Byb2plY3RzL2l0aDMvaXRoMy9ub3RlYm9va3MvYmVzcG9rZS9pdGhfc252cy50c3YnLCAibW1jdG1fcGF0aWVudF9hbmNlc3RyYWxfZGVzY2VuZGFudF9yZXN1bHRfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcmVzdWx0cy9tbWN0bV9yZXN1bHRzL2l0aF9ieS1wYXRpZW50LWFuY2VzdHJ5L291dHB1dCcsICJ4Y3JfbWFwcGluZ19ub3RlYm9vayIgPSAnUm1kL3hjcl9tYXBwaW5nLlJtZCcsICJzaXRlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9hbmFseXNpcy9SbWQvX3NpdGUueW1sJywgInRpbHNfZm9yX3ZhcmlhYmlsaXR5IiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJwaGVub3R5cGVfdGhyZXNob2xkIiA9IDAuODUsICJkcml2ZXJfYW5hbHlzaXNfbm90ZWJvb2siID0gJ1JtZC9kcml2ZXJfYW5hbHlzaXMuUm1kJywgImJjcnBoeWxvX2V4YW1wbGVzX25vdGVib29rIiA9ICdSbWQvYmNyX3BoeWxvX2V4YW1wbGVzLlJtZCcsICJsb2dkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvY2x1c3R0bXAvcGFwZXJhbmFseXNpczInLCAia25vd25fc3VidHlwZXNfbWVyZ2VkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL2tub3duX3N1YnR5cGVzX21lcmdlZC50c3YnLCAiYmNyX2Nsb25vdHlwZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9jbG9ub3R5cGVzL0lHSF9jbG9ub3R5cGVzX2ZpbHRlcmVkLnR4dCcsICJiZW5jaG1hcmtkaXIiID0gJy9zaGFobGFiL2FsemhhbmcvYmVuY2htYXJrcy9wYXBlcmFuYWx5c2lzMicsICJtb2xzdWJ0eXBlX3RpbHR5cGVzIiA9IGMoJ0VfQ0Q4X2RlbnNpdHknLCAnRV9DRDRfZGVuc2l0eScsICdFX0NEMjBfZGVuc2l0eScsICdFX1BsYXNtYV9kZW5zaXR5JywgJ1NfQ0Q4X2RlbnNpdHknLCAnU19DRDRfZGVuc2l0eScsICdTX0NEMjBfZGVuc2l0eScsICdTX1BsYXNtYV9kZW5zaXR5JyksICJtbWN0bV9wYXRpZW50X2FkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXBhdGllbnQtYW5jZXN0cnkvcGxvdHMvaXRoLWJ5LXBhdGllbnQtYW5jZXN0cnlfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAiaXRoX3RpbF9ub3RlYm9vayIgPSAnUm1kL2l0aF90aWxfZGVuc2l0aWVzLlJtZCcsICJ0Y3JfZGl2ZXJzaXR5IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9taXhjci9taXhjcl9ydW5zL2l0aF8xXzJfMy9taXhjcjUvcG9zdHByb2Nlc3MvVFJCL3Bvc3RmaWx0ZXJfZGl2ZXJzaXR5X3N0YXRzL2RpdmVyc2l0eS5zdHJpY3QucmVzYW1wbGVkLnR4dCcsICJ2YXJpYWJpbGl0eV90eXBlIiA9ICdzdGFiaWxpemUnLCAia25vd25fc3VidHlwZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9leHByZXNzaW9uL2FycmF5L3N1YnR5cGVzL2tub3duX3N1YnR5cGVzLnRzdicsICJpY2djX3NwZWNpbWVuX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9JQ0dDL3NwZWNpbWVuLnRzdicsICJ0YWJsZV9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9wYXBlci9yZXN1bHRzL3RhYmxlcy9ydW4yJywgIm11dHNpZ190aWx0eXBlcyIgPSBjKCdFX0NEOF9kZW5zaXR5JywgJ0VfQ0Q0X2RlbnNpdHknLCAnRV9DRDIwX2RlbnNpdHknLCAnRV9QbGFzbWFfZGVuc2l0eScsICdTX0NEOF9kZW5zaXR5JywgJ1NfQ0Q0X2RlbnNpdHknLCAnU19DRDIwX2RlbnNpdHknLCAnU19QbGFzbWFfZGVuc2l0eScsICdUX0NEOF9kZW5zaXR5JywgJ1RfQ0Q0X2RlbnNpdHknLCAnVF9DRDIwX2RlbnNpdHknLCAnVF9QbGFzbWFfZGVuc2l0eScpLCAibW1jdG1fc2FtcGxlX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LXNhbXBsZS9vdXRwdXQnLCAiZGIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9tZXRhZGF0YS9kYi9pbW11bmVfcHJvamVjdC5zcWxpdGUzJywgImloY194Y3JfdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknLCAnVF9DRDhfZGVuc2l0eScsICdUX0NENF9kZW5zaXR5JywgJ1RfQ0QyMF9kZW5zaXR5JywgJ1RfUGxhc21hX2RlbnNpdHknKSwgImltbXR5cGVyX21vZGVscyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvaW1tdHlwZXJfcmVzdWx0cy9rbGFyZW5iZWVrL2FhX3ZqL2dyYWRib29zdCcsICJwcm9wb3J0aW9uX3N1YmNsb25hbF9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvb2xkX3Byb3BvcnRpb25fc3ViY2xvbmFsLnRzdicsICJwcmV2YWxlbmNlX3RocmVzaG9sZCIgPSAwLjAxLCAidGlsX2NsYXNzaWZpZXJfbm90ZWJvb2siID0gJ1JtZC90aWxfY2xhc3NpZmllci5SbWQnLCAieGNyX2Rpc3RhbmNlX21ldGhvZCIgPSAnaG9ybicsICJpbW11bmVfdmFyaWFiaWxpdHlfbm90ZWJvb2siID0gJ1JtZC9pbW11bmVfdmFyaWFiaWxpdHkuUm1kJywgInNhZF9ub3RlYm9vayIgPSAnUm1kL3NwZWNpZXNfYWJ1bmRhbmNlX2Rpc3RyaWJ1dGlvbnMuUm1kJywgIm1hc3Rlcl9icmVha3BvaW50X2ZpbGUiID0gJy9zaGFobGFiL2FtY3BoZXJzb24vcHJvamVjdHMvaXRoMy9pdGgzL25vdGVib29rcy9iZXNwb2tlL2l0aF9icmVha3BvaW50cy50c3YnLCAidGNyX2Nsb25vdHlwZXMiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL21peGNyL21peGNyX3J1bnMvaXRoXzFfMl8zL21peGNyNS9jbG9ub3R5cGVzL1RSQl9jbG9ub3R5cGVzX2ZpbHRlcmVkLnR4dCcsICJpY2djX2V4cHJfbWVsdGVkIiA9ICcvc2hhaGxhYi9hbHpoYW5nL2RhdGEvSUNHQy9PVkFVX2V4cHJfbWVsdGVkLnRzdicsICJjbG9uZV90cmVlX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS90cmVlX2RhdGEudHN2JywgIm12Y2x1c3RfdGlsdHlwZXMiID0gYygnRV9DRDhfZGVuc2l0eScsICdFX0NENF9kZW5zaXR5JywgJ0VfQ0QyMF9kZW5zaXR5JywgJ0VfUGxhc21hX2RlbnNpdHknLCAnU19DRDhfZGVuc2l0eScsICdTX0NENF9kZW5zaXR5JywgJ1NfQ0QyMF9kZW5zaXR5JywgJ1NfUGxhc21hX2RlbnNpdHknKSwgInNwYXRpYWxfbm90ZWJvb2siID0gJ1JtZC9zcGF0aWFsX2FuYWx5c2lzLlJtZCcsICJpdGhfc3RhdGlzdGljc19ub3RlYm9vayIgPSAnUm1kL2l0aF9zdGF0aXN0aWNzLlJtZCcsICJpbW10eXBlcl9sZW5ndGhzIiA9ICcxMSAxMiAxMyAxNCAxNSAxNiAxNyAxOCcsICJpbnRlcm1lZGlhdGVfZGlyIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvcGFwZXIvcmVzdWx0cy9pbnRlcm1lZGlhdGVzL3J1bjInLCAiYmNycGh5bG9fY29ycmVsYXRpb25zX25vdGVib29rIiA9ICdSbWQvYmNyX3BoeWxvX2NvcnJlbGF0aW9ucy5SbWQnLCAibW1jdG1fc2FtcGxlX2FkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvaXRoX2J5LWFuY2VzdHJ5LXNhbXBsZS9wbG90cy9pdGgtYnktYW5jZXN0cmFsLXNhbXBsZV9zbnYtc3Zfc2lnc19tdWx0aXBhbmVsLnBkZicsICJqX2RpY3Rpb25hcnkiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9zdWJwcm9qZWN0cy9pbW10eXBlci9tZXRhZGF0YS9pbWd0L0hvbW9fc2FwaWVuc19UUkJKLmZhc3RhJywgIml0aF9zdGF0c19maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmFsX21lYXN1cmVzLnRzdicsICJ4Y3JfcWNfbm90ZWJvb2siID0gJ1JtZC9yZXBsaWNhdGVzLlJtZCcsICJuZW9hbnRpZ2VuX2VkaXRpbmdfbm90ZWJvb2siID0gJ1JtZC9pbW11bm9lZGl0aW5nLlJtZCcsICJleGFtcGxlX21zYV9wbG90IiA9ICcvc2hhaGxhYi9hbHpoYW5nL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4xMy9vbGQvYWxpZ25tZW50X3Bsb3RzL21zYS9pdGgyXzIvY2x1c3Q5L2luZGVsX3JldmVyc2VkLmh0bWwnLCAiY2xhc3NpZmllcl90eXBlIiA9ICdrbm4nLCAiY2xvbmVfcHJldmFsZW5jZV9maWxlIiA9ICcvc2hhaGxhYi9hbHpoYW5nL3Byb2plY3RzL0lUSF9JbW11bmUvZGF0YS9pdGgvY29tcGxldGUvY2xvbmVfZGF0YS50c3YnLCAiaWhjX3J1bjIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2loYy9jZDc5Y2QxMzhjZDY4L3ZhbGlkYXRlZF9zdGF0c193ZWlnaHRlZC5yZGF0YScsICJjbG9uZV9icmFuY2hfbGVuZ3RoX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2l0aC9jb21wbGV0ZS9icmFuY2hfZGF0YS50c3YnLCAiYmNyX2RpdmVyc2l0eSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvbWl4Y3IvbWl4Y3JfcnVucy9pdGhfMV8yXzMvbWl4Y3I1L3Bvc3Rwcm9jZXNzL0lHSC9wb3N0ZmlsdGVyX2RpdmVyc2l0eV9zdGF0cy9kaXZlcnNpdHkuc3RyaWN0LnJlc2FtcGxlZC50eHQnLCAibmFub3N0cmluZ19zaWduYXR1cmVfbm90ZWJvb2siID0gJ1JtZC9uYW5vc3RyaW5nX3NpZ25hdHVyZXMuUm1kJywgIm1tY3RtX292X2NvbWJpbmVkX3NpZ3Bsb3QiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vcGxvdHMvb3Zfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAidGNnYV9vdl9hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL1RDR0EvdGNnYV9vdl9hbm5vdGF0aW9uX3N1cDEzLnR4dCcsICJpaGNfcnVuMSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL2RhdGEvaWhjL2NkOGNkM2NkMjAvdmFsaWRhdGVkX3N0YXRzX3dlaWdodGVkLnJkYXRhJywgIm11bHRpdmlld2NsdXN0ZXJpbmdfbm90ZWJvb2siID0gJ1JtZC9tdWx0aXZpZXdjbHVzdGVyaW5nLlJtZCcsICJtbWN0bV9zYW1wbGVfc2lncGxvdCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktc2FtcGxlL3Bsb3RzL2l0aC1ieS1zYW1wbGVfc252LXN2X3NpZ3NfbXVsdGlwYW5lbC5wZGYnLCAibW1jdG1fYW5jZXN0cmFsX2Rlc2NlbmRhbnRfcmVzdWx0X2RpciIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbW1jdG1fcmVzdWx0cy9pdGhfYnktYW5jZXN0cnktc2FtcGxlL291dHB1dCcsICJzcGF0aWFsX3Jlc3VsdF9kaXJzIiA9IGxpc3QoImVwaXRoZWxpYWwiID0gJy9zaGFobGFiL2FsemhhbmcvcGlwZWxpbmVfb3V0cHV0cy9pdGhfaW1tdW5lL3NwYXRzaW0vaXRoMy9hYmMnLCAic3Ryb21hbCIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvc3BhdHNpbS9pdGg1L2FiYycpLCAibW9sc3VidHlwZV9ub3RlYm9vayIgPSAnUm1kL21vbGVjdWxhcl9zdWJ0eXBlcy5SbWQnLCAiZXhhbXBsZV9hbm5vdGF0aW9ucyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9waXBlbGluZV9vdXRwdXRzL2l0aF9pbW11bmUvaWdwYXJ0aXRpb24vcnVuMTMvZmluYWxfcGFydGl0aW9ucy9pdGgyXzIvY2x1c3Q5L2Fubm90YXRpb25zX2ZsYWdnZWQudHN2JywgInN1YnR5cGVfbWFya2VyX2ZpbGUiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9zdWJ0eXBlX21hcmtlcnMudHN2JywgIm5hbm9zdHJpbmdfYW5ub3RhdGlvbnMiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9kYXRhL2V4cHJlc3Npb24vbmFub3N0cmluZy9wYW5jYW5jZXJfYW5ub3RhdGlvbnMudHN2JywgIm1tY3RtX292X2NvbWJpbmVkX3Jlc3VsdF9kaXIiID0gJy9zaGFobGFiL2FsemhhbmcvcHJvamVjdHMvSVRIX0ltbXVuZS9yZXN1bHRzL21tY3RtX3Jlc3VsdHMvY29tYmluZWRfb3ZfbW1jdG0vb3V0cHV0JywgInRjZ2FfZXhwcl9tYXRyaXgiID0gJy9zaGFobGFiL2FsemhhbmcvZGF0YS9UQ0dBL2V4cHJfbWF0cml4X25vcm1hbGl6ZV9zdGFuZGFyZGl6ZV9ub2R1cGxpY2F0ZXMudHN2JywgIm5jbHVzdHMiID0gMiwgIm11dGF0aW9uX3NpZ25hdHVyZV9ub3RlYm9vayIgPSAnUm1kL211dGF0aW9uX3NpZ25hdHVyZXMuUm1kJywgIm5hbm9zdHJpbmdfZGF0YSIgPSAnL3NoYWhsYWIvYWx6aGFuZy9wcm9qZWN0cy9JVEhfSW1tdW5lL3Jlc3VsdHMvbmFub3N0cmluZ19yZXN1bHRzL2l0aF9mdWxsL3FjL2xpbW1hX3F1YW50aWxlL25vcm1hbGl6ZWRfZXhwcmVzc2lvbl92b2FfbGFiZWxzX2ZpbHRlcmVkLnRzdicsICJpY2djX21vbGVjdWxhcl9zdWJ0eXBlcyIgPSAnL3NoYWhsYWIvYWx6aGFuZy9kYXRhL0lDR0MvaWNnY19wcmltYXJ5X3R1bW91cl9zdWJ0eXBlcy50c3YnLCAibXZjbHVzdF9uY2x1c3QiID0gMyksCiAgICBydWxlID0gJ2Jjcl9waHlsb19jb3JyZWxhdGlvbnMnCikKIyMjIyMjIyMgT3JpZ2luYWwgc2NyaXB0ICMjIyMjIyMjIwoKICAgICAgICAgICAgICAgICAgICAgICAgYGBgCgoKYGBge3IgZ2xvYmFsX2NodW5rX29wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgdGlkeT1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFKQpgYGAKCgpgYGB7cn0KbGlicmFyeShpdGhpLnV0aWxzKQpsb2FkX2Jhc2VfbGlicygpCgpsaWJyYXJ5KGl0aGkubWV0YSkKbGlicmFyeShpdGhpLmNsb25lcykKYGBgCgojIyBDb2xvdXIgcGFsZXR0ZXMKCmBgYHtyfQpwYWxfcGF0aWVudCA8LSBzZWxlY3RfcGFsZXR0ZSgicGF0aWVudCIpCmBgYAoKIyMgUGFyYW1ldGVycwoKYGBge3J9CmRiX3BhdGggPC0gc25ha2VtYWtlQHBhcmFtcyRkYgoKaWdwYXJ0aXRpb25fb3V0ZGlyIDwtIHNuYWtlbWFrZUBpbnB1dCRpZ3BhcnRpdGlvbl9vdXRkaXIKI2lncGFydGl0aW9uX291dGRpciA8LSAifi9zaGFobGFiL3BpcGVsaW5lX291dHB1dHMvaXRoX2ltbXVuZS9pZ3BhcnRpdGlvbi9ydW4yMiIKaWhjX3RhYmxlX2ZpbGUgPC0gc25ha2VtYWtlQGlucHV0JGloY190YWJsZQp0aWx0eXBlcyA8LSB1bmxpc3Qoc25ha2VtYWtlQHBhcmFtcyR0aWx0eXBlcykKCml0aF9zdGF0c19maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRpdGhfc3RhdHNfZmlsZQpwcm9wb3J0aW9uX3N1YmNsb25hbGl0eV9maWxlIDwtIHNuYWtlbWFrZUBpbnB1dCRzdWJjbG9uYWxpdHkKYGBgCgojIyBEZXNjcmlwdGlvbgoKVGhpcyBkb2N1bWVudCBkZXNjcmliZXMgdGhlIGNvcnJlbGF0aXZlIGFuYWx5c2lzIGNvbmR1Y3RlZCB1c2luZyBtZWFzdXJlcyBzdW1tYXJpemVkIGZyb20gQkNSIHBoeWxvZ2VuZXRpYyBkYXRhLiAKCkFsbCBtZWFzdXJlcyBhcmUgY29tcHV0ZWQgb24gMTAwIGV2ZW5seS1zcGFjZWQgc2FtcGxlcyBmcm9tIHRoZSBwb3N0ZXJpb3IgdHJhY2UgZm9yIGVhY2ggTUNNQyBydW4uIDIgTUNNQyBydW5zIGFyZSBjb25kdWN0ZWQgZm9yIGVhY2ggQkNSIGNsb25hbCBmYW1pbHkuIDE1IGNsb25hbCBmYW1pbGllcyAocmFuZG9tbHkgc2VsZWN0ZWQgYmFzZWQgb24gYSByZWFzb25hYmxlIHNldCBvZiBjcml0ZXJpYSwgbGlzdGVkIGJlbG93KSBhcmUgc2VsZWN0ZWQgZnJvbSBlYWNoIHBhdGllbnQuIEVhY2ggY2xvbmFsIGZhbWlseSBjYW4gY29udGFpbiBCQ1JzIGZyb20gbXVsdGlwbGUgdHVtb3VyIHNpdGVzLgoKIyMgQ2xvbmFsIGZhbWlseSBzZWxlY3Rpb24KClNlbGVjdGlvbiBjcml0ZXJpYToKCiogRXN0aW1hdGVkIHNpemUgb2YgMzAwLTUwMCBCQ1JzIChJIHNheSBlc3RpbWF0ZWQsIGJlY2F1c2UgSSBkb24ndCBrbm93IHRoZSBleGFjdCBzaXplIG9mIGEgY2xvbmFsIGZhbWlseSBwcmlvciB0byB0aGUgZmluYWwsIHNlZWRlZCBjbHVzdGVyaW5nIHN0ZXAuKQoKU2l6ZSBlc3RpbWF0aW9uIGlzIGRvbmUgYnkgdGFraW5nIHRoZSBjbG9uYWwgZmFtaWx5IHNpemUgZnJvbSBzdWJzYW1wbGluZyBhbmQgbmFpdmUgKHZzZWFyY2gpIGNsdXN0ZXJpbmcsIGFuZCBtdWx0aXBseWluZyBpdCBieSB0aGUgcmF0aW8gb2YgdG90YWwgbGlicmFyeSBzaXplIChmb3IgYWxsIHNhbXBsZXMgY29ycmVzcG9uZGluZyB0byB0aGUgcGF0aWVudCBpbiBxdWVzdGlvbikgdG8gZG93bnNhbXBsZWQgbGlicmFyeSBzaXplLiBTaW5jZSBzdWJzYW1wbGluZyBpcyBkb25lIGluIGEgc3RyYXRpZmllZCBtYW5uZXIsIHRoaXMgaXMgY29ycmVjdC4gCgpOb3RlIHRoYXQgZXZlbiB0aG91Z2ggMTUgY2xvbmFsIGZhbWlsaWVzIHdlcmUgcnVuIHBlciBwYXRpZW50LCBzZXZlcmFsIG9mIHRoZW0gbWF5IGhhdmUgZmFpbGVkIHNvbWV3aGVyZSBhbG9uZyB0aGUgcGlwZWxpbmUuIFRoaXMgY2FuIGJlIGR1ZSB0bzogcG9vciBlc3RpbWF0aW9uIG9mIGZhbWlseSBzaXplcyAoZmFtaWxpZXMgbGFyZ2VyIHRoYW4gMTIwMCBCQ1JzIGFyZSBub3QgcnVuIGZvciBjb21wdXRhdGlvbiB0aW1lIGlzc3VlcywgYW5kIGVtcHR5IGZhbWlsaWVzIHlpZWxkIG5vIHJlc3VsdHMpLCBjbHVzdGVyIGZhaWx1cmVzICh3aWxsIGhhdmUgdG8gcmVydW4gYSBmZXcgYmVjYXVzZSBvZiB0aGlzKS4gQXMgZmFyIGFzIEknbSBhd2FyZSwgYWxsIGVhc2lseS1yZXNvbHZlZCBjaGVja3MvZml4ZXMgaGF2ZSBiZWVuIGltcGxlbWVudGVkLiAKCklmIHdlIGFic29sdXRlbHkgbmVlZCAxNSBwZXIgcGF0aWVudCwgSSBzdWdnZXN0IHJlcnVubmluZyBtb3JlIGZhbWlsaWVzLiBUaGlzIGNhbiBiZSBlYXNpbHkgZG9uZSBidXQgdGFrZXMgdGltZSAofiAxIHdlZWsgcGVyIGNvbXBsZXRlIHJ1bikuIAoKIyMgRGVmaW5pdGlvbnMKCkZvciB0aGUgYW5hbHlzaXMgYmVsb3csIHRoZXJlIHdpbGwgb2Z0ZW4gYmUgdHdvIHNldHMgb2YgcGxvdHMuIFRoZXNlIGFyZSBuYW1lZCAob3BlbiB0aGUgY29kZSBjaHVua3MgdG8gcmV2ZWFsKToKCiogTXVncmF0aW9uOiBGdWxsIHBoeWxvZ2VvZ3JhcGhpYyBtb2RlbAoqIE5vbmdlb2dyYXBoaWM6IE5vcm1hbCBwaHlsb2dlbmV0aWMgbW9kZWwgd2l0aCBubyBnZW9ncmFwaGljIGluZm9ybWF0aW9uCgpUaGVyZSBhcmUgYXJndW1lbnRzIHRvIHdoeSB1c2luZyBtdWdyYXRpb24gd291bGRuJ3QgYmUgJ2ZhaXInLiBPYnZpb3VzbHksIGluY29ycG9yYXRpbmcgZ2VvZ3JhcGh5IGFzIGEgZGlzY3JldGUgdHJhaXQgbmF0dXJhbGx5IGxlYWRzIHRvIGNsdXN0ZXJpbmcgYnkgZ2VvZ3JhcGhpYyBsb2NhdGlvbiAtLSB0aGlzIG1pZ2h0IGludHJvZHVjZSBiaWFzZXMgaW50byB0aGUgZ2VuZWFsb2dpY2FsIHNvcnRpbmcgYW5hbHlzaXMgYmVsb3cuIEhlbmNlIHdoeSBJIGFsc28gZG8gdGhlIHNhbWUgYW5hbHlzaXMgZm9yIHRoZSBub25nZW9ncmFwaGljIHRyZWVzLiAKCiMjIERhdGEKCmBgYHtyfQppaGNfdGFibGUgPC0gZnJlYWQoaWhjX3RhYmxlX2ZpbGUpCmloY19zdWJzZXQgPC0gc3Vic2V0KGloY190YWJsZSwgc2VsZWN0PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgdGlsdHlwZXMpKQppaGNfbWVhbnMgPC0gaWhjX3N1YnNldCAlPiUgZ3JvdXBfYnkocGF0aWVudF9pZCkgJT4lIHNlbGVjdCgtYyhjb25kZW5zZWRfaWQpKSAlPiUgc3VtbWFyaXNlX2FsbChtZWFuKQoKaXRoX3N0YXRzIDwtIHJlYWRfaXRoX3N0YXRzKGl0aF9zdGF0c19maWxlLCBkYl9wYXRoLCBkdXBsaWNhdGVzID0gRkFMU0UpCnByb3BvcnRpb25fc3ViY2xvbmFsaXR5IDwtIGZyZWFkKHByb3BvcnRpb25fc3ViY2xvbmFsaXR5X2ZpbGUpCmBgYAoKYGBge3J9Cm11Z3JhdGlvbl9vdXRkaXIgPC0gZmlsZS5wYXRoKGlncGFydGl0aW9uX291dGRpciwgInN0YXRzL2FsbC9waHlsb2dlb2dyYXBoeS9iYXllc2lhbl9tdWdyYXRpb25fc3RhbmRhcmRfbm9uY2xvY2svMS1lbmQvcGFydGl0aW9uZWRfR1RSIikKbm9uZ2VvZ3JhcGhpY19vdXRkaXIgPC0gZmlsZS5wYXRoKGlncGFydGl0aW9uX291dGRpciwgInN0YXRzL2FsbC9waHlsb2dlbmV0aWNzL2JheWVzaWFuX25vbmdlb2dyYXBoaWMvMS1lbmQvcGFydGl0aW9uZWQiKQpyb290ZnJlcV9vdXRkaXIgPC0gZmlsZS5wYXRoKGlncGFydGl0aW9uX291dGRpciwgInBsb3RzL3BoeWxvZ2VvZ3JhcGh5L2JheWVzaWFuX211Z3JhdGlvbl9zdGFuZGFyZF9ub25jbG9jay8xLWVuZC9wYXJ0aXRpb25lZF9HVFIiKQpgYGAKCmBgYHtyfQptdWdyYXRpb25fc3RhdGZpbGVzIDwtIGxpc3QuZmlsZXMobXVncmF0aW9uX291dGRpciwgcGF0dGVybiA9ICIudHN2IiwgcmVjdXJzaXZlID0gVFJVRSwgZnVsbC5uYW1lcyA9IFRSVUUpCm5vbmdlb2dyYXBoaWNfc3RhdGZpbGVzIDwtIGxpc3QuZmlsZXMobm9uZ2VvZ3JhcGhpY19vdXRkaXIsIHBhdHRlcm4gPSAiLnRzdiIsIHJlY3Vyc2l2ZSA9IFRSVUUsIGZ1bGwubmFtZXMgPSBUUlVFKQoKcm9vdGZyZXFfZmlsZXMgPC0gbGlzdC5maWxlcyhyb290ZnJlcV9vdXRkaXIsIHBhdHRlcm49Ii50eHQiLCByZWN1cnNpdmUgPSBUUlVFLCBmdWxsLm5hbWVzID0gVFJVRSkKYGBgCgojIyBHZW5lYWxvZ2ljYWwgc29ydGluZyBhbmFseXNpcwoKYGBge3J9CnJlYWRfZ3NpX3N0YXRzIDwtIGZ1bmN0aW9uKGYpIHsKICBvYnNrZXkgPC0gIm9ic2VydmVkIgogIGRhdCA8LSBmcmVhZChmKQogIGRhdCA8LSBzdWJzZXQoZGF0LCBtZWRpYW5fZ3NpICE9IC0xKQogIAogIG9icyA8LSBzdWJzZXQoZGF0LCBwZXJtdXRhdGlvbiA9PSBvYnNrZXkpCiAgYmtnIDwtIHN1YnNldChkYXQsIHBlcm11dGF0aW9uICE9IG9ic2tleSkKICAKICBia2dfbWVhbiA8LSBia2cgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUgc3VtbWFyaXNlKGdzaV9tPW1lZGlhbihtZWRpYW5fZ3NpLCBuYS5ybT1UUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzaV9zPW1hZChtZWRpYW5fZ3NpLCBuYS5ybT1UUlVFKSkKICBlbXBpcmljYWwgPC0gZGF0ICU+JSBncm91cF9ieShncm91cCkgJT4lIHN1bW1hcmlzZShwdmFsPTEtbGVuZ3RoKHdoaWNoKG1lZGlhbl9nc2lbd2hpY2gocGVybXV0YXRpb24gIT0gb2Jza2V5KV0gPAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVkaWFuX2dzaVtwZXJtdXRhdGlvbiA9PSBvYnNrZXldKSkvKG4oKS0xKSkKICAjIyBOb3QgcmVhbGx5IEdhdXNzaWFuIGJ1dCB2aXN1YWxseSBsb29rcyB+Y2xvc2UKICBkYXRfc3VtbWFyeSA8LSBtZXJnZShvYnMsIGJrZ19tZWFuLCBieT1jKCJncm91cCIpKQogIGRhdF9zdW1tYXJ5JHpfc2NvcmUgPC0gd2l0aChkYXRfc3VtbWFyeSwgKG1lZGlhbl9nc2ktZ3NpX20pL2dzaV9zKQogIAogIHJlcyA8LSBtZXJnZShkYXRfc3VtbWFyeSwgZW1waXJpY2FsLCBieT1jKCJncm91cCIpKQogIHJlcyA8LSBzdWJzZXQocmVzLCBzZWxlY3Q9LWMocGVybXV0YXRpb24pKQogIHJlcyA8LSBzdWJzZXQocmVzLCBnc2lfcyAhPSAwKQogIAogIHJldHVybihyZXMpCn0KYGBgCgpgYGB7cn0KZ2V0X2dzaV90YWJsZSA8LSBmdW5jdGlvbihzdGF0ZmlsZXMpIHsKICBnc2lfc3RhdHMgPC0gcmJpbmQuZmlsbChsYXBwbHkoc3RhdGZpbGVzW3N0cl9kZXRlY3Qoc3RhdGZpbGVzLCAiZ3NpX3ZhbHVlcyIpXSwgZnVuY3Rpb24oZikgewogICAgZGYgPC0gcmVhZF9nc2lfc3RhdHMoZikKICAgIAogICAgcGF0aWVudF9jbHVzdF9yZXAgPC0gc3RyX2V4dHJhY3QoZiwgIlswLTldK1xcL2NsdXN0WzAtOV0rL1swLTldKyIpCiAgICBzdHJzIDwtIHN0cnNwbGl0KHBhdGllbnRfY2x1c3RfcmVwLCAiLyIpW1sxXV0KICAgIHBhdGllbnQgPC0gc3Ryc1sxXQogICAgY2x1c3QgPC0gc3Ryc1syXQogICAgcmVwIDwtIHN0cnNbM10KICAgIAogICAgaWYgKG5yb3coZGYpICE9IDApIHsKICAgICAgZGYgPC0gY2JpbmQocGF0aWVudF9pZD1wYXRpZW50LCBjbHVzdD1jbHVzdCwgcmVwPXJlcCwgZGYpCiAgICAgIGRmJGNvbmRlbnNlZF9pZCA8LSB3aXRoKGRmLCBwYXN0ZShwYXRpZW50X2lkLCBncm91cCwgc2VwPSJfIikpCiAgICB9CiAgICByZXR1cm4oZGYpCiAgfSkpCiAgcmV0dXJuKGdzaV9zdGF0cykKfQpgYGAKCmBgYHtyfQptdWdyYXRpb25fZ3NpX3N0YXRzIDwtIGdldF9nc2lfdGFibGUobXVncmF0aW9uX3N0YXRmaWxlcykKCm5vbmdlb2dyYXBoaWNfZ3NpX3N0YXRzIDwtIGdldF9nc2lfdGFibGUobm9uZ2VvZ3JhcGhpY19zdGF0ZmlsZXMpCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9nc2lfc3RhdHMgPC0gZnVuY3Rpb24oZ3NpX3N0YXRzKSB7CiAgZ3NpX3N0YXRzX2F2ZyA8LSBnc2lfc3RhdHMgJT4lIGdyb3VwX2J5KHBhdGllbnRfaWQsIGNsdXN0LCBncm91cCwgY29uZGVuc2VkX2lkKSAlPiUgc2VsZWN0KC1jKHJlcCkpICU+JSBzdW1tYXJpc2VfYWxsKG1lYW4pCiAgZ3NpX3N1bW1hcnkgPC0gZ3NpX3N0YXRzX2F2ZyAlPiUgZ3JvdXBfYnkoY29uZGVuc2VkX2lkLCBwYXRpZW50X2lkLCBncm91cCkgJT4lCiAgc3VtbWFyaXNlKG1lZGdzaT1tZWRpYW4obWVkaWFuX2dzaSwgbmEucm09VFJVRSksIHo9bWVkaWFuKHpfc2NvcmUsIG5hLnJtPVRSVUUpLCBwc2lnID0gbGVuZ3RoKHdoaWNoKHB2YWwgPCAwLjA1KSkvbGVuZ3RoKHdoaWNoKG1lZGlhbl9nc2kgIT0gLTEpKSwgcHZhbD1tZWRpYW4ocHZhbFt3aGljaChtZWRpYW5fZ3NpICE9IC0xKV0sIG5hLnJtPVRSVUUpLCBudW09bGVuZ3RoKHdoaWNoKG1lZGlhbl9nc2kgIT0gLTEpKSkKICAKICBnc2lfc3VtbWFyeSA8LSBzdWJzZXQoZ3NpX3N1bW1hcnksICFpcy5uYSh6KSkKICByZXR1cm4oZ3NpX3N1bW1hcnkpCn0KCmdzaV9paGNfY29ycmVsYXRpb24gPC0gZnVuY3Rpb24oZ3NpX3N1bW1hcnksIGdzaV9tZWFzdXJlID0gIm1lZGdzaSIsIGloYywgdGlsdHlwZXMpIHsKICBnc2lfaWhjIDwtIG1lcmdlKGdzaV9zdW1tYXJ5LCBpaGMsIGJ5PWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIikpCiAgZ3NpX2loY19tZWx0ZWQgPC0gbWVsdChnc2lfaWhjLCBpZC52YXJzID1jKCJjb25kZW5zZWRfaWQiLCAicGF0aWVudF9pZCIsICJtZWRnc2kiLCAieiIsICJwc2lnIiwgInB2YWwiLCAibnVtIiksIG1lYXN1cmUudmFycyA9IHRpbHR5cGVzLCB2YXJpYWJsZS5uYW1lID0gInRpbHR5cGUiLCB2YWx1ZS5uYW1lID0gImRlbnNpdHkiKQogIAogICMjIEZpbHRlciBmb3IgbnVtYmVyIG9mIG9ic2VydmF0aW9ucwogIGdzaV9paGNfbWVsdGVkX2ZpbHRlcmVkIDwtIHN1YnNldChnc2lfaWhjX21lbHRlZCwgbnVtID49IDMpCiAgCiAgcHZhbHMgPC0gc2V0TmFtZXMoZGRwbHkoZ3NpX2loY19tZWx0ZWRfZmlsdGVyZWQsIC4odGlsdHlwZSksIGZ1bmN0aW9uKHgpIHsKICAgIGRmIDwtIGFzLmRhdGEuZnJhbWUoeCkKICAgIGNvcnJlcyA8LSBjb3IudGVzdChkZlssImRlbnNpdHkiXSwgZGZbLGdzaV9tZWFzdXJlXSwgbWV0aG9kPSJzcGVhcm1hbiIpCiAgICAKICAgIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICAgIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKICB9KSwgYygidGlsdHlwZSIsICJwLnZhbHVlIikpCiAgCiAgcCA8LSBnZ3Bsb3QoZ3NpX2loY19tZWx0ZWRfZmlsdGVyZWQsIGFlc19zdHJpbmcoeD0iZGVuc2l0eSIsIHk9Z3NpX21lYXN1cmUpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIGZhY2V0X3dyYXAofiB0aWx0eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpIAogIHJldHVybihwKQp9Cgpnc2lfaXRoX2NvcnJlbGF0aW9uIDwtIGZ1bmN0aW9uKGdzaV9zdW1tYXJ5LCBnc2lfbWVhc3VyZSA9ICJtZWRnc2kiLCBpdGgsIGl0aF9tZWFzdXJlcykgewogIGdzaV9pdGggPC0gbWVyZ2UoZ3NpX3N1bW1hcnksIGl0aCwgYnk9YygiY29uZGVuc2VkX2lkIiwgInBhdGllbnRfaWQiKSkKICBnc2lfaXRoX21lbHRlZCA8LSBtZWx0KGdzaV9pdGgsIGlkLnZhcnMgPWMoImNvbmRlbnNlZF9pZCIsICJwYXRpZW50X2lkIiwgIm1lZGdzaSIsICJ6IiwgInBzaWciLCAicHZhbCIsICJudW0iKSwgbWVhc3VyZS52YXJzID0gaXRoX21lYXN1cmVzLCB2YXJpYWJsZS5uYW1lID0gIml0aHR5cGUiLCB2YWx1ZS5uYW1lID0gIml0aCIpCiAgCiAgIyMgRmlsdGVyIGZvciBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zCiAgZ3NpX2l0aF9tZWx0ZWRfZmlsdGVyZWQgPC0gc3Vic2V0KGdzaV9pdGhfbWVsdGVkLCBudW0gPj0gMykKICAKICBwdmFscyA8LSBzZXROYW1lcyhkZHBseShnc2lfaXRoX21lbHRlZF9maWx0ZXJlZCwgLihpdGh0eXBlKSwgZnVuY3Rpb24oeCkgewogICAgZGYgPC0gYXMuZGF0YS5mcmFtZSh4KQogICAgY29ycmVzIDwtIGNvci50ZXN0KGRmWywiaXRoIl0sIGRmWyxnc2lfbWVhc3VyZV0sIG1ldGhvZD0ic3BlYXJtYW4iKQogICAgCiAgICBwdmFsIDwtIGNvcnJlcyRwLnZhbHVlCiAgICBlcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyhQKT09cCwgbGlzdChwPWZvcm1hdChwdmFsLCBkaWdpdHM9MykpKQogICAgcmV0dXJuKGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSkpCiAgfSksIGMoIml0aHR5cGUiLCAicC52YWx1ZSIpKQogIAogIHAgPC0gZ2dwbG90KGdzaV9pdGhfbWVsdGVkX2ZpbHRlcmVkLCBhZXNfc3RyaW5nKHg9Iml0aCIsIHk9Z3NpX21lYXN1cmUpKSArIGdlb21fcG9pbnQoYWVzKGNvbG91cj1wYXRpZW50X2lkKSkgKyB0aGVtZV9idygpICsgdGhlbWVfUHVibGljYXRpb24oKSArIGZhY2V0X3dyYXAofiBpdGh0eXBlLCBzY2FsZXMgPSAiZnJlZSIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBhbF9wYXRpZW50KSArIGdlb21fdGV4dChkYXRhPXB2YWxzLCBhZXMoeD1JbmYsIHk9SW5mLCBsYWJlbD1wLnZhbHVlKSwgaGp1c3Q9MS4xLCB2anVzdD0xLjUsc2l6ZT0zLHBhcnNlPVRSVUUpIAogIHJldHVybihwKQp9CmBgYAoKYGBge3J9Cm11Z3JhdGlvbl9nc2lfc3VtbWFyeSA8LSBzdW1tYXJpemVfZ3NpX3N0YXRzKG11Z3JhdGlvbl9nc2lfc3RhdHMpCm5vbmdlb2dyYXBoaWNfZ3NpX3N1bW1hcnkgPC0gc3VtbWFyaXplX2dzaV9zdGF0cyhub25nZW9ncmFwaGljX2dzaV9zdGF0cykKYGBgCgojIyMgR1NJLUlIQyBhbmFseXNpcwoKVGhlIHJlc3VsdHMgYmVsb3cgc2hvdyB0aGF0IHNhbXBsZXMgd2l0aCBoaWdoZXIgVElMIGRlbnNpdGllcyBoYXZlIGxvd2VyIEdTSXMuIExvd2VyIEdTSXMgPSBsZXNzIGdlb2dyYXBoaWMgZXhjbHVzaXZpdHkgaW4gcGh5bG9nZW5pZXMsIGkuZS4gbGVzcyBwaHlsb2dlbmV0aWMgc2lnbmFsIGluIGdlb2dyYXBoeSwgaS5lLiBtb3JlIG1peGluZy4gCgpgYGB7cn0KcCA8LSBnc2lfaWhjX2NvcnJlbGF0aW9uKG11Z3JhdGlvbl9nc2lfc3VtbWFyeSwgIm1lZGdzaSIsIGloY19zdWJzZXQsIHRpbHR5cGVzKQpwbG90KHApCnAgPC0gZ3NpX2loY19jb3JyZWxhdGlvbihub25nZW9ncmFwaGljX2dzaV9zdW1tYXJ5LCAibWVkZ3NpIiwgaWhjX3N1YnNldCwgdGlsdHlwZXMpCnBsb3QocCkKYGBgCgpOb3RlIHRoYXQgdGhpcyByZXN1bHQgaXMgbm90IHNpZ25pZmljYW50IHdoZW4gc3RyYXRpZnlpbmcgYnkgcGF0aWVudCAoaS5lLiBtb2RlbGluZyBwYXRpZW50IGFzIGEgcmFuZG9tIGVmZmVjdCkuIAoKIyMjIEdTSS1JVEggYW5hbHlzaXMKCkFzIHdpdGggYWxsIG90aGVyIGFuYWx5c2VzIGluY29ycG9yYXRpbmcgSVRIIHRoaXMgYW5hbHlzaXMgaXMgY3VycmVudGx5IGZsYXdlZCBhcyB0aGUgSVRIIHN0YXRpc3RpY3MgYXJlIG9mZi4gCgpgYGB7cn0KcCA8LSBnc2lfaXRoX2NvcnJlbGF0aW9uKG11Z3JhdGlvbl9nc2lfc3VtbWFyeSwgIm1lZGdzaSIsIGl0aF9zdGF0cywgYygiZW50cm9weSIsICJkaXZlcmdlbmNlIikpCnBsb3QocCkKcCA8LSBnc2lfaXRoX2NvcnJlbGF0aW9uKG5vbmdlb2dyYXBoaWNfZ3NpX3N1bW1hcnksICJtZWRnc2kiLCBpdGhfc3RhdHMsIGMoImVudHJvcHkiLCAiZGl2ZXJnZW5jZSIpKQpwbG90KHApCmBgYAoKCkhvd2V2ZXIsIHRvIGluc3BpcmUgc29tZSBwb3RlbnRpYWxseSBtaXNwbGFjZWQgaG9wZSwgaGVyZSdzIHdoYXQgdGhlIGFuYWx5c2lzIGxvb2tzIGxpa2UgdXNpbmcgdGhlIG5haXZlIG1lYXN1cmUgJ3Byb3BvcnRpb24gc3ViY2xvbmFsaXR5Jywgd2hpY2ggY29ycmVsYXRlZCB2ZXJ5IHdlbGwgd2l0aCB0aGUgb2xkIElUSCBtZWFzdXJlczoKCmBgYHtyfQpwcm9wb3J0aW9uX3N1YmNsb25hbGl0eSA8LSBzdWJzZXQocHJvcG9ydGlvbl9zdWJjbG9uYWxpdHksIHBhdGllbnRfaWQgIT0gMTUpCgpwIDwtIGdzaV9pdGhfY29ycmVsYXRpb24obXVncmF0aW9uX2dzaV9zdW1tYXJ5LCAibWVkZ3NpIiwgcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHksIGMoInByb3BvcnRpb25fc3ViY2xvbmFsIiwgIm1peCIpKQpwbG90KHApCnAgPC0gZ3NpX2l0aF9jb3JyZWxhdGlvbihub25nZW9ncmFwaGljX2dzaV9zdW1tYXJ5LCAibWVkZ3NpIiwgcHJvcG9ydGlvbl9zdWJjbG9uYWxpdHksIGMoInByb3BvcnRpb25fc3ViY2xvbmFsIiwgIm1peCIpKQpwbG90KHApCmBgYAoKTm90ZSB0aGF0IHBhdGllbnQgMTUgd2FzIGRlbGliZXJhdGVseSBleGNsdWRlZCBiZWNhdXNlIGl0IHdhcyB0aGUgc2FtcGxlIGZvciB3aGljaCBDTiBjYWxscyB3ZXJlIGJhZCAodW50aWwgQW5kcmV3IHJlcnVucyB0aGUgcHJvcG9ydGlvbiBzdWJjbG9uYWxpdHkgY2FsY3VsYXRpb24gSSBzaG91bGRuJ3QgaW5jbHVkZSBpdCkuIAoKSWYgdGhpcyBmaW5kaW5nIGhvbGRzIHVwLCBpdCB3b3VsZCBpbXBseSB0aGF0IG1vcmUgaW50cmF0dW1vdXJhbCBoZXRlcm9nZW5laXR5ID0gbW9yZSBCQ1IgbWl4aW5nIHdpdGhpbiBjbG9uYWwgZmFtaWxpZXMuIEluIG90aGVyIHdvcmRzLCBJVEggPD0+IGhldGVyb2dlbmVpdHkgd2l0aGluIEJDUiBmYW1pbGllcyEgUHJldHR5IGNvb2whIEJleW9uZCB0aGF0LCBJJ20gbm90IHN1cmUgaG93IHRvIGJpb2xvZ2ljYWxseSBpbnRlcnByZXQgdGhpcy4gCgojIyBQaHlsb2dlbmV0aWMgc2lnbmFsIGluIHJlYWRjb3VudHMKCgpgYGB7cn0KcmVhZF9waHlsb3NpZ25hbF9maWxlIDwtIGZ1bmN0aW9uKGYpIHsKICBkYXQgPC0gZnJlYWQoZikKICBjYmluZCh2YXI9YygibXV0X2ZyZXFzIiwgInJlYWRjb3VudCIpLCBkYXQpCn0KCmdldF9waHlsb3NpZ25hbF90YWJsZSA8LSBmdW5jdGlvbihzdGF0ZmlsZXMpIHsKICBwaHlsb3NpZ25hbF9zdGF0cyA8LSByYmluZC5maWxsKGxhcHBseShzdGF0ZmlsZXNbc3RyX2RldGVjdChzdGF0ZmlsZXMsICJwaHlsb3NpZ25hbCIpXSwgZnVuY3Rpb24oZikgewogICAgZGYgPC0gcmVhZF9waHlsb3NpZ25hbF9maWxlKGYpCiAgICAKICAgIHBhdGllbnRfY2x1c3RfcmVwIDwtIHN0cl9leHRyYWN0KGYsICJbMC05XStcXC9jbHVzdFswLTldKy9bMC05XSsiKQogICAgc3RycyA8LSBzdHJzcGxpdChwYXRpZW50X2NsdXN0X3JlcCwgIi8iKVtbMV1dCiAgICBwYXRpZW50IDwtIHN0cnNbMV0KICAgIGNsdXN0IDwtIHN0cnNbMl0KICAgIHJlcCA8LSBzdHJzWzNdCiAgICAKICAgIGlmIChucm93KGRmKSAhPSAwKSB7CiAgICAgIGRmIDwtIGNiaW5kKHBhdGllbnRfaWQ9cGF0aWVudCwgY2x1c3Q9Y2x1c3QsIHJlcD1yZXAsIGRmKQogICAgfQogICAgcmV0dXJuKGRmKQogIH0pKQogIHJldHVybihwaHlsb3NpZ25hbF9zdGF0cykKfQoKc3VtbWFyaXplX3BoeWxvc2lnbmFsX3N0YXRzIDwtIGZ1bmN0aW9uKHBoeWxvc2lnbmFsX3N0YXRzKSB7CiAgcGh5bG9zaWduYWxfc3RhdHNfYXZnIDwtIHBoeWxvc2lnbmFsX3N0YXRzICU+JSBncm91cF9ieShwYXRpZW50X2lkLCBjbHVzdCwgdmFyKSAlPiUgc2VsZWN0KC1jKHJlcCkpICU+JSBzdW1tYXJpc2VfYWxsKG1lYW4pCiAgcGh5bG9zaWduYWxfc3VtbWFyeSA8LSBwaHlsb3NpZ25hbF9zdGF0c19hdmcgJT4lIGdyb3VwX2J5KHBhdGllbnRfaWQsIHZhcikgJT4lCiAgc3VtbWFyaXNlKENtZWFuPW1lZGlhbihzdGF0LkNtZWFuLCBuYS5ybT1UUlVFKSwgQ21lYW5fcD1tZWRpYW4ocHZhbHVlLkNtZWFuLCBuYS5ybT1UUlVFKSwgST1tZWRpYW4oc3RhdC5JLCBuYS5ybT1UUlVFKSwgSV9wID0gbWVkaWFuKHB2YWx1ZS5JLCBuYS5ybT1UUlVFKSwgbnVtPW4oKSkKICAKICByZXR1cm4ocGh5bG9zaWduYWxfc3VtbWFyeSkKfQoKcGh5bG9zaWduYWxfaWhjX2NvcnJlbGF0aW9uIDwtIGZ1bmN0aW9uKHBoeWxvc2lnbmFsX3N1bW1hcnksIHBoeWxvc2lnbmFsX21lYXN1cmUgPSAiQ21lYW4iLCBpaGMsIHRpbHR5cGVzLCB2YXJpYWJsZT0icmVhZGNvdW50IikgewogIHBoeWxvc2lnbmFsX3N1bW1hcnkgPC0gc3Vic2V0KHBoeWxvc2lnbmFsX3N1bW1hcnksIHZhciA9PSB2YXJpYWJsZSkKICBwaHlsb3NpZ25hbF9paGMgPC0gbWVyZ2UocGh5bG9zaWduYWxfc3VtbWFyeSwgaWhjLCBieT1jKCJwYXRpZW50X2lkIikpCiAgcGh5bG9zaWduYWxfaWhjX21lbHRlZCA8LSBtZWx0KHBoeWxvc2lnbmFsX2loYywgaWQudmFycyA9YygicGF0aWVudF9pZCIsICJDbWVhbiIsICJDbWVhbl9wIiwgIkkiLCAiSV9wIiwgIm51bSIpLCBtZWFzdXJlLnZhcnMgPSB0aWx0eXBlcywgdmFyaWFibGUubmFtZSA9ICJ0aWx0eXBlIiwgdmFsdWUubmFtZSA9ICJkZW5zaXR5IikKICAKICAjIyBGaWx0ZXIgZm9yIG51bWJlciBvZiBvYnNlcnZhdGlvbnMKICBwaHlsb3NpZ25hbF9paGNfbWVsdGVkX2ZpbHRlcmVkIDwtIHN1YnNldChwaHlsb3NpZ25hbF9paGNfbWVsdGVkLCBudW0gPj0gMykKICAKICBwdmFscyA8LSBzZXROYW1lcyhkZHBseShwaHlsb3NpZ25hbF9paGNfbWVsdGVkX2ZpbHRlcmVkLCAuKHRpbHR5cGUpLCBmdW5jdGlvbih4KSB7CiAgICBkZiA8LSBhcy5kYXRhLmZyYW1lKHgpCiAgICBjb3JyZXMgPC0gY29yLnRlc3QoZGZbLCJkZW5zaXR5Il0sIGRmWyxwaHlsb3NpZ25hbF9tZWFzdXJlXSwgbWV0aG9kPSJzcGVhcm1hbiIpCiAgICAKICAgIHB2YWwgPC0gY29ycmVzJHAudmFsdWUKICAgIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKFApPT1wLCBsaXN0KHA9Zm9ybWF0KHB2YWwsIGRpZ2l0cz0zKSkpCiAgICByZXR1cm4oYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKSkKICB9KSwgYygidGlsdHlwZSIsICJwLnZhbHVlIikpCiAgCiAgcCA8LSBnZ3Bsb3QocGh5bG9zaWduYWxfaWhjX21lbHRlZF9maWx0ZXJlZCwgYWVzX3N0cmluZyh4PSJkZW5zaXR5IiwgeT1waHlsb3NpZ25hbF9tZWFzdXJlKSkgKyBnZW9tX3BvaW50KGFlcyhjb2xvdXI9cGF0aWVudF9pZCkpICsgdGhlbWVfYncoKSArIHRoZW1lX1B1YmxpY2F0aW9uKCkgKyBmYWNldF93cmFwKH4gdGlsdHlwZSwgc2NhbGVzID0gImZyZWUiKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYWxfcGF0aWVudCkgKyBnZW9tX3RleHQoZGF0YT1wdmFscywgYWVzKHg9SW5mLCB5PUluZiwgbGFiZWw9cC52YWx1ZSksIGhqdXN0PTEuMSwgdmp1c3Q9MS41LHNpemU9MyxwYXJzZT1UUlVFKSAKICByZXR1cm4ocCkKfQpgYGAKCgpgYGB7cn0KbXVncmF0aW9uX3BoeWxvc2lnbmFsX3N0YXRzIDwtIGdldF9waHlsb3NpZ25hbF90YWJsZShtdWdyYXRpb25fc3RhdGZpbGVzKQpub25nZW9ncmFwaGljX3BoeWxvc2lnbmFsX3N0YXRzIDwtIGdldF9waHlsb3NpZ25hbF90YWJsZShub25nZW9ncmFwaGljX3N0YXRmaWxlcykKYGBgCgpgYGB7cn0KbXVncmF0aW9uX3BoeWxvc2lnbmFsX3N1bW1hcnkgPC0gc3VtbWFyaXplX3BoeWxvc2lnbmFsX3N0YXRzKG11Z3JhdGlvbl9waHlsb3NpZ25hbF9zdGF0cykKbm9uZ2VvZ3JhcGhpY19waHlsb3NpZ25hbF9zdW1tYXJ5IDwtIHN1bW1hcml6ZV9waHlsb3NpZ25hbF9zdGF0cyhub25nZW9ncmFwaGljX3BoeWxvc2lnbmFsX3N0YXRzKQpgYGAKCiMjIyBQaHlsb3NpZ25hbC1JSEMgYW5hbHlzaXMKCmBgYHtyfQpwIDwtIHBoeWxvc2lnbmFsX2loY19jb3JyZWxhdGlvbihtdWdyYXRpb25fcGh5bG9zaWduYWxfc3VtbWFyeSwgIkNtZWFuIiwgaWhjX21lYW5zLCB0aWx0eXBlcywgdmFyaWFibGUgPSAicmVhZGNvdW50IikKcGxvdChwKQoKcCA8LSBwaHlsb3NpZ25hbF9paGNfY29ycmVsYXRpb24obXVncmF0aW9uX3BoeWxvc2lnbmFsX3N1bW1hcnksICJJIiwgaWhjX21lYW5zLCB0aWx0eXBlcywgdmFyaWFibGUgPSAicmVhZGNvdW50IikKcGxvdChwKQoKcCA8LSBwaHlsb3NpZ25hbF9paGNfY29ycmVsYXRpb24obXVncmF0aW9uX3BoeWxvc2lnbmFsX3N1bW1hcnksICJDbWVhbiIsIGloY19tZWFucywgdGlsdHlwZXMsIHZhcmlhYmxlID0gIm11dF9mcmVxcyIpCnBsb3QocCkKCnAgPC0gcGh5bG9zaWduYWxfaWhjX2NvcnJlbGF0aW9uKG5vbmdlb2dyYXBoaWNfcGh5bG9zaWduYWxfc3VtbWFyeSwgIkNtZWFuIiwgaWhjX21lYW5zLCB0aWx0eXBlcywgdmFyaWFibGUgPSAicmVhZGNvdW50IikKcGxvdChwKQoKcCA8LSBwaHlsb3NpZ25hbF9paGNfY29ycmVsYXRpb24obm9uZ2VvZ3JhcGhpY19waHlsb3NpZ25hbF9zdW1tYXJ5LCAiSSIsIGloY19tZWFucywgdGlsdHlwZXMsIHZhcmlhYmxlID0gInJlYWRjb3VudCIpCnBsb3QocCkKCnAgPC0gcGh5bG9zaWduYWxfaWhjX2NvcnJlbGF0aW9uKG5vbmdlb2dyYXBoaWNfcGh5bG9zaWduYWxfc3VtbWFyeSwgIkNtZWFuIiwgaWhjX21lYW5zLCB0aWx0eXBlcywgdmFyaWFibGUgPSAibXV0X2ZyZXFzIikKcGxvdChwKQpgYGAKClNvIHBoeWxvZ2VuZXRpYyBzaWduYWwgaW4gcmVhZGNvdW50cyBjb3JyZWxhdGVzIHdpdGggQ0QyMCsgYW5kIENENCsgVElMIGRlbnNpdGllcy4gVGhpcyBpcyBwb3RlbnRpYWxseSBldmlkZW5jZSBvZiBzZWxlY3Rpb24gb2NjdXJyaW5nIChjbGFkZS1zcGVjaWZpYyBleHBhbnNpb24pIGluIHNhbXBsZXMgd2l0aCBoaWdoZXIgVElMIGRlbnNpdGllcywgYSBmaW5kaW5nIHRoYXQgd2UgbWlnaHQgbmFpdmVseSBleHBlY3QuIAoKTm90ZSB0aGF0IEkgcmFuIGEgZmV3IHBsb3Qgc2V0cyB1c2luZyB0aGUgdmFyaWFibGUgKm11dF9mcmVxcyosIHdoaWNoIGp1c3QgcmVmZXJzIHRvIG11dGF0aW9uIGZyZXF1ZW5jeS4gV2UgZG9uJ3QgZXhwZWN0IHBoeWxvZ2VuZXRpYyBzaWduYWwgZm9yIHRoaXMgcGFyYW1ldGVyIHRvIGNvcnJlbGF0ZSB3aXRoIGFueSBiaW9sb2dpY2FsIHBhcmFtZXRlcnMsIHdoaWNoIGlzIHdoYXQgd2Ugb2JzZXJ2ZS4gCgojIyMgUGh5bG9zaWduYWwtSVRIIGFuYWx5c2lzCgpUT0RPLiAKCgojIyBPcmlnaW4gYW5hbHlzaXMKCiMjIyBEZXNjcmlwdGlvbgoKT3JpZ2luIGFuYWx5c2lzID0gcm9vdCBmcmVxdWVuY3kgYW5hbHlzaXMuIFRoZSBnb2FsIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gaW5mZXIgdGhlIGdlb2dyYXBoaWNhbCBvcmlnaW4gb2YgZWFjaCBCLWNlbGwgY2xvbmFsIGZhbWlseSwgZXF1aXZhbGVudCB0byBhbmNlc3RyYWwgc3RhdGUgYW5hbHlzaXMgYXQgdGhlIHJvb3Qgbm9kZS4gVGhlc2UgcmVzdWx0cyBjYW4gdGhlbiBiZSBjb3JyZWxhdGVkIHdpdGggb3RoZXIga25vd24gY292YXJpYXRlcy4gCgojIyMgUXVlc3Rpb25zCgpTb21lIHJlYWxseSBzaW1wbGUgcXVlc3Rpb25zIHRvIGFzayBpbmNsdWRlOgoKKiBEbyBjbG9uYWwgZmFtaWxpZXMgZnJvbSB0aGUgc2FtZSBwYXRpZW50IGNvbnNpc3RlbnRseSBhcmlzZSBmcm9tIHRoZSBzYW1lIGdlb2dyYXBoaWMgb3JpZ2luPwoqIElzIGdlb2dyYXBoaWMgb3JpZ2luIHJlc3RyaWN0ZWQgdG8gY2VydGFpbiBhbmF0b21pYyBzaXRlcz8gCiogQXJlIGdlb2dyYXBoaWMgb3JpZ2luL21pZ3JhdGlvbiBwYXR0ZXJucyBjb3JyZWxhdGVkIHdpdGggdHVtb3VyIGNsb25lIG1pZ3JhdGlvbiBwYXR0ZXJucz8KCkluIHRoZSBwcmV2aW91cyB2ZXJzaW9uIG9mIHRoaXMgYW5hbHlzaXMsIEkgb2JzZXJ2ZWQgYSBwYXR0ZXJuIHdoZXJlLCBpbiBoaWdoLWltbXVuZSBwYXRpZW50cywgZ2VvZ3JhcGhpYyBvcmlnaW4gdGVuZGVkIHRvIGJlIGNvbnNpc3RlbnQgYWNyb3NzIGNsb25hbCBmYW1pbGllcy4gVGhlIHNhbWUgdHJlbmQgd2FzIG5vdCBwcmVzZW50IGZvciBsb3ctaW1tdW5lIGNsdXN0ZXJzLiBBIGh5cG90aGV0aWNhbCBiaW9sb2dpY2FsIGV4cGxhbmF0aW9uIGZvciB0aGlzIHdhcyB0aGF0LCBpbiBoaWdoLWltbXVuZSBwYXRpZW50cywgQi1jZWxsIGNsb25hbCBmYW1pbGllcyByZXNwb25kIHRvIHRoZSBzYW1lL3NpbWlsYXIgYW50aWdlbmljIHRocmVhdHMsIGFuZCB0aGVyZWZvcmUgYXJvc2UgZnJvbSB0aGUgc2FtZSBzaXRlIHdoZXJlIHRob3NlIGFudGlnZW5zIHdlcmUgaW5pdGlhbGx5IHByZXNlbnRlZC4gV2hlcmVhcywgaW4gbG93LWltbXVuZSBwYXRpZW50cywgaXQgd2FzIHBvc3NpYmxlIHRoYXQgQi1jZWxsIGNsb25hbCBmYW1pbGllcyB3ZXJlIG5vdCB0dW1vdXItcmVhY3RpdmUgYW5kIHdlcmUgaW5zdGVhZCAnc2VudGluZWxzJyBwYXRyb2xsaW5nIGVhY2ggdHVtb3VyIHNpdGUuIAoKV2UgY2hlY2sgaWYgdGhpcyBwYXR0ZXJuIGhvbGRzIHVwLiAKCgojIyMgUmVzdWx0cwoKYGBge3J9CnJlYWRfcm9vdF9mcmVxdWVuY2llcyA8LSBmdW5jdGlvbihmLCBjbHVzdCwgcmVwKSB7CiAgZnJlcXMgPC0gZnJlYWQoZikKICBjYmluZChjbHVzdD1jbHVzdCwgcmVwPXJlcCwgZnJlcXMpCn0KCmdldF9yZl90YWJsZSA8LSBmdW5jdGlvbihmaWxlcykgewogIHBhdGllbnRfY2x1c3RfcmVwIDwtIHN0cl9leHRyYWN0KGZpbGVzLCAiWzAtOV0rXFwvY2x1c3RbMC05XSsvWzAtOV0rIikKICBzdHJzIDwtIHN0cnNwbGl0KHBhdGllbnRfY2x1c3RfcmVwLCAiLyIpCiAgcGF0aWVudCA8LSBzYXBwbHkoc3RycywgZnVuY3Rpb24oeCkgeFsxXSkKICBjbHVzdCA8LSBzYXBwbHkoc3RycywgZnVuY3Rpb24oeCkgeFsyXSkKICByZXAgPC0gc2FwcGx5KHN0cnMsIGZ1bmN0aW9uKHgpIHhbM10pCiAgCiAgaW5mbyA8LSBkYXRhLmZyYW1lKHBhdGllbnRfaWQ9cGF0aWVudCwgY2x1c3Q9Y2x1c3QsIHJlcD1yZXAsIHRhYmxlZmlsZT1maWxlcywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogIAogIGZyZXFfdGFibGVzIDwtIGluZm8gJT4lIGdyb3VwX2J5KHBhdGllbnRfaWQpICU+JSBkbyhyZj1yYmluZC5maWxsKGxhcHBseShzZXFfYWxvbmcoLiR0YWJsZWZpbGUpLCBmdW5jdGlvbihpKSByZWFkX3Jvb3RfZnJlcXVlbmNpZXMoLiR0YWJsZWZpbGVbaV0sIC4kY2x1c3RbaV0sIC4kcmVwW2ldKSkpKQogIAogIGZyZXFfdGFibGVzJHJmIDwtIGxhcHBseShmcmVxX3RhYmxlcyRyZiwgZnVuY3Rpb24oeCkgewogICAgcm93b3JkZXIgPC0gb3JkZXIoYXMubnVtZXJpYyhzdHJfZXh0cmFjdCh4JGNsdXN0LCAiWzAtOV0rIikpLCB4JHJlcCkKICAgIHggPC0geFtyb3dvcmRlcixdCiAgfSkKICBmcmVxX3RhYmxlcyA8LSBmcmVxX3RhYmxlc1tvcmRlcihhcy5udW1lcmljKGZyZXFfdGFibGVzJHBhdGllbnRfaWQpKSxdCiAgcmV0dXJuKGZyZXFfdGFibGVzKQp9CgpwbG90X3JmX3RhYmxlIDwtIGZ1bmN0aW9uKHJmLCBhbm5vdGF0aW9uX2NvbHMgPSBjKCJjbHVzdCIsICJyZXAiKSwgdGl0bGUpIHsKICBkYXQgPC0gYXMuZGF0YS5mcmFtZShyZilbLCghY29sbmFtZXMocmYpICVpbiUgYW5ub3RhdGlvbl9jb2xzKV0KICAKICBhbm5vdGF0aW9uX3JvdyA8LSBzdWJzZXQocmYsIHNlbGVjdD1jKGFubm90YXRpb25fY29scykpCiAgYW5ub3RhdGlvbl9jb2xvdXJzIDwtIGxpc3QoCiAgICBjbHVzdD1nZXRfY29sb3VyX3BhbGV0dGUoYW5ub3RhdGlvbl9yb3ckY2x1c3QpLAogICAgcmVwPWMoJzAnPSIjMDAwMDAwIiwgJzEnPSIjRkZGRkZGIikKICApCiAgCiAgcGhlYXRtYXAoZGF0LCBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIGFubm90YXRpb25fcm93ID0gYW5ub3RhdGlvbl9yb3csCiAgICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPWFubm90YXRpb25fY29sb3Vycywgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBtYWluID0gdGl0bGUpCn0KYGBgCgpgYGB7cn0KcmZfdGFibGUgPC0gZ2V0X3JmX3RhYmxlKHJvb3RmcmVxX2ZpbGVzKQpgYGAKCmBgYHtyfQppZ25vcmUgPC0gbGFwcGx5KDE6bnJvdyhyZl90YWJsZSksIGZ1bmN0aW9uKGkpIHsKICBwYXRpZW50X2lkIDwtIHJmX3RhYmxlJHBhdGllbnRfaWRbaV0KICBwbG90X3RpdGxlIDwtIHBhc3RlKCJQYXRpZW50IiwgcGF0aWVudF9pZCwgc2VwID0gIiAiKQogIHBsb3RfcmZfdGFibGUocmZfdGFibGUkcmZbW2ldXSwgdGl0bGUgPSBwbG90X3RpdGxlKQp9KQpgYGAKCgpJdCdzIHByZXR0eSBvYnZpb3VzIGZyb20gdGhlc2UgcGxvdHMgdGhhdCB0aGlzIHBhdHRlcm4gZG9lc24ndCBob2xkIHVwIGFueW1vcmUuIFRoZSBvbmx5IHBhdGllbnQgZm9yIHdoaWNoIHdlIHNlZSBjb25zaXN0ZW50IGFuYXRvbWljIG9yaWdpbiBpcyBwYXRpZW50IDIsIHdoZXJlIG5lYXJseSBhbGwgY2xvbmFsIGZhbWlsaWVzIGFyaXNlIGZyb20gdGhlIHJpZ2h0IG92YXJ5LiBUaGUgcmVzdWx0cyBmb3IgcGF0aWVudCAyIGNvcnJvYm9yYXRlIG91ciBwcmV2aW91cyBhbmFseXNpcywgYnV0IG5vIG90aGVyIHBhdGllbnRzIGRvLiBQYXRpZW50IDEgYW5kIDIyLCB3aGljaCBhcmUgdHdvIGltbXVuZS1oaWdoIHBhdGllbnRzIHRoYXQgcHJldmlvdXNseSBmb2xsb3dlZCB0aGF0IHRyZW5kLCBubyBsb25nZXIgZG8uIExpa2V3aXNlLCBwYXRpZW50IDE1IGlzIGEgbmV3IChJVEgtMykgcGF0aWVudCB0aGF0IGlzIGV4dHJlbWVseSBpbW11bmUtaGlnaCBhbmQgZG9lcyBub3QgZm9sbG93IHRoZSB0cmVuZC4gVGhlcmVmb3JlIG91ciBoeXBvdGhlc2lzIGlzIG5vdCBzdXBwb3J0ZWQgYnkgdGhlIG5ldyBkYXRhLiA=