Goal

Plot the induction of oxidative stress response genes in C. glabrata during phosphate starvation. Genes known to be induced during hydrogen peroxide stress in S. cerevisiae are the basis for selecting the candiate gene set.

Data

C. glabrata -Pi time course

Load normalized gene counts for the -Pi time course in C. glabrata. Data collected in 2017 by Bin He. Pre-processing was done as described under the E009/seq-results folder.

Below is a simple chart to show the design:

Genotype Time point Replicates Comment
80∆ rich media 2 for consistency check with my previous data
80∆ 4∆ rich media 2 for consistency check with my previous data
wt-1 pre, 20’, 30’, 45’, 60’, 90’, 120’, 150’, 180’, 240’ 1 time course for wt
4∆-1 pre, 20’, 30’, 45’, 60’, 90’, 120’, 150’, 180’, 240’ 1 time course for 4∆
wt-2 pre, 20’, 30’, 45’, 60’, 120’, 180’, 240’ 1 biol. repl. for wt-1
4∆-2 pre, 20’, 30’, 45’, 60’, 120’, 180’ 1 biol. repl. for 4∆-1
load("../input/Cgla-Pi-He2017-unpub/E009-Cgla-Pi-time-course-normalized.RData")
cgToSc <- cgToSc %>% 
  select(CglaID = cgid, CglaName = cgname, ScerID = scid, ScerName = scname)
anno <- left_join(anno, cgToSc, by = c("GeneID" = "CglaID")) %>% 
  select(CglaID = GeneID, CglaName, ScerID, ScerName, Chr, Type.of.Gene)

Construct a SummarizedExperiment object

se0 <- SummarizedExperiment(
  assays = lcpm, 
  rowData = column_to_rownames(anno, "CglaID"),
  colData = column_to_rownames(sample, "Sample")
  )
# subset the experiment to include just the wt time course
se <- se0[, se0$Genotype == "wt" & se0$Timepoint != "del80"]

S. cerevisiae -Pi 1hr gene expression

Description and code below copied/adapted from uncat-analyses/CTA1-induction-noPi

Data source

GEO# Description Reference
GSE23580 Microarray expression analyses of S. cerevisiae wt, pho4∆, pho2∆ in rich or no Pi media, sampled at 1hr Zhou & O’Shea 2011
Zhou X, O’Shea EK. 2011. Integrated approaches reveal determinants of genome-wide binding and function of the transcription factor Pho4. Mol Cell 42:826–836.

The analysis below is inspired by a GEOquery workshop offered by Jason Ratcliff at the Iowa Institute of Human Genetics. getGEO() will return an ExpressionSet object, which is then converted into a “SummarizedExperiment” object, which is a more modern data structure that is easier to deal with.

#Sys.setenv("VROOM_CONNECTION_SIZE" = 131072*10) # increase the local cache size
gse <- GEOquery::getGEO(filename = "../input/Scer-Pi-Zhou2011/GSE23580_series_matrix.txt.gz") %>% 
  as("SummarizedExperiment")

The experimental information is stored in the colData fields. The ones we are interested in are:

colData(gse) %>% 
  as_tibble() %>%
  filter(grepl("Wild type no vs high Pi conditions", title) | grepl("Comparison 1$", title)) %>% 
  select(title, geo_accession, organism = organism_ch1, strain = characteristics_ch1, 
         condition_ch1 = characteristics_ch1.2, label_ch1, condition_ch2 = characteristics_ch2.2, label_ch2)

The first three are part of the mutant cycle while the latter four are not. The latter four are said to have incorporated a dye swap, although I can’t tell how the swap was done from the table above.

Separately extract the two subsets and examine them separately.

gse1 <- gse[, grepl("Comparison 1$", gse$title)]
gse2 <- gse[, grepl("Wild type no vs high Pi conditions", gse$title)]

OSR gene list based on S. cerevisiae literature

Compile gene list known to be involved in the response to hydrogen perxoide. This list is mostly based on Lee et al. 1999 (PMID: 10347154) and Hasan et al. 2002 (PMID: 12100562)

# manually compiled datasets
scer.osr <- read_csv("../input/gene-list/s-cerevisiae-hydroperoxide-response-genes.csv", col_types = cols()) %>%
  mutate(ori.name = str_to_upper(ori.name), cor.name = str_to_upper(cor.name))
# these IDs are retrieved from YeastMine
scer.id <- read_csv("../input/gene-list/s-cerevisiae-hydroperoxide-response-gene-id.csv", col_types = cols()) %>% 
  mutate(input = str_to_upper(input))
# merge
scer.osr <- scer.id %>% 
  select(input, scer.sys = secondaryIdentifier, fun = name, length) %>% 
  left_join(scer.osr, by = c("input" = "cor.name")) %>% 
  select(scer.common = input, -ori.name, everything())

Mapping between the two genomes

Mapping between the two species are downloaded from CGD. Two types of mappings are available: orthology is based on the Yeast Gene Order Browser; best hits is based on blastp and is only performed for genes lacking a credible ortholog. Note that because both mappings start from C. glabrata, they are not the ideal mapping to use for identifying the C. glabrata ortholog for S. cerevisiae genes, but should be sufficient for the current purpose. This is because most of the genes do have 1-to-1 orthologs between the two species and thus starting from either species should yield the same mapping.

Below our goal is to reverse-engineer an scToCg one-to-one mapping from the two cgToSc mappings.

# cgToSc is loaded already and stores the orthology mapping
# read the best hit mapping
cgToSc.bh <- read_tsv("../input/annotation/C_glabrata_CBS138_S_cerevisiae_best_hits.txt", 
                           comment = "#", col_types = cols(), 
                           col_names = c("CglaID","CglaName","Cgla.id","ScerID","ScerName","Scer.id")) %>% 
  select(CglaID, CglaName, ScerID, ScerName)
re.scToCg <- bind_rows(
  ortholog = cgToSc, 
  # only include the rows in the best hit table if the Scer gene is not already in the orthology mapping
  # for Scer genes with more than one Cgla genes mapped to it by blastp, randomly pick the first one
  `best hit` = cgToSc.bh %>% filter(!ScerID %in% cgToSc$ScerID) %>% group_by(ScerID) %>% filter(row_number() == 1),
  .id = "mapping")

Combine the orthology and best hits mapping

cgla.list <- scer.osr %>% 
  dplyr::rename(ScerID = scer.sys, ScerName = scer.common) %>%
  left_join(cgToSc %>% select(CglaID.or = CglaID, CglaName.or = CglaName, ScerID, ScerName)) %>% 
  # note that the following is not a robust procedure. it just happens that none of the Scer genes in 
  # scer.osr have more than one best hit mapped Cgla genes
  left_join(cgToSc.bh %>% select(CglaID.bh = CglaID, CglaName.bh = CglaName, ScerID, ScerName)) %>% 
  mutate(
    CglaID = ifelse(is.na(CglaID.or), CglaID.bh, CglaID.or),
    CglaName = ifelse(is.na(CglaName.or), CglaName.bh, CglaName.or)
  ) %>% 
  select(starts_with("Cgla"), everything())
Joining, by = c("ScerName", "ScerID")
Joining, by = c("ScerName", "ScerID")

Analysis

Plotting C. glabrata time course

myGenesPlot <- function(genes = "CAGL0B02475g", names = "PMU2") {
  # this function takes in the read count matrix (normalized and transformed) and a list of gene IDs
  # and plots the values stratified by genotype and timepoint
  if(any(!genes %in% rownames(se))){
    print("The following genes are not included in the experiment. Check to make sure that the gene names are correct.")
    setdiff(genes, rownames(se))
    stop("Gene names not included in the experiment")
  }
  
  if(!is.null(names(genes))){
    names = names(genes)
  }
  else if(length(genes) != length(names)){
    stop("length of 'genes' must equal length of 'names'")
  }
  
  
  # construct tibble for plotting
  dat <- t(assay(se[genes,])) %>% as_tibble(rownames = NA) %>% rownames_to_column(var = "sample") # subset the expression matrix
  tb <- colData(se) %>% as_tibble(rownames = NA) %>% 
    rownames_to_column(var = "sample") %>% 
    select(sample, Genotype, Timepoint) %>%
    left_join(dat, by = "sample") %>% 
    pivot_longer(cols = starts_with("CAGL"), names_to = "gene", values_to = "log2 cpm") %>% 
    left_join(tibble(gene = genes, name = names), by = "gene")
    
  p <- ggplot(tb, aes(x = Timepoint, y = `log2 cpm`)) + 
    geom_point() + geom_smooth(method = "loess", formula = y ~ x, aes(x = as.numeric(Timepoint), y = `log2 cpm`)) + 
    xlab("") + ylab("log2 count per million") + facet_wrap(~paste(gene, name, sep = ":"), scale = "free_y") +
    theme_bw(base_size = 12) + 
    theme(axis.text.x = element_text(angle = 45, size = rel(0.75), hjust = 1),
          axis.text.y = element_text(size = rel(0.75)))
  return(p)
}
group <- c("antioxidant", "protein degradation", "chaperon")
categories <- lapply(group, function(x){
  test <- cgla.list %>% 
    filter(category == x) %>% 
    select(ScerName, CglaID) %>% 
    filter(!is.na(CglaID)) %>% 
    deframe()
})
names(categories) <- group
categories$antioxidant <- c(categories$antioxidant, CTT1 = "CAGL0K10868g", TSA2 = "CAGL0K06259g")
for (i in seq_along(categories)){
  p <- myGenesPlot(categories[[i]])
  print(p + ggtitle(str_to_upper(group[i])) + theme(plot.title = element_text(hjust = 0.5)))
}
Warning: The `x` argument of `as_tibble.matrix()` must have unique column names if `.name_repair` is omitted as of tibble 2.0.0.
Using compatibility `.name_repair`.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

Plotting S. cerevisiae -Pi at 1hr

To compare OSR genes under phopshate starvation in C. glabrata vs in S. cerevisiae, I downloaded Xu’s 2011 data, which was measured using two-color microarray at 1hr post starvation. In the future I plan to use Gurvich 2017, which contains the time course data but requires more processing.

First, we need to assemble the dataset. The idea is to calculate the log2FC between 1hr and pre-stress time points from the C. glabrata dataset. Later we will merge it with the S. cerevisiae data. The final data should include Species | Name | ID | log2FC, in which Name is always from S. cerevisiae and ID from the respective species.

tmp <- assay(se[, se$Timepoint %in% c("pre", "60m")])
colnames(tmp) <- c("pre.1","pre.2","60m.1","60m.2","60m.3")
cgla.1hr <- as_tibble(tmp, rownames = "CglaID") %>% 
  mutate(
    base = (pre.1 + pre.2)/2,
    m60.1 = `60m.1` - base,
    m60.2 = `60m.2` - base,
    m60.3 = `60m.3` - base
  ) %>% 
  select(CglaID, starts_with("m60")) %>% 
  pivot_longer(m60.1:m60.3, names_to = NULL, values_to = "log2FC")

With the Cgla data calculated, we can write a function that takes a list of S. cerevisiae gene names as input and plot the logFC in both species.

myCompare <- function(genes = "YML123C"){
  # check gene names against the S. cerevisiae microarray annotation
  scerArray <- rowData(gse)$ORF
  names(scerArray) <- rowData(gse)$Name
  if(all(!genes %in% scerArray)){
    stop("Check ORF names. None are present in the annotation file.")
  }else if(any(!genes %in% scerArray)){
    print("The following S. cerevisiae genes are not included on the microarray chip.")
    print(setdiff(genes, scerArray))
  }else if(any(!genes %in% re.scToCg$ScerID)){
    print("The following S. cerevisiae genes do not have a C. glabrata ortholog.")
    print(setdiff(genes, re.scToCg$ScerID))
  }
  geneSet <- rowData(gse) %>% 
    as_tibble(rownames = NULL) %>% 
    filter(ORF %in% genes) %>% 
    select(ScerID = ORF) %>% 
    inner_join(re.scToCg, by = "ScerID")
  
  # extract Scer data
  probes <- rowData(gse1) %>% as_tibble() %>% filter(ORF %in% geneSet$ScerID) %>% select(ID, ORF)
  scerDat <- assay(gse1)[probes$ID,] %>% 
    as_tibble(rownames = "ID") %>% 
    left_join(probes, by = "ID") %>% 
    pivot_longer(cols = starts_with("GSM"), names_to = NULL, values_to = "log2FC") %>% 
    select(ScerID = ORF, log2FC) %>% 
    left_join(select(geneSet, ScerID, ScerName), by = "ScerID") %>% 
    select(ScerName, ID = ScerID, log2FC)
  # extract Cgla data
  cglaDat <- cgla.1hr %>% 
    filter(CglaID %in% na.omit(geneSet$CglaID)) %>% 
    left_join(select(geneSet, CglaID, ScerName), by = "CglaID") %>% 
    select(ScerName, ID = CglaID, log2FC)
  Dat <- bind_rows(`S. cerevisiae` = scerDat, `C. glabrata` = cglaDat, .id = "species") %>%
    # sort the gene list by Cgla induction mean
    mutate(ScerName = forcats::fct_reorder(ScerName, log2FC, mean, .desc = TRUE))
  
  # plotting
  p <- ggplot(Dat, aes(x = ScerName, y = log2FC, group = species)) +
    geom_bar(aes(fill = species), stat = "summary", fun = "mean", alpha = 0.9,
             position = position_dodge(0.9)) +
    geom_point(aes(color = species), position = position_dodge(0.9), size = 1) +
    geom_hline(yintercept = 0, linetype = 1, color = "gray20") +
    geom_hline(yintercept = 1, linetype = 2, color = "gray50") +
    scale_color_manual(values = c("S. cerevisiae" = "gray30", "C. glabrata" = "orange2"), guide = NULL) +
    scale_fill_manual("Species", values = c("S. cerevisiae" = "yellowgreen", "C. glabrata" = "royalblue3")) +
    labs(x = expression(paste(italic("S. cerevisiae"), " gene names")),
         y = "log2 Fold Change") +
    #coord_flip() + 
    theme_bw(base_size = 12) + 
    theme(legend.text = element_text(face = 3), 
          legend.position = c(0.8, 0.8)) +
    background_grid(major = "y", minor = "none")
  return(p)
}
[1] "antioxidant"
[1] "The following S. cerevisiae genes do not have a C. glabrata ortholog."
[1] "YGR088W" "YBR117C" "YCL035C" "YGR209C"
[1] "protein degradation"
[1] "chaperon"
[1] "The following S. cerevisiae genes do not have a C. glabrata ortholog."
[1] "YPL240C" "YAL005C"

LS0tCnRpdGxlOiAiUGxvdCB0aGUgZXhwcmVzc2lvbiBvZiBQaSBzdGFydmFpdG9uIGluZHVjZWQgZ2VuZXMgcmVsZXZhbnQgZm9yIG94aWRhdGl2ZSBzdHJlc3MsIGluIF9DLiBnbGFicmF0YV8iCmF1dGhvcjogIkJpbiBIZSIKZGF0ZTogIjIwMjItMDQtMjggKHVwZGF0ZWQgYHIgU3lzLkRhdGUoKWApIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKYGBge3IgbG9hZF9saWJyYXJ5LCBlY2hvPUZBTFNFfQojIGluc3RhbGwgdGhlIHBhY2thZ2UgaWYgbm90IGFscmVhZHkgYXZhaWxhYmxlCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh0aWR5dmVyc2UpKQojc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGRhdGEudGFibGUpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoY293cGxvdCkpCnN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ3RleHQpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoU3VtbWFyaXplZEV4cGVyaW1lbnQpKQpgYGAKCiMjIEdvYWwKUGxvdCB0aGUgaW5kdWN0aW9uIG9mIG94aWRhdGl2ZSBzdHJlc3MgcmVzcG9uc2UgZ2VuZXMgaW4gX0MuIGdsYWJyYXRhXyBkdXJpbmcgcGhvc3BoYXRlIHN0YXJ2YXRpb24uIEdlbmVzIGtub3duIHRvIGJlIGluZHVjZWQgZHVyaW5nIGh5ZHJvZ2VuIHBlcm94aWRlIHN0cmVzcyBpbiBfUy4gY2VyZXZpc2lhZV8gYXJlIHRoZSBiYXNpcyBmb3Igc2VsZWN0aW5nIHRoZSBjYW5kaWF0ZSBnZW5lIHNldC4KCiMjIERhdGEKIyMjIF9DLiBnbGFicmF0YV8gLVBpIHRpbWUgY291cnNlCkxvYWQgbm9ybWFsaXplZCBnZW5lIGNvdW50cyBmb3IgdGhlIC1QaSB0aW1lIGNvdXJzZSBpbiBfQy4gZ2xhYnJhdGFfLiBEYXRhIGNvbGxlY3RlZCBpbiAyMDE3IGJ5IEJpbiBIZS4gUHJlLXByb2Nlc3Npbmcgd2FzIGRvbmUgYXMgZGVzY3JpYmVkIHVuZGVyIHRoZSBFMDA5L3NlcS1yZXN1bHRzIGZvbGRlci4KCkJlbG93IGlzIGEgc2ltcGxlIGNoYXJ0IHRvIHNob3cgdGhlIGRlc2lnbjoKCnwgR2Vub3R5cGUgfCBUaW1lIHBvaW50ICB8IFJlcGxpY2F0ZXMgfCBDb21tZW50IHwKfCAtLS0tLS0tLSB8IDotLS0tLS0tLS0tIHwgOi0tLS0tLS0tOiB8IC0tLS0tLS0gfAp8IDgw4oiGICAgICAgfCByaWNoIG1lZGlhICB8IDIgICAgICAgICAgfCBmb3IgY29uc2lzdGVuY3kgY2hlY2sgd2l0aCBteSBwcmV2aW91cyBkYXRhIHwKfCA4MOKIhiA04oiGICAgfCByaWNoIG1lZGlhICB8IDIgICAgICAgICAgfCBmb3IgY29uc2lzdGVuY3kgY2hlY2sgd2l0aCBteSBwcmV2aW91cyBkYXRhIHwKfCB3dC0xICAgICB8IHByZSwgMjAnLCAzMCcsIDQ1JywgNjAnLCA5MCcsIDEyMCcsIDE1MCcsIDE4MCcsIDI0MCcgfCAxIHwgdGltZSBjb3Vyc2UgZm9yIHd0ICB8CnwgNOKIhi0xICAgICB8IHByZSwgMjAnLCAzMCcsIDQ1JywgNjAnLCA5MCcsIDEyMCcsIDE1MCcsIDE4MCcsIDI0MCcgfCAxIHwgdGltZSBjb3Vyc2UgZm9yIDTiiIYgIHwgCnwgd3QtMiAgICAgfCBwcmUsIDIwJywgMzAnLCA0NScsIDYwJywgICAgICAxMjAnLCAgICAgICAxODAnLCAyNDAnIHwgMSB8IGJpb2wuIHJlcGwuIGZvciB3dC0xfAp8IDTiiIYtMiAgICAgfCBwcmUsIDIwJywgMzAnLCA0NScsIDYwJywgICAgICAxMjAnLCAgICAgICAxODAnICAgICAgIHwgMSB8IGJpb2wuIHJlcGwuIGZvciA04oiGLTF8IAoKYGBge3J9CmxvYWQoIi4uL2lucHV0L0NnbGEtUGktSGUyMDE3LXVucHViL0UwMDktQ2dsYS1QaS10aW1lLWNvdXJzZS1ub3JtYWxpemVkLlJEYXRhIikKY2dUb1NjIDwtIGNnVG9TYyAlPiUgCiAgc2VsZWN0KENnbGFJRCA9IGNnaWQsIENnbGFOYW1lID0gY2duYW1lLCBTY2VySUQgPSBzY2lkLCBTY2VyTmFtZSA9IHNjbmFtZSkKYW5ubyA8LSBsZWZ0X2pvaW4oYW5ubywgY2dUb1NjLCBieSA9IGMoIkdlbmVJRCIgPSAiQ2dsYUlEIikpICU+JSAKICBzZWxlY3QoQ2dsYUlEID0gR2VuZUlELCBDZ2xhTmFtZSwgU2NlcklELCBTY2VyTmFtZSwgQ2hyLCBUeXBlLm9mLkdlbmUpCmBgYAoKQ29uc3RydWN0IGEgU3VtbWFyaXplZEV4cGVyaW1lbnQgb2JqZWN0CmBgYHtyfQpzZTAgPC0gU3VtbWFyaXplZEV4cGVyaW1lbnQoCiAgYXNzYXlzID0gbGNwbSwgCiAgcm93RGF0YSA9IGNvbHVtbl90b19yb3duYW1lcyhhbm5vLCAiQ2dsYUlEIiksCiAgY29sRGF0YSA9IGNvbHVtbl90b19yb3duYW1lcyhzYW1wbGUsICJTYW1wbGUiKQogICkKIyBzdWJzZXQgdGhlIGV4cGVyaW1lbnQgdG8gaW5jbHVkZSBqdXN0IHRoZSB3dCB0aW1lIGNvdXJzZQpzZSA8LSBzZTBbLCBzZTAkR2Vub3R5cGUgPT0gInd0IiAmIHNlMCRUaW1lcG9pbnQgIT0gImRlbDgwIl0KYGBgCgojIyMgX1MuIGNlcmV2aXNpYWVfIC1QaSAxaHIgZ2VuZSBleHByZXNzaW9uCkRlc2NyaXB0aW9uIGFuZCBjb2RlIGJlbG93IGNvcGllZC9hZGFwdGVkIGZyb20gYHVuY2F0LWFuYWx5c2VzL0NUQTEtaW5kdWN0aW9uLW5vUGlgCgpfRGF0YSBzb3VyY2VfCgp8IEdFTyMgfCBEZXNjcmlwdGlvbiB8IFJlZmVyZW5jZSB8CnwgLS0tLSB8IC0tLS0tLS0tLS0tIHwgLS0tLS0tLS0tIHwKfCBHU0UyMzU4MCB8IE1pY3JvYXJyYXkgZXhwcmVzc2lvbiBhbmFseXNlcyBvZiBfUy4gY2VyZXZpc2lhZV8gd3QsIF9waG804oiGXywgX3BobzLiiIZfIGluIHJpY2ggb3Igbm8gUGkgbWVkaWEsIHNhbXBsZWQgYXQgMWhyIHwgWmhvdSAmIE8nU2hlYSAyMDExIHwKCiAgICBaaG91IFgsIE/igJlTaGVhIEVLLiAyMDExLiBJbnRlZ3JhdGVkIGFwcHJvYWNoZXMgcmV2ZWFsIGRldGVybWluYW50cyBvZiBnZW5vbWUtd2lkZSBiaW5kaW5nIGFuZCBmdW5jdGlvbiBvZiB0aGUgdHJhbnNjcmlwdGlvbiBmYWN0b3IgUGhvNC4gTW9sIENlbGwgNDI6ODI24oCTODM2LgoKVGhlIGFuYWx5c2lzIGJlbG93IGlzIGluc3BpcmVkIGJ5IGEgR0VPcXVlcnkgd29ya3Nob3Agb2ZmZXJlZCBieSBKYXNvbiBSYXRjbGlmZiBhdCB0aGUgSW93YSBJbnN0aXR1dGUgb2YgSHVtYW4gR2VuZXRpY3MuIGBnZXRHRU8oKWAgd2lsbCByZXR1cm4gYW4gRXhwcmVzc2lvblNldCBvYmplY3QsIHdoaWNoIGlzIHRoZW4gY29udmVydGVkIGludG8gYSAiU3VtbWFyaXplZEV4cGVyaW1lbnQiIG9iamVjdCwgd2hpY2ggaXMgYSBtb3JlIG1vZGVybiBkYXRhIHN0cnVjdHVyZSB0aGF0IGlzIGVhc2llciB0byBkZWFsIHdpdGguCmBgYHtyfQojU3lzLnNldGVudigiVlJPT01fQ09OTkVDVElPTl9TSVpFIiA9IDEzMTA3MioxMCkgIyBpbmNyZWFzZSB0aGUgbG9jYWwgY2FjaGUgc2l6ZQpnc2UgPC0gR0VPcXVlcnk6OmdldEdFTyhmaWxlbmFtZSA9ICIuLi9pbnB1dC9TY2VyLVBpLVpob3UyMDExL0dTRTIzNTgwX3Nlcmllc19tYXRyaXgudHh0Lmd6IikgJT4lIAogIGFzKCJTdW1tYXJpemVkRXhwZXJpbWVudCIpCmBgYAoKVGhlIGV4cGVyaW1lbnRhbCBpbmZvcm1hdGlvbiBpcyBzdG9yZWQgaW4gdGhlIGBjb2xEYXRhYCBmaWVsZHMuIFRoZSBvbmVzIHdlIGFyZSBpbnRlcmVzdGVkIGluIGFyZToKYGBge3J9CmNvbERhdGEoZ3NlKSAlPiUgCiAgYXNfdGliYmxlKCkgJT4lCiAgZmlsdGVyKGdyZXBsKCJXaWxkIHR5cGUgbm8gdnMgaGlnaCBQaSBjb25kaXRpb25zIiwgdGl0bGUpIHwgZ3JlcGwoIkNvbXBhcmlzb24gMSQiLCB0aXRsZSkpICU+JSAKICBzZWxlY3QodGl0bGUsIGdlb19hY2Nlc3Npb24sIG9yZ2FuaXNtID0gb3JnYW5pc21fY2gxLCBzdHJhaW4gPSBjaGFyYWN0ZXJpc3RpY3NfY2gxLCAKICAgICAgICAgY29uZGl0aW9uX2NoMSA9IGNoYXJhY3RlcmlzdGljc19jaDEuMiwgbGFiZWxfY2gxLCBjb25kaXRpb25fY2gyID0gY2hhcmFjdGVyaXN0aWNzX2NoMi4yLCBsYWJlbF9jaDIpCmBgYAoKVGhlIGZpcnN0IHRocmVlIGFyZSBwYXJ0IG9mIHRoZSBtdXRhbnQgY3ljbGUgd2hpbGUgdGhlIGxhdHRlciBmb3VyIGFyZSBub3QuIFRoZSBsYXR0ZXIgZm91ciBhcmUgc2FpZCB0byBoYXZlIGluY29ycG9yYXRlZCBhIGR5ZSBzd2FwLCBhbHRob3VnaCBJIGNhbid0IHRlbGwgaG93IHRoZSBzd2FwIHdhcyBkb25lIGZyb20gdGhlIHRhYmxlIGFib3ZlLgoKU2VwYXJhdGVseSBleHRyYWN0IHRoZSB0d28gc3Vic2V0cyBhbmQgZXhhbWluZSB0aGVtIHNlcGFyYXRlbHkuCmBgYHtyfQpnc2UxIDwtIGdzZVssIGdyZXBsKCJDb21wYXJpc29uIDEkIiwgZ3NlJHRpdGxlKV0KZ3NlMiA8LSBnc2VbLCBncmVwbCgiV2lsZCB0eXBlIG5vIHZzIGhpZ2ggUGkgY29uZGl0aW9ucyIsIGdzZSR0aXRsZSldCmBgYAoKIyMjIE9TUiBnZW5lIGxpc3QgYmFzZWQgb24gX1MuIGNlcmV2aXNpYWVfIGxpdGVyYXR1cmUKQ29tcGlsZSBnZW5lIGxpc3Qga25vd24gdG8gYmUgaW52b2x2ZWQgaW4gdGhlIHJlc3BvbnNlIHRvIGh5ZHJvZ2VuIHBlcnhvaWRlLiBUaGlzIGxpc3QgaXMgbW9zdGx5IGJhc2VkIG9uIExlZSBfZXQgYWwuXyAxOTk5IChQTUlEOiAxMDM0NzE1NCkgYW5kIEhhc2FuIF9ldCBhbC5fIDIwMDIgKFBNSUQ6IDEyMTAwNTYyKQpgYGB7cn0KIyBtYW51YWxseSBjb21waWxlZCBkYXRhc2V0cwpzY2VyLm9zciA8LSByZWFkX2NzdigiLi4vaW5wdXQvZ2VuZS1saXN0L3MtY2VyZXZpc2lhZS1oeWRyb3Blcm94aWRlLXJlc3BvbnNlLWdlbmVzLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lCiAgbXV0YXRlKG9yaS5uYW1lID0gc3RyX3RvX3VwcGVyKG9yaS5uYW1lKSwgY29yLm5hbWUgPSBzdHJfdG9fdXBwZXIoY29yLm5hbWUpKQojIHRoZXNlIElEcyBhcmUgcmV0cmlldmVkIGZyb20gWWVhc3RNaW5lCnNjZXIuaWQgPC0gcmVhZF9jc3YoIi4uL2lucHV0L2dlbmUtbGlzdC9zLWNlcmV2aXNpYWUtaHlkcm9wZXJveGlkZS1yZXNwb25zZS1nZW5lLWlkLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkgJT4lIAogIG11dGF0ZShpbnB1dCA9IHN0cl90b191cHBlcihpbnB1dCkpCiMgbWVyZ2UKc2Nlci5vc3IgPC0gc2Nlci5pZCAlPiUgCiAgc2VsZWN0KGlucHV0LCBzY2VyLnN5cyA9IHNlY29uZGFyeUlkZW50aWZpZXIsIGZ1biA9IG5hbWUsIGxlbmd0aCkgJT4lIAogIGxlZnRfam9pbihzY2VyLm9zciwgYnkgPSBjKCJpbnB1dCIgPSAiY29yLm5hbWUiKSkgJT4lIAogIHNlbGVjdChzY2VyLmNvbW1vbiA9IGlucHV0LCAtb3JpLm5hbWUsIGV2ZXJ5dGhpbmcoKSkKYGBgCgojIyMgTWFwcGluZyBiZXR3ZWVuIHRoZSB0d28gZ2Vub21lcwoKTWFwcGluZyBiZXR3ZWVuIHRoZSB0d28gc3BlY2llcyBhcmUgZG93bmxvYWRlZCBmcm9tIFtDR0RdKHd3dy5jYW5kaWRhZ2Vub21lLm9yZykuIFR3byB0eXBlcyBvZiBtYXBwaW5ncyBhcmUgYXZhaWxhYmxlOiBbb3J0aG9sb2d5XShodHRwOi8vd3d3LmNhbmRpZGFnZW5vbWUub3JnL2Rvd25sb2FkL2hvbW9sb2d5L29ydGhvbG9ncy8pIGlzIGJhc2VkIG9uIHRoZSBbWWVhc3QgR2VuZSBPcmRlciBCcm93c2VyXShodHRwOi8veWdvYi51Y2QuaWUvKTsgW2Jlc3QgaGl0c10oaHR0cDovL3d3dy5jYW5kaWRhZ2Vub21lLm9yZy9kb3dubG9hZC9ob21vbG9neS9iZXN0X2hpdHMvKSBpcyBiYXNlZCBvbiBgYmxhc3RwYCBhbmQgaXMgb25seSBwZXJmb3JtZWQgZm9yIGdlbmVzIGxhY2tpbmcgYSBjcmVkaWJsZSBvcnRob2xvZy4gTm90ZSB0aGF0IGJlY2F1c2UgYm90aCBtYXBwaW5ncyBzdGFydCBmcm9tIF9DLiBnbGFicmF0YV8sIHRoZXkgYXJlIG5vdCB0aGUgaWRlYWwgbWFwcGluZyB0byB1c2UgZm9yIGlkZW50aWZ5aW5nIHRoZSBfQy4gZ2xhYnJhdGFfIG9ydGhvbG9nIGZvciBfUy4gY2VyZXZpc2lhZV8gZ2VuZXMsIGJ1dCBzaG91bGQgYmUgc3VmZmljaWVudCBmb3IgdGhlIGN1cnJlbnQgcHVycG9zZS4gVGhpcyBpcyBiZWNhdXNlIG1vc3Qgb2YgdGhlIGdlbmVzIGRvIGhhdmUgMS10by0xIG9ydGhvbG9ncyBiZXR3ZWVuIHRoZSB0d28gc3BlY2llcyBhbmQgdGh1cyBzdGFydGluZyBmcm9tIGVpdGhlciBzcGVjaWVzIHNob3VsZCB5aWVsZCB0aGUgc2FtZSBtYXBwaW5nLgoKQmVsb3cgb3VyIGdvYWwgaXMgdG8gcmV2ZXJzZS1lbmdpbmVlciBhbiBzY1RvQ2cgb25lLXRvLW9uZSBtYXBwaW5nIGZyb20gdGhlIHR3byBjZ1RvU2MgbWFwcGluZ3MuCmBgYHtyfQojIGNnVG9TYyBpcyBsb2FkZWQgYWxyZWFkeSBhbmQgc3RvcmVzIHRoZSBvcnRob2xvZ3kgbWFwcGluZwojIHJlYWQgdGhlIGJlc3QgaGl0IG1hcHBpbmcKY2dUb1NjLmJoIDwtIHJlYWRfdHN2KCIuLi9pbnB1dC9hbm5vdGF0aW9uL0NfZ2xhYnJhdGFfQ0JTMTM4X1NfY2VyZXZpc2lhZV9iZXN0X2hpdHMudHh0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSAiIyIsIGNvbF90eXBlcyA9IGNvbHMoKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IGMoIkNnbGFJRCIsIkNnbGFOYW1lIiwiQ2dsYS5pZCIsIlNjZXJJRCIsIlNjZXJOYW1lIiwiU2Nlci5pZCIpKSAlPiUgCiAgc2VsZWN0KENnbGFJRCwgQ2dsYU5hbWUsIFNjZXJJRCwgU2Nlck5hbWUpCnJlLnNjVG9DZyA8LSBiaW5kX3Jvd3MoCiAgb3J0aG9sb2cgPSBjZ1RvU2MsIAogICMgb25seSBpbmNsdWRlIHRoZSByb3dzIGluIHRoZSBiZXN0IGhpdCB0YWJsZSBpZiB0aGUgU2NlciBnZW5lIGlzIG5vdCBhbHJlYWR5IGluIHRoZSBvcnRob2xvZ3kgbWFwcGluZwogICMgZm9yIFNjZXIgZ2VuZXMgd2l0aCBtb3JlIHRoYW4gb25lIENnbGEgZ2VuZXMgbWFwcGVkIHRvIGl0IGJ5IGJsYXN0cCwgcmFuZG9tbHkgcGljayB0aGUgZmlyc3Qgb25lCiAgYGJlc3QgaGl0YCA9IGNnVG9TYy5iaCAlPiUgZmlsdGVyKCFTY2VySUQgJWluJSBjZ1RvU2MkU2NlcklEKSAlPiUgZ3JvdXBfYnkoU2NlcklEKSAlPiUgZmlsdGVyKHJvd19udW1iZXIoKSA9PSAxKSwKICAuaWQgPSAibWFwcGluZyIpCmBgYAoKQ29tYmluZSB0aGUgb3J0aG9sb2d5IGFuZCBiZXN0IGhpdHMgbWFwcGluZwpgYGB7cn0KY2dsYS5saXN0IDwtIHNjZXIub3NyICU+JSAKICBkcGx5cjo6cmVuYW1lKFNjZXJJRCA9IHNjZXIuc3lzLCBTY2VyTmFtZSA9IHNjZXIuY29tbW9uKSAlPiUKICBsZWZ0X2pvaW4oY2dUb1NjICU+JSBzZWxlY3QoQ2dsYUlELm9yID0gQ2dsYUlELCBDZ2xhTmFtZS5vciA9IENnbGFOYW1lLCBTY2VySUQsIFNjZXJOYW1lKSkgJT4lIAogICMgbm90ZSB0aGF0IHRoZSBmb2xsb3dpbmcgaXMgbm90IGEgcm9idXN0IHByb2NlZHVyZS4gaXQganVzdCBoYXBwZW5zIHRoYXQgbm9uZSBvZiB0aGUgU2NlciBnZW5lcyBpbiAKICAjIHNjZXIub3NyIGhhdmUgbW9yZSB0aGFuIG9uZSBiZXN0IGhpdCBtYXBwZWQgQ2dsYSBnZW5lcwogIGxlZnRfam9pbihjZ1RvU2MuYmggJT4lIHNlbGVjdChDZ2xhSUQuYmggPSBDZ2xhSUQsIENnbGFOYW1lLmJoID0gQ2dsYU5hbWUsIFNjZXJJRCwgU2Nlck5hbWUpKSAlPiUgCiAgbXV0YXRlKAogICAgQ2dsYUlEID0gaWZlbHNlKGlzLm5hKENnbGFJRC5vciksIENnbGFJRC5iaCwgQ2dsYUlELm9yKSwKICAgIENnbGFOYW1lID0gaWZlbHNlKGlzLm5hKENnbGFOYW1lLm9yKSwgQ2dsYU5hbWUuYmgsIENnbGFOYW1lLm9yKQogICkgJT4lIAogIHNlbGVjdChzdGFydHNfd2l0aCgiQ2dsYSIpLCBldmVyeXRoaW5nKCkpCmBgYAoKCiMjIEFuYWx5c2lzCiMjIyBQbG90dGluZyBfQy4gZ2xhYnJhdGFfIHRpbWUgY291cnNlCmBgYHtyfQpteUdlbmVzUGxvdCA8LSBmdW5jdGlvbihnZW5lcyA9ICJDQUdMMEIwMjQ3NWciLCBuYW1lcyA9ICJQTVUyIikgewogICMgdGhpcyBmdW5jdGlvbiB0YWtlcyBpbiB0aGUgcmVhZCBjb3VudCBtYXRyaXggKG5vcm1hbGl6ZWQgYW5kIHRyYW5zZm9ybWVkKSBhbmQgYSBsaXN0IG9mIGdlbmUgSURzCiAgIyBhbmQgcGxvdHMgdGhlIHZhbHVlcyBzdHJhdGlmaWVkIGJ5IGdlbm90eXBlIGFuZCB0aW1lcG9pbnQKICBpZihhbnkoIWdlbmVzICVpbiUgcm93bmFtZXMoc2UpKSl7CiAgICBwcmludCgiVGhlIGZvbGxvd2luZyBnZW5lcyBhcmUgbm90IGluY2x1ZGVkIGluIHRoZSBleHBlcmltZW50LiBDaGVjayB0byBtYWtlIHN1cmUgdGhhdCB0aGUgZ2VuZSBuYW1lcyBhcmUgY29ycmVjdC4iKQogICAgc2V0ZGlmZihnZW5lcywgcm93bmFtZXMoc2UpKQogICAgc3RvcCgiR2VuZSBuYW1lcyBub3QgaW5jbHVkZWQgaW4gdGhlIGV4cGVyaW1lbnQiKQogIH0KICAKICBpZighaXMubnVsbChuYW1lcyhnZW5lcykpKXsKICAgIG5hbWVzID0gbmFtZXMoZ2VuZXMpCiAgfQogIGVsc2UgaWYobGVuZ3RoKGdlbmVzKSAhPSBsZW5ndGgobmFtZXMpKXsKICAgIHN0b3AoImxlbmd0aCBvZiAnZ2VuZXMnIG11c3QgZXF1YWwgbGVuZ3RoIG9mICduYW1lcyciKQogIH0KICAKICAKICAjIGNvbnN0cnVjdCB0aWJibGUgZm9yIHBsb3R0aW5nCiAgZGF0IDwtIHQoYXNzYXkoc2VbZ2VuZXMsXSkpICU+JSBhc190aWJibGUocm93bmFtZXMgPSBOQSkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAic2FtcGxlIikgIyBzdWJzZXQgdGhlIGV4cHJlc3Npb24gbWF0cml4CiAgdGIgPC0gY29sRGF0YShzZSkgJT4lIGFzX3RpYmJsZShyb3duYW1lcyA9IE5BKSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4odmFyID0gInNhbXBsZSIpICU+JSAKICAgIHNlbGVjdChzYW1wbGUsIEdlbm90eXBlLCBUaW1lcG9pbnQpICU+JQogICAgbGVmdF9qb2luKGRhdCwgYnkgPSAic2FtcGxlIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiQ0FHTCIpLCBuYW1lc190byA9ICJnZW5lIiwgdmFsdWVzX3RvID0gImxvZzIgY3BtIikgJT4lIAogICAgbGVmdF9qb2luKHRpYmJsZShnZW5lID0gZ2VuZXMsIG5hbWUgPSBuYW1lcyksIGJ5ID0gImdlbmUiKQogICAgCiAgcCA8LSBnZ3Bsb3QodGIsIGFlcyh4ID0gVGltZXBvaW50LCB5ID0gYGxvZzIgY3BtYCkpICsgCiAgICBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBmb3JtdWxhID0geSB+IHgsIGFlcyh4ID0gYXMubnVtZXJpYyhUaW1lcG9pbnQpLCB5ID0gYGxvZzIgY3BtYCkpICsgCiAgICB4bGFiKCIiKSArIHlsYWIoImxvZzIgY291bnQgcGVyIG1pbGxpb24iKSArIGZhY2V0X3dyYXAofnBhc3RlKGdlbmUsIG5hbWUsIHNlcCA9ICI6IiksIHNjYWxlID0gImZyZWVfeSIpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEyKSArIAogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgc2l6ZSA9IHJlbCgwLjc1KSwgaGp1c3QgPSAxKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC43NSkpKQogIHJldHVybihwKQp9CmBgYAoKYGBge3J9Cmdyb3VwIDwtIGMoImFudGlveGlkYW50IiwgInByb3RlaW4gZGVncmFkYXRpb24iLCAiY2hhcGVyb24iKQpjYXRlZ29yaWVzIDwtIGxhcHBseShncm91cCwgZnVuY3Rpb24oeCl7CiAgdGVzdCA8LSBjZ2xhLmxpc3QgJT4lIAogICAgZmlsdGVyKGNhdGVnb3J5ID09IHgpICU+JSAKICAgIHNlbGVjdChTY2VyTmFtZSwgQ2dsYUlEKSAlPiUgCiAgICBmaWx0ZXIoIWlzLm5hKENnbGFJRCkpICU+JSAKICAgIGRlZnJhbWUoKQp9KQpuYW1lcyhjYXRlZ29yaWVzKSA8LSBncm91cApjYXRlZ29yaWVzJGFudGlveGlkYW50IDwtIGMoY2F0ZWdvcmllcyRhbnRpb3hpZGFudCwgQ1RUMSA9ICJDQUdMMEsxMDg2OGciLCBUU0EyID0gIkNBR0wwSzA2MjU5ZyIpCmZvciAoaSBpbiBzZXFfYWxvbmcoY2F0ZWdvcmllcykpewogIHAgPC0gbXlHZW5lc1Bsb3QoY2F0ZWdvcmllc1tbaV1dKQogIHByaW50KHAgKyBnZ3RpdGxlKHN0cl90b191cHBlcihncm91cFtpXSkpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpKQp9CmBgYAoKCiMjIyBQbG90dGluZyBfUy4gY2VyZXZpc2lhZV8gLVBpIGF0IDFocgoKVG8gY29tcGFyZSBPU1IgZ2VuZXMgdW5kZXIgcGhvcHNoYXRlIHN0YXJ2YXRpb24gaW4gX0MuIGdsYWJyYXRhXyB2cyBpbiBfUy4gY2VyZXZpc2lhZV8sIEkgZG93bmxvYWRlZCBYdSdzIDIwMTEgZGF0YSwgd2hpY2ggd2FzIG1lYXN1cmVkIHVzaW5nIHR3by1jb2xvciBtaWNyb2FycmF5IGF0IDFociBwb3N0IHN0YXJ2YXRpb24uIEluIHRoZSBmdXR1cmUgSSBwbGFuIHRvIHVzZSBHdXJ2aWNoIDIwMTcsIHdoaWNoIGNvbnRhaW5zIHRoZSB0aW1lIGNvdXJzZSBkYXRhIGJ1dCByZXF1aXJlcyBtb3JlIHByb2Nlc3NpbmcuCgpGaXJzdCwgd2UgbmVlZCB0byBhc3NlbWJsZSB0aGUgZGF0YXNldC4gVGhlIGlkZWEgaXMgdG8gY2FsY3VsYXRlIHRoZSBsb2cyRkMgYmV0d2VlbiAxaHIgYW5kIHByZS1zdHJlc3MgdGltZSBwb2ludHMgZnJvbSB0aGUgX0MuIGdsYWJyYXRhXyBkYXRhc2V0LiBMYXRlciB3ZSB3aWxsIG1lcmdlIGl0IHdpdGggdGhlIF9TLiBjZXJldmlzaWFlXyBkYXRhLiBUaGUgZmluYWwgZGF0YSBzaG91bGQgaW5jbHVkZSBgU3BlY2llcyB8IE5hbWUgfCBJRCB8IGxvZzJGQ2AsIGluIHdoaWNoIGBOYW1lYCBpcyBhbHdheXMgZnJvbSBfUy4gY2VyZXZpc2lhZV8gYW5kIGBJRGAgZnJvbSB0aGUgcmVzcGVjdGl2ZSBzcGVjaWVzLgpgYGB7cn0KdG1wIDwtIGFzc2F5KHNlWywgc2UkVGltZXBvaW50ICVpbiUgYygicHJlIiwgIjYwbSIpXSkKY29sbmFtZXModG1wKSA8LSBjKCJwcmUuMSIsInByZS4yIiwiNjBtLjEiLCI2MG0uMiIsIjYwbS4zIikKY2dsYS4xaHIgPC0gYXNfdGliYmxlKHRtcCwgcm93bmFtZXMgPSAiQ2dsYUlEIikgJT4lIAogIG11dGF0ZSgKICAgIGJhc2UgPSAocHJlLjEgKyBwcmUuMikvMiwKICAgIG02MC4xID0gYDYwbS4xYCAtIGJhc2UsCiAgICBtNjAuMiA9IGA2MG0uMmAgLSBiYXNlLAogICAgbTYwLjMgPSBgNjBtLjNgIC0gYmFzZQogICkgJT4lIAogIHNlbGVjdChDZ2xhSUQsIHN0YXJ0c193aXRoKCJtNjAiKSkgJT4lIAogIHBpdm90X2xvbmdlcihtNjAuMTptNjAuMywgbmFtZXNfdG8gPSBOVUxMLCB2YWx1ZXNfdG8gPSAibG9nMkZDIikKYGBgCgpXaXRoIHRoZSBDZ2xhIGRhdGEgY2FsY3VsYXRlZCwgd2UgY2FuIHdyaXRlIGEgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIGxpc3Qgb2YgX1MuIGNlcmV2aXNpYWVfIGdlbmUgbmFtZXMgYXMgaW5wdXQgYW5kIHBsb3QgdGhlIGxvZ0ZDIGluIGJvdGggc3BlY2llcy4KYGBge3J9Cm15Q29tcGFyZSA8LSBmdW5jdGlvbihnZW5lcyA9ICJZTUwxMjNDIil7CiAgIyBjaGVjayBnZW5lIG5hbWVzIGFnYWluc3QgdGhlIFMuIGNlcmV2aXNpYWUgbWljcm9hcnJheSBhbm5vdGF0aW9uCiAgc2NlckFycmF5IDwtIHJvd0RhdGEoZ3NlKSRPUkYKICBuYW1lcyhzY2VyQXJyYXkpIDwtIHJvd0RhdGEoZ3NlKSROYW1lCiAgaWYoYWxsKCFnZW5lcyAlaW4lIHNjZXJBcnJheSkpewogICAgc3RvcCgiQ2hlY2sgT1JGIG5hbWVzLiBOb25lIGFyZSBwcmVzZW50IGluIHRoZSBhbm5vdGF0aW9uIGZpbGUuIikKICB9ZWxzZSBpZihhbnkoIWdlbmVzICVpbiUgc2NlckFycmF5KSl7CiAgICBwcmludCgiVGhlIGZvbGxvd2luZyBTLiBjZXJldmlzaWFlIGdlbmVzIGFyZSBub3QgaW5jbHVkZWQgb24gdGhlIG1pY3JvYXJyYXkgY2hpcC4iKQogICAgcHJpbnQoc2V0ZGlmZihnZW5lcywgc2NlckFycmF5KSkKICB9ZWxzZSBpZihhbnkoIWdlbmVzICVpbiUgcmUuc2NUb0NnJFNjZXJJRCkpewogICAgcHJpbnQoIlRoZSBmb2xsb3dpbmcgUy4gY2VyZXZpc2lhZSBnZW5lcyBkbyBub3QgaGF2ZSBhIEMuIGdsYWJyYXRhIG9ydGhvbG9nLiIpCiAgICBwcmludChzZXRkaWZmKGdlbmVzLCByZS5zY1RvQ2ckU2NlcklEKSkKICB9CiAgZ2VuZVNldCA8LSByb3dEYXRhKGdzZSkgJT4lIAogICAgYXNfdGliYmxlKHJvd25hbWVzID0gTlVMTCkgJT4lIAogICAgZmlsdGVyKE9SRiAlaW4lIGdlbmVzKSAlPiUgCiAgICBzZWxlY3QoU2NlcklEID0gT1JGKSAlPiUgCiAgICBpbm5lcl9qb2luKHJlLnNjVG9DZywgYnkgPSAiU2NlcklEIikKICAKICAjIGV4dHJhY3QgU2NlciBkYXRhCiAgcHJvYmVzIDwtIHJvd0RhdGEoZ3NlMSkgJT4lIGFzX3RpYmJsZSgpICU+JSBmaWx0ZXIoT1JGICVpbiUgZ2VuZVNldCRTY2VySUQpICU+JSBzZWxlY3QoSUQsIE9SRikKICBzY2VyRGF0IDwtIGFzc2F5KGdzZTEpW3Byb2JlcyRJRCxdICU+JSAKICAgIGFzX3RpYmJsZShyb3duYW1lcyA9ICJJRCIpICU+JSAKICAgIGxlZnRfam9pbihwcm9iZXMsIGJ5ID0gIklEIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiR1NNIiksIG5hbWVzX3RvID0gTlVMTCwgdmFsdWVzX3RvID0gImxvZzJGQyIpICU+JSAKICAgIHNlbGVjdChTY2VySUQgPSBPUkYsIGxvZzJGQykgJT4lIAogICAgbGVmdF9qb2luKHNlbGVjdChnZW5lU2V0LCBTY2VySUQsIFNjZXJOYW1lKSwgYnkgPSAiU2NlcklEIikgJT4lIAogICAgc2VsZWN0KFNjZXJOYW1lLCBJRCA9IFNjZXJJRCwgbG9nMkZDKQogICMgZXh0cmFjdCBDZ2xhIGRhdGEKICBjZ2xhRGF0IDwtIGNnbGEuMWhyICU+JSAKICAgIGZpbHRlcihDZ2xhSUQgJWluJSBuYS5vbWl0KGdlbmVTZXQkQ2dsYUlEKSkgJT4lIAogICAgbGVmdF9qb2luKHNlbGVjdChnZW5lU2V0LCBDZ2xhSUQsIFNjZXJOYW1lKSwgYnkgPSAiQ2dsYUlEIikgJT4lIAogICAgc2VsZWN0KFNjZXJOYW1lLCBJRCA9IENnbGFJRCwgbG9nMkZDKQogIERhdCA8LSBiaW5kX3Jvd3MoYFMuIGNlcmV2aXNpYWVgID0gc2NlckRhdCwgYEMuIGdsYWJyYXRhYCA9IGNnbGFEYXQsIC5pZCA9ICJzcGVjaWVzIikgJT4lCiAgICAjIHNvcnQgdGhlIGdlbmUgbGlzdCBieSBDZ2xhIGluZHVjdGlvbiBtZWFuCiAgICBtdXRhdGUoU2Nlck5hbWUgPSBmb3JjYXRzOjpmY3RfcmVvcmRlcihTY2VyTmFtZSwgbG9nMkZDLCBtZWFuLCAuZGVzYyA9IFRSVUUpKQogIAogICMgcGxvdHRpbmcKICBwIDwtIGdncGxvdChEYXQsIGFlcyh4ID0gU2Nlck5hbWUsIHkgPSBsb2cyRkMsIGdyb3VwID0gc3BlY2llcykpICsKICAgIGdlb21fYmFyKGFlcyhmaWxsID0gc3BlY2llcyksIHN0YXQgPSAic3VtbWFyeSIsIGZ1biA9ICJtZWFuIiwgYWxwaGEgPSAwLjksCiAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gc3BlY2llcyksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC45KSwgc2l6ZSA9IDEpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMSwgY29sb3IgPSAiZ3JheTIwIikgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbGluZXR5cGUgPSAyLCBjb2xvciA9ICJncmF5NTAiKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiUy4gY2VyZXZpc2lhZSIgPSAiZ3JheTMwIiwgIkMuIGdsYWJyYXRhIiA9ICJvcmFuZ2UyIiksIGd1aWRlID0gTlVMTCkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwoIlNwZWNpZXMiLCB2YWx1ZXMgPSBjKCJTLiBjZXJldmlzaWFlIiA9ICJ5ZWxsb3dncmVlbiIsICJDLiBnbGFicmF0YSIgPSAicm95YWxibHVlMyIpKSArCiAgICBsYWJzKHggPSBleHByZXNzaW9uKHBhc3RlKGl0YWxpYygiUy4gY2VyZXZpc2lhZSIpLCAiIGdlbmUgbmFtZXMiKSksCiAgICAgICAgIHkgPSAibG9nMiBGb2xkIENoYW5nZSIpICsKICAgICNjb29yZF9mbGlwKCkgKyAKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDEyKSArIAogICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9IDMpLCAKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjgpKSArCiAgICBiYWNrZ3JvdW5kX2dyaWQobWFqb3IgPSAieSIsIG1pbm9yID0gIm5vbmUiKQogIHJldHVybihwKQp9CmBgYAoKYGBge3IgcGxvdF9nZW5lLCBlY2hvPUZBTFNFfQpnZW5lTGlzdCA8LSBsYXBwbHkoZ3JvdXAsIGZ1bmN0aW9uKHgpewogIHNjZXIub3NyICU+JSBmaWx0ZXIoY2F0ZWdvcnkgPT0geCkgJT4lIHB1bGwoc2Nlci5zeXMpCn0pCm5hbWVzKGdlbmVMaXN0KSA8LSBncm91cAojIGFkZCBDVEExLCBQUlgxIGFuZCBUUlgzLCB3aGljaCBhcmUgT1NSLXJlbGF0ZWQgYW5kIGluZHVjZWQgaW4gQy4gZ2xhYnJhdGEgYnV0IG5vdCBpbiBTLiBjZXJldmlzaWFlCiMgcmVtb3ZlIENZUzMgYW5kIFRTQTIsIGJvdGggb2Ygd2hpY2ggYXBwZWFyIHRvIGJlIGRvd24gdW5kZXIgLVBpIGluIGJvdGggc3BlY2llcwpnZW5lTGlzdCRhbnRpb3hpZGFudCA8LSBzZXRkaWZmKGMoZ2VuZUxpc3QkYW50aW94aWRhbnQsICJZRFIyNTZDIiwgIllCTDA2NEMiLCAiWUNSMDgzVyIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJZRFI0NTNDIiwgIllBTDAxMlciKSkKZm9yIChpIGluIHNlcV9hbG9uZyhnZW5lTGlzdCkpewogIHByaW50KGdyb3VwW2ldKQogIHAgPC0gbXlDb21wYXJlKGdlbmVMaXN0W1tpXV0pCiAgcHJpbnQocCArIGdndGl0bGUoc3RyX3RvX3VwcGVyKGdyb3VwW2ldKSkgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkpCiAgZ2dzYXZlKHBhc3RlMCgiLi4vb3V0cHV0LyIsIGdzdWIoIi0iLCAiIiwgU3lzLkRhdGUoKSksICItY29tcGFyZS0iLCBncm91cFtpXSwgIi1nZW5lLWluZHVjdGlvbi1pbi1uby1QaS5wZGYiKSwKICAgICAgICAgd2lkdGggPSA2LCBoZWlnaHQgPSA0KQp9CmBgYAo=