Miriam Yeung

Modified version of material from the University of Cambridge Bioinformatics Training Unit.

4. Automation in R

Motivation

Typically when we perform an analysis we need to perform the same steps multiple times, for example, over multiple genes, samples, datasets.

With the basic analyses and graphs that we have created in the previous sessions it is possible to copy and paste the relevant section of code and adjust the code to produce multiple graphs.

However, take the scenarios where you want to:

  1. Count the number of genes expressed in the patients for multiple chromosomes
  2. Create boxplots of expression in ER -ve versus ER +ve patients for multiple genes
  3. Creating boxplots for multiple genes if expression is different in ER +ve versus ER-ve

First lets load in the libraries we’ll need.

library(dplyr)
library(ggplot2)
library(RColorBrewer)

Lets also load in the data.

patients <-  read.delim("updated.patient.txt", stringsAsFactors = FALSE)
exprsAnno <- read.delim("anno.gene.expression.txt", stringsAsFactors = FALSE)

You can check the dimensions of the objects, to check the numbers of rows and columns they contain.

dim(patients)
[1] 168   6
dim(exprsAnno)
[1] 342 171

Take a look at the files to check what they contain and as a sanity check that all looks ok.

View(patients)
View(exprsAnno)

You can check the column names.

colnames(patients)
[1] "samplename" "age"        "er"         "grade"      "her2"      
[6] "pr"        
colnames(exprsAnno)
  [1] "NKI_154"          "NKI_199"          "NKI_35"          
  [4] "NKI_99"           "NKI_201"          "NKI_138"         
  [7] "NKI_256"          "NKI_71"           "NKI_378"         
 [10] "NKI_229"          "NKI_172"          "NKI_273"         
 [13] "NKI_383"          "NKI_276"          "NKI_321"         
 [16] "NKI_260"          "NKI_125"          "NKI_226"         
 [19] "NKI_294"          "NKI_324"          "NKI_120"         
 [22] "NKI_393"          "NKI_78"           "NKI_44"          
 [25] "NKI_297"          "NKI_214"          "NKI_140"         
 [28] "NKI_347"          "NKI_236"          "NKI_34"          
 [31] "NKI_72"           "NKI_380"          "NKI_23"          
 [34] "NKI_193"          "NKI_197"          "NKI_288"         
 [37] "NKI_27"           "NKI_29"           "NKI_221"         
 [40] "NKI_85"           "NKI_327"          "NKI_286"         
 [43] "NKI_390"          "NKI_355"          "NKI_354"         
 [46] "NKI_4"            "NKI_341"          "NKI_402"         
 [49] "NKI_51"           "NKI_280"          "NKI_157"         
 [52] "NKI_94"           "NKI_70"           "NKI_391"         
 [55] "NKI_239"          "NKI_333"          "NKI_150"         
 [58] "NKI_217"          "NKI_109"          "NKI_180"         
 [61] "NKI_404"          "NKI_135"          "NKI_302"         
 [64] "NKI_48"           "NKI_147"          "NKI_7"           
 [67] "NKI_243"          "NKI_222"          "NKI_365"         
 [70] "NKI_188"          "NKI_122"          "NKI_346"         
 [73] "NKI_304"          "NKI_31"           "NKI_60"          
 [76] "NKI_169"          "NKI_205"          "NKI_107"         
 [79] "NKI_148"          "NKI_39"           "NKI_131"         
 [82] "NKI_241"          "NKI_318"          "NKI_371"         
 [85] "NKI_79"           "NKI_287"          "NKI_271"         
 [88] "NKI_314"          "NKI_149"          "NKI_113"         
 [91] "NKI_265"          "NKI_61"           "NKI_91"          
 [94] "NKI_335"          "NKI_358"          "NKI_369"         
 [97] "NKI_98"           "NKI_307"          "NKI_284"         
[100] "NKI_374"          "NKI_401"          "NKI_123"         
[103] "NKI_106"          "NKI_336"          "NKI_215"         
[106] "NKI_110"          "NKI_38"           "NKI_86"          
[109] "NKI_292"          "NKI_181"          "NKI_363"         
[112] "NKI_366"          "NKI_62"           "NKI_183"         
[115] "NKI_348"          "NKI_126"          "NKI_184"         
[118] "NKI_190"          "NKI_325"          "NKI_129"         
[121] "NKI_359"          "NKI_381"          "NKI_403"         
[124] "NKI_357"          "NKI_161"          "NKI_128"         
[127] "NKI_104"          "NKI_338"          "NKI_136"         
[130] "NKI_296"          "NKI_59"           "NKI_218"         
[133] "NKI_245"          "NKI_203"          "NKI_319"         
[136] "NKI_254"          "NKI_251"          "NKI_295"         
[139] "NKI_283"          "NKI_146"          "NKI_166"         
[142] "NKI_385"          "NKI_281"          "NKI_95"          
[145] "NKI_356"          "NKI_308"          "NKI_97"          
[148] "NKI_322"          "NKI_186"          "NKI_328"         
[151] "NKI_275"          "NKI_270"          "NKI_11"          
[154] "NKI_114"          "NKI_185"          "NKI_195"         
[157] "NKI_272"          "NKI_361"          "NKI_264"         
[160] "NKI_345"          "NKI_360"          "NKI_237"         
[163] "NKI_213"          "NKI_191"          "NKI_323"         
[166] "NKI_337"          "NKI_187"          "NKI_298"         
[169] "HUGO.gene.symbol" "Chromosome"       "Start"           

patients has 6 columns for samplename, age, er, grade, her2, pr, for 168 patients. We can use this object to get ER/PR/HER2 status of the patients.

exprsAnno has the expression information for the 168 patients (who all have an ID that starts with “NKI”), followed by columns for gene symbol, chromosome and start position (HUGO.gene.symbol, Chromosome, Start).

Automation Example 1 - Counting genes for multiple chromosomes

We want to identify the number of the genes in the dataset for Chromosomes 1, 5, 6, 8.

First lets identify the number on chromosome 1. We can use dplyr’s filter and summarise functions. We can use summarise(n()) to get a count of rows.

exprsAnno %>%
    filter(Chromosome == "chr1") %>%
    summarise(n())

Note that tally() is a convenient wrapper function you can use instead of summarise(n())

exprsAnno %>%
    filter(Chromosome == "chr1") %>%
    tally()

That gives us the count for one chromosome (chr1). Now how do we identify the number of genes for multiple chromosomes, chromosomes 1, 5, 6 and 8?

We could copy and paste the code like below.

exprsAnno %>%
    filter(Chromosome == "chr1") %>%
    summarise(n())
exprsAnno %>%
    filter(Chromosome == "chr5") %>%
    summarise(n())
exprsAnno %>%
    filter(Chromosome == "chr6") %>%
    summarise(n())
exprsAnno %>%
    filter(Chromosome == "chr8") %>%
    summarise(n())

But as you can imagine, the more lines of code that need to be copied, pasted and edited, the more likely it is that errors will arise.

This method of copying, pasting and editing can be:

1. Tedious
2. Erroneous 

Automating Commands: Loops and flow control

  • Many programming languages have ways of doing the same thing many times, perhaps changing some variable each time. This is called looping. It is a way to automate tasks.

  • As we are doing the same thing multiple times, but with a different chromosome each time, we can use a loop instead
  • R has two basic types of loop
    • a while loop: run some code while some condition is true (hardly ever used! Therefore will not be covered)
    • a for loop: run some code on every value in a vector

for The basic structure of a for loop:

for (element in vector){
    ... do this ...
}
  • Therefore the code is only run for as many elements are in the vector. Therefore we can predict the number of times the code is run.

  • Note: element and vector are just variable names and can therefore be named whatever you want them to be, as long as they satisfy the constraints on varible naming.

  • Here’s how we might use a for loop to find out the number of genes on each chromosome. We store the count in a variable called numRows so we can then print it out.

chrom <- c("chr1", "chr5", "chr6", "chr8")
for (chr in chrom){
    numRows <- exprsAnno %>%
        filter(Chromosome == chr) %>%
        summarise(n())
    s <- paste("The number of genes on", chr, "is", numRows)
    print(s)
}
[1] "The number of genes on chr1 is 35"
[1] "The number of genes on chr5 is 16"
[1] "The number of genes on chr6 is 20"
[1] "The number of genes on chr8 is 12"
  • The above for loop finds out the same information that we identified earlier by copying/pasting/editing
  • To more accurately depict the operations of a for loop, the commands being run are similar to the following:
chr <- "chr1"
numRows <- exprsAnno %>% filter(Chromosome == chr) %>% summarise(n())
chr <- "chr5"
numRows <- exprsAnno %>% filter(Chromosome == chr) %>% summarise(n())
chr <- "chr6"
numRows <- exprsAnno %>% filter(Chromosome == chr) %>% summarise(n())
chr <- "chr8"
numRows <- exprsAnno %>% filter(Chromosome == chr) %>% summarise(n())

Exercise

Using the patients object, find the number of patients who are aged above 20, 30, 40, and print the answer to the console.

ages <- c(20, 30, 40)
### Insert your code here ###

Automation Example 2 - Creating boxplots for multiple genes

Whilst the first example only contains 3 lines of code, and therefore is less likely to lead to errors, imagine that you want to create multiple boxplots for the expression values of the following genes in ER -ve and ER +ve patients.

- FOXE1  
- TECTA  
- BAX  
- MAP3K8  
- GEMIN8

First lets create a boxplot for one gene, FOXE1, in ER -ve and ER +ve patients.

We need to select the rows where HUGO.gene.symbol == “FOXE1” and the columns with the expression values for the patients. We select the columns starting with the patient ids “NKI” as we don’t want the columns HUGO.gene.symbol, Chromosome, Start.

exprsAnno %>% 
    filter(HUGO.gene.symbol == "FOXE1") %>%
    select(starts_with("NKI")) %>%
    View()

This gives us the expression values all in one row. We need to tranpose this to plot. With dplyr we can pipe (%>%) the result into R’s t() function to easily achieve this.

exprsAnno %>% 
    filter(HUGO.gene.symbol == "FOXE1") %>%
    select(starts_with("NKI")) %>%
    t() %>%
    View()

This is a matrix (which we can see from the “V1” header in View or if we use class() around the code above). We need to convert this into a data frame, which we can do by adding another pipe.

exprsAnno %>% 
    filter(HUGO.gene.symbol == "FOXE1") %>%
    select(starts_with("NKI")) %>%
    t() %>%
    data.frame() %>%
    View()

Next we’ll save the data frame as an object called filtered.

filtered <- exprsAnno %>% 
    filter(HUGO.gene.symbol == "FOXE1") %>% 
    select(starts_with("NKI")) %>%
    t() %>%
    data.frame()

And add the gene name as a header.

colnames(filtered) <- "FOXE1"

Now we’re ready to make a boxplot. We have a data frame with all the patients values for FOXE1. We want to group these by ER status, to visualise the distribution of expression values for the ER+ve and ER-ve patients.

We can identify the ER status of the patients from the patients dataframe.

patients[, "er"]
  [1] 1 0 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 0 0 1 0 0 1 0
 [36] 1 0 1 1 1 1 1 1 1 1 0 0 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1
 [71] 1 1 0 0 1 1 1 1 1 1 0 0 1 1 0 0 1 0 0 1 0 1 1 1 1 1 1 1 0 0 1 1 1 1 0
[106] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 1 1 0 0 1 1 0 0 1 1
[141] 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1

A 1 means a patient is ER +ve, a 0 means they’re ER -ve. We can then tell ggplot to group the patients by the ER status column.

ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]))) +
    geom_boxplot()

We can also colour by ER status.

ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot()

These are the default ggplot colours. We can also specify our own colours with scale_fill_manual. For example, we could colour the ER -ve goldenrod (yellow) and ER +ve dodgerblue and add labels.

ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                    labels = c("ER -ve", "ER +ve"))

We can label the axes with labs, and scale_x_discrete(labels = NULL) can be used to remove the “1” and “0” from the x axis ticks.

g <- ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                      labels = c("ER -ve", "ER +ve")) +
    labs(title = "Expression of FOXE1 ~ Estrogen Receptor Status",
         x = "Estrogen Receptor Status",
         y = "Expression values of FOXE1") +
    scale_x_discrete(labels = NULL)
print(g)

To centre the title theme(plot.title = element_text(hjust = 0.5)) can be used.

ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                      labels = c("ER -ve", "ER +ve")) +
    labs(title = "Expression of FOXE1 ~ Estrogen Receptor Status",
         x = "Estrogen Receptor Status",
         y = "Expression values of FOXE1") +
    scale_x_discrete(labels = NULL) + 
    theme(plot.title = element_text(hjust = 0.5))

We can save the plot in an object, lets call it g.

g <- ggplot(filtered, aes(y = FOXE1, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                      labels = c("ER -ve", "ER +ve")) +
    labs(title = "Expression of FOXE1 ~ Estrogen Receptor Status",
         x = "Estrogen Receptor Status",
         y = "Expression values of FOXE1") +
    scale_x_discrete(labels = NULL) + 
    theme(plot.title = element_text(hjust = 0.5))

If we want to produce the plot from g we can use print().

print(g)

Now we’ve got a nice plot showing the expression of FOXE1 in ER -ve and ER +ve patients.

Next, to make the boxplots for the other genes, TECTA, BAX, MAP3K8, GEMIN8, we could copy and paste the code for the FOXE1 plot over and over like below.

## TECTA gene ##

filtered <- exprsAnno %>% 
    filter(HUGO.gene.symbol == "TECTA") %>% 
    select(starts_with("NKI")) %>%
    t() %>%
    data.frame()
colnames(filtered) <- "TECTA"
g <- ggplot(filtered, aes(y = TECTA, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                      labels = c("ER -ve", "ER +ve")) +
    labs(title = "Expression of TECTA ~ Estrogen Receptor Status",
         x = "Estrogen Receptor Status",
         y = "Expression values of TECTA") +
    scale_x_discrete(labels = NULL) + 
    theme(plot.title = element_text(hjust = 0.5))
print(g)

## BAX gene ##

filtered <- exprsAnno %>% 
    filter(HUGO.gene.symbol == "BAX") %>% 
    select(starts_with("NKI")) %>%
    t() %>%
    data.frame()
colnames(filtered) <- "BAX"
g <- ggplot(filtered, aes(y = BAX, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
    geom_boxplot() + 
    scale_fill_manual(values = c("goldenrod", "dodgerblue"),
                      name = "ER status",
                      labels = c("ER -ve", "ER +ve")) +
    labs(title = "Expression of BAX ~ Estrogen Receptor Status",
         x = "Estrogen Receptor Status",
         y = "Expression values of BAX") +
    scale_x_discrete(labels = NULL) + 
    theme(plot.title = element_text(hjust = 0.5))
print(g)

## MAP3K8 gene ##

# ETC....

However, as already stated, this is tedious and error-prone.

So instead, lets automate it!

Exercise

Convert the code used to generate the boxplots in the second example of copy/paste/edit into a for loop.

Which variables are you looping over?

Hint: Consider using the function paste() to help change the y-axis label and title to reflect the gene that is being plotted.

Saving multiple plots

You can save all the plots produced by a for loop in a PDF by using the pdf() and dev.off() functions outside the for loop. This will create one file with all the plots.

pdf("myplots.pdf")
for (element in vector) {   
    # plot code
}
dev.off()

Otherwise, another option is to create a PDF (or PNG/JPEG) for each graph inside the for loop.

for (element in vector) {
    pdf(element)
    # plot code   
    dev.off()
}

Storing results

Note that this for loop is helping us identify the number of genes in each chromosome but the result is not stored. Thus, we can not access the results at a later time.

  • When storing the results from a loop, we often create an empty variable before starting the for loop
  • This is used store the result at each iteration of the loop
numGenes <- NULL
chrom <- c("chr1", "chr5", "chr6", "chr8")
for(chr in chrom) {
  numRows <- exprsAnno %>%
      filter(Chromosome == chr) %>%
      summarise(n())
  numGenes[chr] <- numRows
  }
numGenes
$chr1
[1] 35

$chr5
[1] 16

$chr6
[1] 20

$chr8
[1] 12

Exercise

Identify the number of patients who are positive for ER/PR/HER2 when considering each status individually. Store the results in a vector.

status <- c("er", "pr", "her2")
### Your answer here ###

Conditional branching: Commands and flow control

What if we only wanted to create boxplots for genes if they met certain criteria, for example, if the expression of the gene differed between ER -ve and ER +ve. To do that we could use an if statement.

  • Use an if statement for any kind of condition testing
  • Different outcomes can be selected based on a condition within brackets
if (condition) {
  ... do this ...
  } else {
    ... do something else ...
    }
  • condition is any logical value, and can contain multiple conditions.
    • e.g. (a == 2 & b < 5), this is a compound conditional argument
  • The condition should return a single value of TRUE or FALSE

Other conditional tests

  • There are various tests that can check the type of data stored in a variable
    • these tend to be called is...().
      • try tab-complete on is.
is.numeric(10)
[1] TRUE
is.numeric("TEN")
[1] FALSE
is.character(10)
[1] FALSE
  • is.na() is useful for seeing if an NA value is found
    • cannot use == NA!
x <- c(1, 2, NA)
mean(x)
[1] NA
is.na(x)
[1] FALSE FALSE  TRUE

Example

Checking if a gene of interest is in the dataset - There are 2 methods that could be used to achieve this 1. Make uses of the functions all() and any() 2. Make use of the function sum()

## Method 1
if (any(exprsAnno$HUGO.gene.symbol == "PIK3CA")){
    print("PIK3CA is in the dataset")
}else{
    print("PIK3CA is not in the dataset")
}
[1] "PIK3CA is not in the dataset"
## Method 2
if (sum(exprsAnno$HUGO.gene.symbol == "PIK3CA") == 1){
    print("PIK3CA is in the dataset")
}else{
    print("PIK3CA is not in the dataset")
}
[1] "PIK3CA is not in the dataset"

Exercise

Write an if else statement to check if all of the following genes are in the dataset: BCL2, HOXA9, MAPK1, ARID1A, GATA3, ESR1

Hint: Make use of %in%

Other useful conditionals

file.exists(), dir.exists() and dir.create() are also functions that are useful in if else statements. Particularly, when writing scripts that take inputs from the commandline.

Combining Loops and Conditional branching

Using the for loop we wrote before, we could add an if else branch to identify if the difference between the mean expression for a gene differs between the groups ER positive/negative.

Automation Example 3 - Creating boxplots if expression differs between ER -ve and ER +ve

We are interested in finding out if the following genes:
AMPD3, TECTA, TRPV4, CD244, ABHD10, GEMIN8, MAP1A, SMAD7 have difference between the ER status of greater than 0.04.

  • Here’s how we can combine a for loop and an if statement to test for this for each iteration of the loop: 1. Identify the expression values associated with the gene of interest 2. Group the samples based on there ER status, and find the mean for each group 3. if the absolute difference is greater than 0.04, print a statement that informs us of this 4. else, do nothing
mygenes <- c("AMPD3", "TECTA", "TRPV4", "CD244", "ABHD10", "GEMIN8", "MAP1A", "SMAD7")
for (gen in mygenes){
    filtered <- exprsAnno %>%
        filter(HUGO.gene.symbol == gen) %>%
        select(starts_with("NKI")) %>%
        t() %>%
        data.frame()
    colnames(filtered) <- "gene"
    x <- filtered %>% 
        group_by(factor(patients[, "er"])) %>%
        summarise("exprsAv" = mean(gene, na.rm = TRUE))
    diff <- x$exprsAv[1] - x$exprsAv[2]
    if (abs(diff) > 0.04){
        print(paste("The difference between ER status for", gen, "is", abs(diff), "which is greater than 0.04"))
    }else{
        print(paste("The difference between ER status for", gen, "is not greater than 0.04"))
    }
}
[1] "The difference between ER status for AMPD3 is 0.046240625 which is greater than 0.04"
[1] "The difference between ER status for TECTA is 0.046746875 which is greater than 0.04"
[1] "The difference between ER status for TRPV4 is 0.0914234375 which is greater than 0.04"
[1] "The difference between ER status for CD244 is not greater than 0.04"
[1] "The difference between ER status for ABHD10 is 0.0404546875 which is greater than 0.04"
[1] "The difference between ER status for GEMIN8 is 0.0495082677165354 which is greater than 0.04"
[1] "The difference between ER status for MAP1A is not greater than 0.04"
[1] "The difference between ER status for SMAD7 is not greater than 0.04"

Whilst it is slightly informative to know that the difference between means of ER status for each gene. It would be more interesting to plot the expression of the genes that have an absolute difference of greater than 0.04.

Therefore we want to take the same steps as above but change from printing a statement to plotting a graph - Here’s how we can use an if statement to test for this + for each iteration of the the loop: 1. Identify the expression values associated with the gene of interest 2. Group the samples based on there ER status, and find the mean for each group 3. if the absolute difference is greater than 0.04, produce a boxplot to depict the difference 4. else, do nothing

mygenes <- c("AMPD3", "TECTA", "TRPV4", "CD244", "ABHD10", "GEMIN8", "MAP1A", "SMAD7")
for (gen in mygenes){
    filtered <- exprsAnno %>%
        filter(HUGO.gene.symbol == gen) %>%
        select(starts_with("NKI")) %>%
        t() %>%
        data.frame()
    colnames(filtered) <- "gene"
    x <- filtered %>% 
        group_by(factor(patients[, "er"])) %>%
        summarise("exprsAv" = mean(gene, na.rm = TRUE))
    diff <- x$exprsAv[1] - x$exprsAv[2]
    if (abs(diff) > 0.04){
        g <- ggplot(filtered, aes(y = gene, x = factor(patients[, "er"]), fill = factor(patients[, "er"]))) +
            geom_boxplot()
        print(g)
    }else{
        next
    }
}

Exercise

Add additional layers to the above plot so that the labels are more informative. See plot below, replace “x” in the labs for the gene that we are investigating:
Also, change the colours associated with each ER status.

Code formatting avoids bugs!

Compare:

for (a in ages){
numRows <- patients %>%
filter(age > a) %>%
summarise(n())
if(numRows < 10){
s <- paste("There are fewer than 10 patients younger than", a)
print(s)
}else{
s <- paste("There are more than 10 patients younger than", a)
print(s)
}
}

to:

for (a in ages) {
    numRows <- patients %>%
        filter(age > a) %>%
        summarise(n())
    if (numRows < 10) {
        s <- paste("There are fewer than 10 patients younger than", a)
        print(s)
    } else{
        s <- paste("There are more than 10 patients younger than", a)
        print(s)
    }
}
  • The code between brackets {} always is indented, this clearly separates what is executed once, and what is run multiple times
  • Trailing bracket } always alone on the line at the same indentation level as the initial bracket {
  • Use white spaces to divide the horizontal space between units of your code, e.g. around assignments, comparisons
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyBVc2luZyBSIC0gV2VlayA0IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwotLS0KCipNaXJpYW0gWWV1bmcqCgpNb2RpZmllZCB2ZXJzaW9uIG9mIG1hdGVyaWFsIGZyb20gdGhlIFVuaXZlcnNpdHkgb2YgQ2FtYnJpZGdlIEJpb2luZm9ybWF0aWNzIFRyYWluaW5nIFVuaXQuCgojNC4gQXV0b21hdGlvbiBpbiBSCgojIyBNb3RpdmF0aW9uCgpUeXBpY2FsbHkgd2hlbiB3ZSBwZXJmb3JtIGFuIGFuYWx5c2lzIHdlIG5lZWQgdG8gcGVyZm9ybSB0aGUgc2FtZSBzdGVwcyAqKm11bHRpcGxlIHRpbWVzKiosIGZvciBleGFtcGxlLCBvdmVyIG11bHRpcGxlIGdlbmVzLCBzYW1wbGVzLCBkYXRhc2V0cy4KCldpdGggdGhlIGJhc2ljIGFuYWx5c2VzIGFuZCBncmFwaHMgdGhhdCB3ZSBoYXZlIGNyZWF0ZWQgaW4gdGhlIHByZXZpb3VzIHNlc3Npb25zIGl0IGlzIHBvc3NpYmxlIHRvCmNvcHkgYW5kIHBhc3RlIHRoZSByZWxldmFudCBzZWN0aW9uIG9mIGNvZGUgYW5kIGFkanVzdCB0aGUgY29kZSB0byBwcm9kdWNlIG11bHRpcGxlIGdyYXBocy4KCkhvd2V2ZXIsIHRha2UgdGhlIHNjZW5hcmlvcyB3aGVyZSB5b3Ugd2FudCB0bzoKCiAgMS4gQ291bnQgdGhlIG51bWJlciBvZiBnZW5lcyBleHByZXNzZWQgaW4gdGhlIHBhdGllbnRzIGZvciAqKiptdWx0aXBsZSBjaHJvbW9zb21lcyoqKiAKICAyLiBDcmVhdGUgYm94cGxvdHMgb2YgZXhwcmVzc2lvbiBpbiBFUiAtdmUgdmVyc3VzIEVSICt2ZSBwYXRpZW50cyBmb3IgKioqbXVsdGlwbGUgZ2VuZXMqKioKICAzLiBDcmVhdGluZyBib3hwbG90cyBmb3IgKioqbXVsdGlwbGUgZ2VuZXMqKiogKmlmKiBleHByZXNzaW9uIGlzIGRpZmZlcmVudCBpbiBFUiArdmUgdmVyc3VzIEVSLXZlCgpGaXJzdCBsZXRzIGxvYWQgaW4gdGhlIGxpYnJhcmllcyB3ZSdsbCBuZWVkLgoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKTGV0cyBhbHNvIGxvYWQgaW4gdGhlIGRhdGEuCgpgYGB7cn0KcGF0aWVudHMgPC0gIHJlYWQuZGVsaW0oInVwZGF0ZWQucGF0aWVudC50eHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmV4cHJzQW5ubyA8LSByZWFkLmRlbGltKCJhbm5vLmdlbmUuZXhwcmVzc2lvbi50eHQiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKWW91IGNhbiBjaGVjayB0aGUgZGltZW5zaW9ucyBvZiB0aGUgb2JqZWN0cywgdG8gY2hlY2sgdGhlIG51bWJlcnMgb2Ygcm93cyBhbmQgY29sdW1ucyB0aGV5IGNvbnRhaW4uCgpgYGB7cn0KZGltKHBhdGllbnRzKQpkaW0oZXhwcnNBbm5vKQpgYGAKClRha2UgYSBsb29rIGF0IHRoZSBmaWxlcyB0byBjaGVjayB3aGF0IHRoZXkgY29udGFpbiBhbmQgYXMgYSBzYW5pdHkgY2hlY2sgdGhhdCBhbGwgbG9va3Mgb2suCgpgYGB7cn0KVmlldyhwYXRpZW50cykKVmlldyhleHByc0Fubm8pCmBgYAoKWW91IGNhbiBjaGVjayB0aGUgY29sdW1uIG5hbWVzLgoKYGBge3J9CmNvbG5hbWVzKHBhdGllbnRzKQpjb2xuYW1lcyhleHByc0Fubm8pCmBgYAoKYHBhdGllbnRzYCBoYXMgNiBjb2x1bW5zIGZvciBzYW1wbGVuYW1lLCBhZ2UsIGVyLCBncmFkZSwgaGVyMiwgcHIsIGZvciAxNjggcGF0aWVudHMuIFdlIGNhbiB1c2UgdGhpcyBvYmplY3QgdG8gZ2V0IEVSL1BSL0hFUjIgc3RhdHVzIG9mIHRoZSBwYXRpZW50cy4gCgpgZXhwcnNBbm5vYCBoYXMgdGhlIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gZm9yIHRoZSAxNjggcGF0aWVudHMgKHdobyBhbGwgaGF2ZSBhbiBJRCB0aGF0IHN0YXJ0cyB3aXRoICJOS0kiKSwgZm9sbG93ZWQgYnkgY29sdW1ucyBmb3IgZ2VuZSBzeW1ib2wsIGNocm9tb3NvbWUgYW5kIHN0YXJ0IHBvc2l0aW9uIChIVUdPLmdlbmUuc3ltYm9sLCBDaHJvbW9zb21lLCBTdGFydCkuCgojIyBBdXRvbWF0aW9uIEV4YW1wbGUgMSAtIENvdW50aW5nIGdlbmVzIGZvciBtdWx0aXBsZSBjaHJvbW9zb21lcwoKV2Ugd2FudCB0byBpZGVudGlmeSB0aGUgbnVtYmVyIG9mIHRoZSBnZW5lcyBpbiB0aGUgZGF0YXNldCBmb3IgQ2hyb21vc29tZXMgMSwgNSwgNiwgOC4KCkZpcnN0IGxldHMgaWRlbnRpZnkgdGhlIG51bWJlciBvbiBjaHJvbW9zb21lIDEuIFdlIGNhbiB1c2UgYGRwbHlyYCdzIGZpbHRlciBhbmQgc3VtbWFyaXNlIGZ1bmN0aW9ucy4gV2UgY2FuIHVzZSBzdW1tYXJpc2UobigpKSB0byBnZXQgYSBjb3VudCBvZiByb3dzLgoKYGBge3J9CmV4cHJzQW5ubyAlPiUKICAgIGZpbHRlcihDaHJvbW9zb21lID09ICJjaHIxIikgJT4lCiAgICBzdW1tYXJpc2UobigpKQpgYGAKCk5vdGUgdGhhdCBgdGFsbHkoKWAgaXMgYSBjb252ZW5pZW50IHdyYXBwZXIgZnVuY3Rpb24geW91IGNhbiB1c2UgaW5zdGVhZCBvZiBgc3VtbWFyaXNlKG4oKSlgCgpgYGB7cn0KZXhwcnNBbm5vICU+JQogICAgZmlsdGVyKENocm9tb3NvbWUgPT0gImNocjEiKSAlPiUKICAgIHRhbGx5KCkKYGBgCgpUaGF0IGdpdmVzIHVzIHRoZSBjb3VudCBmb3Igb25lIGNocm9tb3NvbWUgKGNocjEpLiBOb3cgaG93IGRvIHdlIGlkZW50aWZ5IHRoZSBudW1iZXIgb2YgZ2VuZXMgZm9yIG11bHRpcGxlIGNocm9tb3NvbWVzLCBjaHJvbW9zb21lcyAxLCA1LCA2IGFuZCA4PwoKV2UgY291bGQgY29weSBhbmQgcGFzdGUgdGhlIGNvZGUgbGlrZSBiZWxvdy4KCmBgYHtyfQpleHByc0Fubm8gJT4lCiAgICBmaWx0ZXIoQ2hyb21vc29tZSA9PSAiY2hyMSIpICU+JQogICAgc3VtbWFyaXNlKG4oKSkKCmV4cHJzQW5ubyAlPiUKICAgIGZpbHRlcihDaHJvbW9zb21lID09ICJjaHI1IikgJT4lCiAgICBzdW1tYXJpc2UobigpKQoKZXhwcnNBbm5vICU+JQogICAgZmlsdGVyKENocm9tb3NvbWUgPT0gImNocjYiKSAlPiUKICAgIHN1bW1hcmlzZShuKCkpCgpleHByc0Fubm8gJT4lCiAgICBmaWx0ZXIoQ2hyb21vc29tZSA9PSAiY2hyOCIpICU+JQogICAgc3VtbWFyaXNlKG4oKSkKYGBgCgpCdXQgYXMgeW91IGNhbiBpbWFnaW5lLCB0aGUgbW9yZSBsaW5lcyBvZiBjb2RlIHRoYXQgbmVlZCB0byBiZSBjb3BpZWQsIHBhc3RlZCBhbmQgZWRpdGVkLCB0aGUgbW9yZSBsaWtlbHkgaXQgaXMgdGhhdCBlcnJvcnMgd2lsbCBhcmlzZS4KClRoaXMgbWV0aG9kIG9mIGNvcHlpbmcsIHBhc3RpbmcgYW5kIGVkaXRpbmcgY2FuIGJlOgoKICAgIDEuIFRlZGlvdXMKICAgIDIuIEVycm9uZW91cyAKCiMjIEF1dG9tYXRpbmcgQ29tbWFuZHM6IExvb3BzIGFuZCBmbG93IGNvbnRyb2wKCi0gTWFueSBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaGF2ZSB3YXlzIG9mIGRvaW5nIHRoZSBzYW1lIHRoaW5nIG1hbnkgdGltZXMsIHBlcmhhcHMgY2hhbmdpbmcgc29tZSB2YXJpYWJsZSBlYWNoIHRpbWUuIFRoaXMgaXMgY2FsbGVkICoqbG9vcGluZyoqLiBJdCBpcyBhIHdheSB0byBhdXRvbWF0ZSB0YXNrcy4KCi0gQXMgd2UgYXJlIGRvaW5nIHRoZSBzYW1lIHRoaW5nIG11bHRpcGxlIHRpbWVzLCBidXQgd2l0aCBhIGRpZmZlcmVudCBjaHJvbW9zb21lIGVhY2ggdGltZSwgd2UgY2FuIHVzZSBhICoqbG9vcCoqIGluc3RlYWQKLSBSIGhhcyB0d28gYmFzaWMgdHlwZXMgb2YgbG9vcAogICAgKyBhICoqYHdoaWxlYCoqIGxvb3A6IHJ1biBzb21lIGNvZGUgd2hpbGUgc29tZSBjb25kaXRpb24gaXMgdHJ1ZSAKICAgICgqaGFyZGx5IGV2ZXIgdXNlZCEgVGhlcmVmb3JlIHdpbGwgbm90IGJlIGNvdmVyZWQqKQogICAgKyBhICoqYGZvcmAqKiBsb29wOiBydW4gc29tZSBjb2RlIG9uIGV2ZXJ5IHZhbHVlIGluIGEgdmVjdG9yCiAgICAKYGZvcmAgClRoZSBiYXNpYyBzdHJ1Y3R1cmUgb2YgYSBgZm9yYCBsb29wOgpgYGAKZm9yIChlbGVtZW50IGluIHZlY3Rvcil7CiAgICAuLi4gZG8gdGhpcyAuLi4KfQoKYGBgCgotIFRoZXJlZm9yZSB0aGUgY29kZSBpcyBvbmx5IHJ1biBmb3IgYXMgbWFueSBlbGVtZW50cyBhcmUgaW4gdGhlIHZlY3Rvci4gVGhlcmVmb3JlIHdlIGNhbiBwcmVkaWN0IHRoZSBudW1iZXIgb2YgdGltZXMgdGhlIGNvZGUgaXMgcnVuLgoKLSBOb3RlOiAqZWxlbWVudCogYW5kICp2ZWN0b3IqIGFyZSBqdXN0IHZhcmlhYmxlIG5hbWVzIGFuZCBjYW4gdGhlcmVmb3JlIGJlIG5hbWVkIHdoYXRldmVyIHlvdSB3YW50CnRoZW0gdG8gYmUsIGFzIGxvbmcgYXMgdGhleSBzYXRpc2Z5IHRoZSBjb25zdHJhaW50cyBvbiB2YXJpYmxlIG5hbWluZy4KCi0gSGVyZSdzIGhvdyB3ZSBtaWdodCB1c2UgYSBgZm9yYCBsb29wIHRvIGZpbmQgb3V0IHRoZSBudW1iZXIgb2YgZ2VuZXMgb24gZWFjaCBjaHJvbW9zb21lLiBXZSBzdG9yZSB0aGUgY291bnQgaW4gYSB2YXJpYWJsZSBjYWxsZWQgYG51bVJvd3NgIHNvIHdlIGNhbiB0aGVuIHByaW50IGl0IG91dC4KCmBgYHtyfQpjaHJvbSA8LSBjKCJjaHIxIiwgImNocjUiLCAiY2hyNiIsICJjaHI4IikKCmZvciAoY2hyIGluIGNocm9tKXsKICAgIG51bVJvd3MgPC0gZXhwcnNBbm5vICU+JQogICAgICAgIGZpbHRlcihDaHJvbW9zb21lID09IGNocikgJT4lCiAgICAgICAgc3VtbWFyaXNlKG4oKSkKICAgIHMgPC0gcGFzdGUoIlRoZSBudW1iZXIgb2YgZ2VuZXMgb24iLCBjaHIsICJpcyIsIG51bVJvd3MpCiAgICBwcmludChzKQp9CgpgYGAKCi0gVGhlIGFib3ZlIGZvciBsb29wIGZpbmRzIG91dCB0aGUgc2FtZSBpbmZvcm1hdGlvbiB0aGF0IHdlIGlkZW50aWZpZWQgZWFybGllciBieQpjb3B5aW5nL3Bhc3RpbmcvZWRpdGluZwotIFRvIG1vcmUgYWNjdXJhdGVseSBkZXBpY3QgdGhlIG9wZXJhdGlvbnMgb2YgYSBgZm9yYCBsb29wLCB0aGUgY29tbWFuZHMgYmVpbmcgcnVuIGFyZSBzaW1pbGFyIHRvIHRoZQpmb2xsb3dpbmc6CmBgYHtyfQpjaHIgPC0gImNocjEiCm51bVJvd3MgPC0gZXhwcnNBbm5vICU+JSBmaWx0ZXIoQ2hyb21vc29tZSA9PSBjaHIpICU+JSBzdW1tYXJpc2UobigpKQoKY2hyIDwtICJjaHI1IgpudW1Sb3dzIDwtIGV4cHJzQW5ubyAlPiUgZmlsdGVyKENocm9tb3NvbWUgPT0gY2hyKSAlPiUgc3VtbWFyaXNlKG4oKSkKCmNociA8LSAiY2hyNiIKbnVtUm93cyA8LSBleHByc0Fubm8gJT4lIGZpbHRlcihDaHJvbW9zb21lID09IGNocikgJT4lIHN1bW1hcmlzZShuKCkpCgpjaHIgPC0gImNocjgiCm51bVJvd3MgPC0gZXhwcnNBbm5vICU+JSBmaWx0ZXIoQ2hyb21vc29tZSA9PSBjaHIpICU+JSBzdW1tYXJpc2UobigpKQpgYGAKCiMjIEV4ZXJjaXNlCgpVc2luZyB0aGUgYHBhdGllbnRzYCBvYmplY3QsIGZpbmQgdGhlIG51bWJlciBvZiBwYXRpZW50cyB3aG8gYXJlIGFnZWQgYWJvdmUgMjAsIDMwLCA0MCwgYW5kIHByaW50IHRoZSBhbnN3ZXIgdG8gdGhlIGNvbnNvbGUuCmBgYHtyfQphZ2VzIDwtIGMoMjAsIDMwLCA0MCkKIyMjIEluc2VydCB5b3VyIGNvZGUgaGVyZSAjIyMKYGBgCgoKIyMgQXV0b21hdGlvbiBFeGFtcGxlIDIgLSBDcmVhdGluZyBib3hwbG90cyBmb3IgbXVsdGlwbGUgZ2VuZXMKCldoaWxzdCB0aGUgZmlyc3QgZXhhbXBsZSBvbmx5IGNvbnRhaW5zIDMgbGluZXMgb2YgY29kZSwgYW5kIHRoZXJlZm9yZSBpcyBsZXNzIGxpa2VseSB0byBsZWFkIHRvIGVycm9ycywgaW1hZ2luZSB0aGF0IHlvdSB3YW50IHRvIGNyZWF0ZSBtdWx0aXBsZSBib3hwbG90cyBmb3IgdGhlIGV4cHJlc3Npb24gdmFsdWVzIG9mIHRoZSBmb2xsb3dpbmcgZ2VuZXMgaW4gRVIgLXZlIGFuZCBFUiArdmUgcGF0aWVudHMuCgogICAgLSBGT1hFMSAgCiAgICAtIFRFQ1RBICAKICAgIC0gQkFYICAKICAgIC0gTUFQM0s4ICAKICAgIC0gR0VNSU44CgpGaXJzdCBsZXRzIGNyZWF0ZSBhIGJveHBsb3QgZm9yIG9uZSBnZW5lLCBGT1hFMSwgaW4gRVIgLXZlIGFuZCBFUiArdmUgcGF0aWVudHMuCgpXZSBuZWVkIHRvIHNlbGVjdCB0aGUgcm93cyB3aGVyZSBIVUdPLmdlbmUuc3ltYm9sID09ICJGT1hFMSIgYW5kIHRoZSBjb2x1bW5zIHdpdGggdGhlIGV4cHJlc3Npb24gdmFsdWVzIGZvciB0aGUgcGF0aWVudHMuIFdlIHNlbGVjdCB0aGUgY29sdW1ucyBzdGFydGluZyB3aXRoIHRoZSBwYXRpZW50IGlkcyAiTktJIiBhcyB3ZSBkb24ndCB3YW50IHRoZSBjb2x1bW5zIEhVR08uZ2VuZS5zeW1ib2wsIENocm9tb3NvbWUsIFN0YXJ0LgoKYGBge3IgZXZhbD1GQUxTRX0KZXhwcnNBbm5vICU+JSAKICAgIGZpbHRlcihIVUdPLmdlbmUuc3ltYm9sID09ICJGT1hFMSIpICU+JQogICAgc2VsZWN0KHN0YXJ0c193aXRoKCJOS0kiKSkgJT4lCiAgICBWaWV3KCkKYGBgCgpUaGlzIGdpdmVzIHVzIHRoZSBleHByZXNzaW9uIHZhbHVlcyBhbGwgaW4gb25lIHJvdy4gV2UgbmVlZCB0byB0cmFucG9zZSB0aGlzIHRvIHBsb3QuIFdpdGggYGRwbHlyYCB3ZSBjYW4gcGlwZSAoYCU+JWApIHRoZSByZXN1bHQgaW50byBSJ3MgYHQoKWAgZnVuY3Rpb24gdG8gZWFzaWx5IGFjaGlldmUgdGhpcy4KCmBgYHtyfQpleHByc0Fubm8gJT4lIAogICAgZmlsdGVyKEhVR08uZ2VuZS5zeW1ib2wgPT0gIkZPWEUxIikgJT4lCiAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoIk5LSSIpKSAlPiUKICAgIHQoKSAlPiUKICAgIFZpZXcoKQpgYGAKClRoaXMgaXMgYSBtYXRyaXggKHdoaWNoIHdlIGNhbiBzZWUgZnJvbSB0aGUgIlYxIiBoZWFkZXIgaW4gVmlldyBvciBpZiB3ZSB1c2UgY2xhc3MoKSBhcm91bmQgdGhlIGNvZGUgYWJvdmUpLiBXZSBuZWVkIHRvIGNvbnZlcnQgdGhpcyBpbnRvIGEgZGF0YSBmcmFtZSwgd2hpY2ggd2UgY2FuIGRvIGJ5IGFkZGluZyBhbm90aGVyIHBpcGUuCgpgYGB7cn0KZXhwcnNBbm5vICU+JSAKICAgIGZpbHRlcihIVUdPLmdlbmUuc3ltYm9sID09ICJGT1hFMSIpICU+JQogICAgc2VsZWN0KHN0YXJ0c193aXRoKCJOS0kiKSkgJT4lCiAgICB0KCkgJT4lCiAgICBkYXRhLmZyYW1lKCkgJT4lCiAgICBWaWV3KCkKYGBgCgpOZXh0IHdlJ2xsIHNhdmUgdGhlIGRhdGEgZnJhbWUgYXMgYW4gb2JqZWN0IGNhbGxlZCBgZmlsdGVyZWRgLgoKYGBge3J9CmZpbHRlcmVkIDwtIGV4cHJzQW5ubyAlPiUgCiAgICBmaWx0ZXIoSFVHTy5nZW5lLnN5bWJvbCA9PSAiRk9YRTEiKSAlPiUgCiAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoIk5LSSIpKSAlPiUKICAgIHQoKSAlPiUKICAgIGRhdGEuZnJhbWUoKQpgYGAKCkFuZCBhZGQgdGhlIGdlbmUgbmFtZSBhcyBhIGhlYWRlci4KCmBgYHtyfQpjb2xuYW1lcyhmaWx0ZXJlZCkgPC0gIkZPWEUxIgpgYGAKCk5vdyB3ZSdyZSByZWFkeSB0byBtYWtlIGEgYm94cGxvdC4gV2UgaGF2ZSBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgdGhlIHBhdGllbnRzIHZhbHVlcyBmb3IgRk9YRTEuIFdlIHdhbnQgdG8gZ3JvdXAgdGhlc2UgYnkgRVIgc3RhdHVzLCB0byB2aXN1YWxpc2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiBleHByZXNzaW9uIHZhbHVlcyBmb3IgdGhlIEVSK3ZlIGFuZCBFUi12ZSBwYXRpZW50cy4KCldlIGNhbiBpZGVudGlmeSB0aGUgRVIgc3RhdHVzIG9mIHRoZSBwYXRpZW50cyBmcm9tIHRoZSBwYXRpZW50cyBkYXRhZnJhbWUuCgpgYGB7cn0gCnBhdGllbnRzWywgImVyIl0KYGBgCgpBIDEgbWVhbnMgYSBwYXRpZW50IGlzIEVSICt2ZSwgYSAwIG1lYW5zIHRoZXkncmUgRVIgLXZlLiBXZSBjYW4gdGhlbiB0ZWxsIGdncGxvdCB0byBncm91cCB0aGUgcGF0aWVudHMgYnkgdGhlIEVSIHN0YXR1cyBjb2x1bW4uCgpgYGB7cn0KZ2dwbG90KGZpbHRlcmVkLCBhZXMoeSA9IEZPWEUxLCB4ID0gZmFjdG9yKHBhdGllbnRzWywgImVyIl0pKSkgKwogICAgZ2VvbV9ib3hwbG90KCkKYGBgCgpXZSBjYW4gYWxzbyBjb2xvdXIgYnkgRVIgc3RhdHVzLgoKYGBge3J9CmdncGxvdChmaWx0ZXJlZCwgYWVzKHkgPSBGT1hFMSwgeCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSwgZmlsbCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSkpICsKICAgIGdlb21fYm94cGxvdCgpCmBgYAoKVGhlc2UgYXJlIHRoZSBkZWZhdWx0IGdncGxvdCBjb2xvdXJzLiBXZSBjYW4gYWxzbyBzcGVjaWZ5IG91ciBvd24gY29sb3VycyB3aXRoIGBzY2FsZV9maWxsX21hbnVhbGAuIEZvciBleGFtcGxlLCB3ZSBjb3VsZCBjb2xvdXIgdGhlIEVSIC12ZSBnb2xkZW5yb2QgKHllbGxvdykgYW5kIEVSICt2ZSBkb2RnZXJibHVlIGFuZCBhZGQgbGFiZWxzLgoKYGBge3J9CmdncGxvdChmaWx0ZXJlZCwgYWVzKHkgPSBGT1hFMSwgeCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSwgZmlsbCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSkpICsKICAgIGdlb21fYm94cGxvdCgpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJnb2xkZW5yb2QiLCAiZG9kZ2VyYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJFUiBzdGF0dXMiLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkVSIC12ZSIsICJFUiArdmUiKSkKYGBgCgpXZSBjYW4gbGFiZWwgdGhlIGF4ZXMgd2l0aCBgbGFic2AsIGFuZCBgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBOVUxMKWAgY2FuIGJlIHVzZWQgdG8gcmVtb3ZlIHRoZSAiMSIgYW5kICIwIiBmcm9tIHRoZSB4IGF4aXMgdGlja3MuCgpgYGB7cn0KZyA8LSBnZ3Bsb3QoZmlsdGVyZWQsIGFlcyh5ID0gRk9YRTEsIHggPSBmYWN0b3IocGF0aWVudHNbLCAiZXIiXSksIGZpbGwgPSBmYWN0b3IocGF0aWVudHNbLCAiZXIiXSkpKSArCiAgICBnZW9tX2JveHBsb3QoKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZ29sZGVucm9kIiwgImRvZGdlcmJsdWUiKSwKICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiRVIgc3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkVSIC12ZSIsICJFUiArdmUiKSkgKwogICAgbGFicyh0aXRsZSA9ICJFeHByZXNzaW9uIG9mIEZPWEUxIH4gRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgICAgeCA9ICJFc3Ryb2dlbiBSZWNlcHRvciBTdGF0dXMiLAogICAgICAgICB5ID0gIkV4cHJlc3Npb24gdmFsdWVzIG9mIEZPWEUxIikgKwogICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBOVUxMKQpwcmludChnKQpgYGAKClRvIGNlbnRyZSB0aGUgdGl0bGUgYHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKWAgY2FuIGJlIHVzZWQuCgpgYGB7cn0KZ2dwbG90KGZpbHRlcmVkLCBhZXMoeSA9IEZPWEUxLCB4ID0gZmFjdG9yKHBhdGllbnRzWywgImVyIl0pLCBmaWxsID0gZmFjdG9yKHBhdGllbnRzWywgImVyIl0pKSkgKwogICAgZ2VvbV9ib3hwbG90KCkgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdvbGRlbnJvZCIsICJkb2RnZXJibHVlIiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkVSIHN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJFUiAtdmUiLCAiRVIgK3ZlIikpICsKICAgIGxhYnModGl0bGUgPSAiRXhwcmVzc2lvbiBvZiBGT1hFMSB+IEVzdHJvZ2VuIFJlY2VwdG9yIFN0YXR1cyIsCiAgICAgICAgIHggPSAiRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgICAgeSA9ICJFeHByZXNzaW9uIHZhbHVlcyBvZiBGT1hFMSIpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gTlVMTCkgKyAKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpgYGAKCldlIGNhbiBzYXZlIHRoZSBwbG90IGluIGFuIG9iamVjdCwgbGV0cyBjYWxsIGl0IGBnYC4KCmBgYHtyfQpnIDwtIGdncGxvdChmaWx0ZXJlZCwgYWVzKHkgPSBGT1hFMSwgeCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSwgZmlsbCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSkpICsKICAgIGdlb21fYm94cGxvdCgpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJnb2xkZW5yb2QiLCAiZG9kZ2VyYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJFUiBzdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiRVIgLXZlIiwgIkVSICt2ZSIpKSArCiAgICBsYWJzKHRpdGxlID0gIkV4cHJlc3Npb24gb2YgRk9YRTEgfiBFc3Ryb2dlbiBSZWNlcHRvciBTdGF0dXMiLAogICAgICAgICB4ID0gIkVzdHJvZ2VuIFJlY2VwdG9yIFN0YXR1cyIsCiAgICAgICAgIHkgPSAiRXhwcmVzc2lvbiB2YWx1ZXMgb2YgRk9YRTEiKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IE5VTEwpICsgCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgpJZiB3ZSB3YW50IHRvIHByb2R1Y2UgdGhlIHBsb3QgZnJvbSBgZ2Agd2UgY2FuIHVzZSBwcmludCgpLgoKYGBge3J9CnByaW50KGcpCmBgYAoKTm93IHdlJ3ZlIGdvdCBhIG5pY2UgcGxvdCBzaG93aW5nIHRoZSBleHByZXNzaW9uIG9mIEZPWEUxIGluIEVSIC12ZSBhbmQgRVIgK3ZlIHBhdGllbnRzLiAKCk5leHQsIHRvIG1ha2UgdGhlIGJveHBsb3RzIGZvciB0aGUgb3RoZXIgZ2VuZXMsIFRFQ1RBLCBCQVgsIE1BUDNLOCwgR0VNSU44LCB3ZSBjb3VsZCBjb3B5IGFuZCBwYXN0ZSB0aGUgY29kZSBmb3IgdGhlIEZPWEUxIHBsb3Qgb3ZlciBhbmQgb3ZlciBsaWtlIGJlbG93LgoKCmBgYHtyLCBldmFsPUZBTFNFfQoKIyMgVEVDVEEgZ2VuZSAjIwoKZmlsdGVyZWQgPC0gZXhwcnNBbm5vICU+JSAKICAgIGZpbHRlcihIVUdPLmdlbmUuc3ltYm9sID09ICJURUNUQSIpICU+JSAKICAgIHNlbGVjdChzdGFydHNfd2l0aCgiTktJIikpICU+JQogICAgdCgpICU+JQogICAgZGF0YS5mcmFtZSgpCmNvbG5hbWVzKGZpbHRlcmVkKSA8LSAiVEVDVEEiCmcgPC0gZ2dwbG90KGZpbHRlcmVkLCBhZXMoeSA9IFRFQ1RBLCB4ID0gZmFjdG9yKHBhdGllbnRzWywgImVyIl0pLCBmaWxsID0gZmFjdG9yKHBhdGllbnRzWywgImVyIl0pKSkgKwogICAgZ2VvbV9ib3hwbG90KCkgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImdvbGRlbnJvZCIsICJkb2RnZXJibHVlIiksCiAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkVSIHN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJFUiAtdmUiLCAiRVIgK3ZlIikpICsKICAgIGxhYnModGl0bGUgPSAiRXhwcmVzc2lvbiBvZiBURUNUQSB+IEVzdHJvZ2VuIFJlY2VwdG9yIFN0YXR1cyIsCiAgICAgICAgIHggPSAiRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgICAgeSA9ICJFeHByZXNzaW9uIHZhbHVlcyBvZiBURUNUQSIpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gTlVMTCkgKyAKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpwcmludChnKQoKIyMgQkFYIGdlbmUgIyMKCmZpbHRlcmVkIDwtIGV4cHJzQW5ubyAlPiUgCiAgICBmaWx0ZXIoSFVHTy5nZW5lLnN5bWJvbCA9PSAiQkFYIikgJT4lIAogICAgc2VsZWN0KHN0YXJ0c193aXRoKCJOS0kiKSkgJT4lCiAgICB0KCkgJT4lCiAgICBkYXRhLmZyYW1lKCkKY29sbmFtZXMoZmlsdGVyZWQpIDwtICJCQVgiCmcgPC0gZ2dwbG90KGZpbHRlcmVkLCBhZXMoeSA9IEJBWCwgeCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSwgZmlsbCA9IGZhY3RvcihwYXRpZW50c1ssICJlciJdKSkpICsKICAgIGdlb21fYm94cGxvdCgpICsgCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJnb2xkZW5yb2QiLCAiZG9kZ2VyYmx1ZSIpLAogICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJFUiBzdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiRVIgLXZlIiwgIkVSICt2ZSIpKSArCiAgICBsYWJzKHRpdGxlID0gIkV4cHJlc3Npb24gb2YgQkFYIH4gRXN0cm9nZW4gUmVjZXB0b3IgU3RhdHVzIiwKICAgICAgICAgeCA9ICJFc3Ryb2dlbiBSZWNlcHRvciBTdGF0dXMiLAogICAgICAgICB5ID0gIkV4cHJlc3Npb24gdmFsdWVzIG9mIEJBWCIpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gTlVMTCkgKyAKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpwcmludChnKQoKIyMgTUFQM0s4IGdlbmUgIyMKCiMgRVRDLi4uLgpgYGAKCkhvd2V2ZXIsIGFzIGFscmVhZHkgc3RhdGVkLCB0aGlzIGlzIHRlZGlvdXMgYW5kIGVycm9yLXByb25lLiAKClNvIGluc3RlYWQsIGxldHMgYXV0b21hdGUgaXQhCgojIyBFeGVyY2lzZQoKQ29udmVydCB0aGUgY29kZSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBib3hwbG90cyBpbiB0aGUgc2Vjb25kIGV4YW1wbGUgb2YgY29weS9wYXN0ZS9lZGl0CmludG8gYSBgZm9yYCBsb29wLgoKV2hpY2ggdmFyaWFibGVzIGFyZSB5b3UgbG9vcGluZyBvdmVyPwoKSGludDogQ29uc2lkZXIgdXNpbmcgdGhlIGZ1bmN0aW9uIGBwYXN0ZSgpYCB0byBoZWxwIGNoYW5nZSB0aGUgeS1heGlzIGxhYmVsIGFuZCB0aXRsZSB0byByZWZsZWN0IHRoZQpnZW5lIHRoYXQgaXMgYmVpbmcgcGxvdHRlZC4KCiMjIFNhdmluZyBtdWx0aXBsZSBwbG90cwoKWW91IGNhbiBzYXZlIGFsbCB0aGUgcGxvdHMgcHJvZHVjZWQgYnkgYSBgZm9yYCBsb29wIGluIGEgUERGIGJ5IHVzaW5nIHRoZSBgcGRmKClgIGFuZCBgZGV2Lm9mZigpYCBmdW5jdGlvbnMgKm91dHNpZGUqIHRoZSBgZm9yYCBsb29wLgpUaGlzIHdpbGwgY3JlYXRlIG9uZSBmaWxlIHdpdGggYWxsIHRoZSBwbG90cy4KCmBgYApwZGYoIm15cGxvdHMucGRmIikKZm9yIChlbGVtZW50IGluIHZlY3RvcikgeyAgIAogICAgIyBwbG90IGNvZGUKfQpkZXYub2ZmKCkKYGBgCgpPdGhlcndpc2UsIGFub3RoZXIgb3B0aW9uIGlzIHRvIGNyZWF0ZSBhIFBERiAob3IgUE5HL0pQRUcpIGZvciBlYWNoIGdyYXBoICppbnNpZGUqIHRoZSBgZm9yYCBsb29wLiAKCmBgYApmb3IgKGVsZW1lbnQgaW4gdmVjdG9yKSB7CiAgICBwZGYoZWxlbWVudCkKICAgICMgcGxvdCBjb2RlICAgCiAgICBkZXYub2ZmKCkKfQpgYGAKCiMjIFN0b3JpbmcgcmVzdWx0cwoKTm90ZSB0aGF0IHRoaXMgYGZvcmAgbG9vcCBpcyBoZWxwaW5nIHVzIGlkZW50aWZ5IHRoZSBudW1iZXIgb2YgZ2VuZXMgaW4gZWFjaCBjaHJvbW9zb21lIGJ1dCB0aGUKcmVzdWx0IGlzIG5vdCBzdG9yZWQuIFRodXMsIHdlIGNhbiBub3QgYWNjZXNzIHRoZSByZXN1bHRzIGF0IGEgbGF0ZXIgdGltZS4KCi0gV2hlbiBzdG9yaW5nIHRoZSByZXN1bHRzIGZyb20gYSBsb29wLCB3ZSBvZnRlbiBjcmVhdGUgYW4gZW1wdHkgdmFyaWFibGUgYmVmb3JlIHN0YXJ0aW5nIHRoZSBmb3IgbG9vcAotIFRoaXMgaXMgdXNlZCBzdG9yZSB0aGUgcmVzdWx0IGF0IGVhY2ggaXRlcmF0aW9uIG9mIHRoZSBsb29wCgpgYGB7cn0KbnVtR2VuZXMgPC0gTlVMTApjaHJvbSA8LSBjKCJjaHIxIiwgImNocjUiLCAiY2hyNiIsICJjaHI4IikKZm9yKGNociBpbiBjaHJvbSkgewogIG51bVJvd3MgPC0gZXhwcnNBbm5vICU+JQogICAgICBmaWx0ZXIoQ2hyb21vc29tZSA9PSBjaHIpICU+JQogICAgICBzdW1tYXJpc2UobigpKQogIG51bUdlbmVzW2Nocl0gPC0gbnVtUm93cwogIH0KbnVtR2VuZXMKYGBgCgoKIyMgRXhlcmNpc2UKSWRlbnRpZnkgdGhlIG51bWJlciBvZiBwYXRpZW50cyB3aG8gYXJlIHBvc2l0aXZlIGZvciBFUi9QUi9IRVIyIHdoZW4gY29uc2lkZXJpbmcgZWFjaCBzdGF0dXMKaW5kaXZpZHVhbGx5LiBTdG9yZSB0aGUgcmVzdWx0cyBpbiBhIHZlY3Rvci4KCmBgYHtyfQpzdGF0dXMgPC0gYygiZXIiLCAicHIiLCAiaGVyMiIpCiMjIyBZb3VyIGFuc3dlciBoZXJlICMjIwpgYGAKCgojI0NvbmRpdGlvbmFsIGJyYW5jaGluZzogQ29tbWFuZHMgYW5kIGZsb3cgY29udHJvbAoKV2hhdCBpZiB3ZSBvbmx5IHdhbnRlZCB0byBjcmVhdGUgYm94cGxvdHMgZm9yIGdlbmVzIGlmIHRoZXkgbWV0IGNlcnRhaW4gY3JpdGVyaWEsIGZvciBleGFtcGxlLCBpZiB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZSBkaWZmZXJlZCBiZXR3ZWVuIEVSIC12ZSBhbmQgRVIgK3ZlLiBUbyBkbyB0aGF0IHdlIGNvdWxkIHVzZSBhbiBpZiBzdGF0ZW1lbnQuCgotIFVzZSBhbiBgaWZgIHN0YXRlbWVudCBmb3IgYW55IGtpbmQgb2YgY29uZGl0aW9uIHRlc3RpbmcKLSBEaWZmZXJlbnQgb3V0Y29tZXMgY2FuIGJlIHNlbGVjdGVkIGJhc2VkIG9uIGEgY29uZGl0aW9uIHdpdGhpbiBicmFja2V0cwoKYGBgCmlmIChjb25kaXRpb24pIHsKICAuLi4gZG8gdGhpcyAuLi4KICB9IGVsc2UgewogICAgLi4uIGRvIHNvbWV0aGluZyBlbHNlIC4uLgogICAgfQpgYGAKCi0gYGNvbmRpdGlvbmAgaXMgYW55IGxvZ2ljYWwgdmFsdWUsIGFuZCBjYW4gY29udGFpbiBtdWx0aXBsZSBjb25kaXRpb25zLiAKICAgICsgZS5nLiBgKGEgPT0gMiAmIGIgPCA1KWAsIHRoaXMgaXMgYSBjb21wb3VuZCBjb25kaXRpb25hbCBhcmd1bWVudAotIFRoZSBjb25kaXRpb24gc2hvdWxkIHJldHVybiBhICpzaW5nbGUqIHZhbHVlIG9mIGBUUlVFYCBvciBgRkFMU0VgCgogICAgCiMjIE90aGVyIGNvbmRpdGlvbmFsIHRlc3RzCgotIFRoZXJlIGFyZSB2YXJpb3VzIHRlc3RzIHRoYXQgY2FuIGNoZWNrIHRoZSB0eXBlIG9mIGRhdGEgc3RvcmVkIGluIGEgdmFyaWFibGUKICAgICsgdGhlc2UgdGVuZCB0byBiZSBjYWxsZWQgKipgaXMuLi4oKWAqKi4gCiAgICAgICAgKyB0cnkgKnRhYi1jb21wbGV0ZSogb24gYGlzLmAKCmBgYHtyfQppcy5udW1lcmljKDEwKQppcy5udW1lcmljKCJURU4iKQppcy5jaGFyYWN0ZXIoMTApCmBgYAoKLSBgaXMubmEoKWAgaXMgdXNlZnVsIGZvciBzZWVpbmcgaWYgYW4gYE5BYCB2YWx1ZSBpcyBmb3VuZAogICAgKyBjYW5ub3QgdXNlIGA9PSBOQWAhCgpgYGB7cn0KeCA8LSBjKDEsIDIsIE5BKQptZWFuKHgpCmlzLm5hKHgpCmBgYAoKIyMgRXhhbXBsZQpDaGVja2luZyBpZiBhIGdlbmUgb2YgaW50ZXJlc3QgaXMgaW4gdGhlIGRhdGFzZXQKICAgIC0gVGhlcmUgYXJlIDIgbWV0aG9kcyB0aGF0IGNvdWxkIGJlIHVzZWQgdG8gYWNoaWV2ZSB0aGlzCiAgICAxLiBNYWtlIHVzZXMgb2YgdGhlIGZ1bmN0aW9ucyBgYWxsKClgIGFuZCBgYW55KClgCiAgICAyLiBNYWtlIHVzZSBvZiB0aGUgZnVuY3Rpb24gYHN1bSgpYAoKYGBge3J9CiMjIE1ldGhvZCAxCmlmIChhbnkoZXhwcnNBbm5vJEhVR08uZ2VuZS5zeW1ib2wgPT0gIlBJSzNDQSIpKXsKICAgIHByaW50KCJQSUszQ0EgaXMgaW4gdGhlIGRhdGFzZXQiKQp9ZWxzZXsKICAgIHByaW50KCJQSUszQ0EgaXMgbm90IGluIHRoZSBkYXRhc2V0IikKfQoKIyMgTWV0aG9kIDIKaWYgKHN1bShleHByc0Fubm8kSFVHTy5nZW5lLnN5bWJvbCA9PSAiUElLM0NBIikgPT0gMSl7CiAgICBwcmludCgiUElLM0NBIGlzIGluIHRoZSBkYXRhc2V0IikKfWVsc2V7CiAgICBwcmludCgiUElLM0NBIGlzIG5vdCBpbiB0aGUgZGF0YXNldCIpCn0KYGBgCgojIyBFeGVyY2lzZQpXcml0ZSBhbiBgaWYgZWxzZWAgc3RhdGVtZW50IHRvIGNoZWNrIGlmICphbGwqIG9mIHRoZSBmb2xsb3dpbmcgZ2VuZXMgYXJlIGluIHRoZSBkYXRhc2V0OgpCQ0wyLCBIT1hBOSwgTUFQSzEsIEFSSUQxQSwgR0FUQTMsIEVTUjEKCkhpbnQ6IE1ha2UgdXNlIG9mIGAlaW4lYAoKCiMjIE90aGVyIHVzZWZ1bCBjb25kaXRpb25hbHMKYGZpbGUuZXhpc3RzKClgLCBgZGlyLmV4aXN0cygpYCBhbmQgYGRpci5jcmVhdGUoKWAgYXJlIGFsc28gZnVuY3Rpb25zIHRoYXQgYXJlIHVzZWZ1bCBpbiBgaWYgZWxzZWAgc3RhdGVtZW50cy4KUGFydGljdWxhcmx5LCB3aGVuIHdyaXRpbmcgc2NyaXB0cyB0aGF0IHRha2UgaW5wdXRzIGZyb20gdGhlIGNvbW1hbmRsaW5lLgoKCiMjIENvbWJpbmluZyBMb29wcyBhbmQgQ29uZGl0aW9uYWwgYnJhbmNoaW5nCgpVc2luZyB0aGUgKipgZm9yYCoqIGxvb3Agd2Ugd3JvdGUgYmVmb3JlLCB3ZSBjb3VsZCBhZGQgYW4gYGlmIGVsc2VgIGJyYW5jaCB0byBpZGVudGlmeSBpZiB0aGUgZGlmZmVyZW5jZQpiZXR3ZWVuIHRoZSBtZWFuIGV4cHJlc3Npb24gZm9yIGEgZ2VuZSBkaWZmZXJzIGJldHdlZW4gdGhlIGdyb3VwcyBFUiBwb3NpdGl2ZS9uZWdhdGl2ZS4KCiMjIEF1dG9tYXRpb24gRXhhbXBsZSAzIC0gQ3JlYXRpbmcgYm94cGxvdHMgaWYgZXhwcmVzc2lvbiBkaWZmZXJzIGJldHdlZW4gRVIgLXZlIGFuZCBFUiArdmUKCldlIGFyZSBpbnRlcmVzdGVkIGluIGZpbmRpbmcgb3V0IGlmIHRoZSBmb2xsb3dpbmcgZ2VuZXM6ICAKQU1QRDMsIFRFQ1RBLCBUUlBWNCwgQ0QyNDQsIEFCSEQxMCwgR0VNSU44LCBNQVAxQSwgU01BRDcKaGF2ZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIEVSIHN0YXR1cyBvZiBncmVhdGVyIHRoYW4gMC4wNC4KCi0gSGVyZSdzIGhvdyB3ZSBjYW4gY29tYmluZSBhIGBmb3JgIGxvb3AgYW5kIGFuIGBpZmAgc3RhdGVtZW50IHRvIHRlc3QgZm9yIHRoaXMKICAgICoqYGZvcmAqKiBlYWNoIGl0ZXJhdGlvbiBvZiB0aGUgbG9vcDoKICAgICAgICAxLiBJZGVudGlmeSB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBnZW5lIG9mIGludGVyZXN0CiAgICAgICAgMi4gR3JvdXAgdGhlIHNhbXBsZXMgYmFzZWQgb24gdGhlcmUgRVIgc3RhdHVzLCBhbmQgZmluZCB0aGUgbWVhbiBmb3IgZWFjaCBncm91cAogICAgICAgIDMuICoqYGlmYCoqIHRoZSBhYnNvbHV0ZSBkaWZmZXJlbmNlIGlzIGdyZWF0ZXIgdGhhbiAwLjA0LCBwcmludCBhIHN0YXRlbWVudCB0aGF0IGluZm9ybXMgdXMgb2YgdGhpcwogICAgICAgIDQuICoqYGVsc2VgKiosIGRvIG5vdGhpbmcKYGBge3J9Cm15Z2VuZXMgPC0gYygiQU1QRDMiLCAiVEVDVEEiLCAiVFJQVjQiLCAiQ0QyNDQiLCAiQUJIRDEwIiwgIkdFTUlOOCIsICJNQVAxQSIsICJTTUFENyIpCgpmb3IgKGdlbiBpbiBteWdlbmVzKXsKICAgIGZpbHRlcmVkIDwtIGV4cHJzQW5ubyAlPiUKICAgICAgICBmaWx0ZXIoSFVHTy5nZW5lLnN5bWJvbCA9PSBnZW4pICU+JQogICAgICAgIHNlbGVjdChzdGFydHNfd2l0aCgiTktJIikpICU+JQogICAgICAgIHQoKSAlPiUKICAgICAgICBkYXRhLmZyYW1lKCkKICAgIGNvbG5hbWVzKGZpbHRlcmVkKSA8LSAiZ2VuZSIKICAgIHggPC0gZmlsdGVyZWQgJT4lIAogICAgICAgIGdyb3VwX2J5KGZhY3RvcihwYXRpZW50c1ssICJlciJdKSkgJT4lCiAgICAgICAgc3VtbWFyaXNlKCJleHByc0F2IiA9IG1lYW4oZ2VuZSwgbmEucm0gPSBUUlVFKSkKICAgIGRpZmYgPC0geCRleHByc0F2WzFdIC0geCRleHByc0F2WzJdCiAgICBpZiAoYWJzKGRpZmYpID4gMC4wNCl7CiAgICAgICAgcHJpbnQocGFzdGUoIlRoZSBkaWZmZXJlbmNlIGJldHdlZW4gRVIgc3RhdHVzIGZvciIsIGdlbiwgImlzIiwgYWJzKGRpZmYpLCAid2hpY2ggaXMgZ3JlYXRlciB0aGFuIDAuMDQiKSkKICAgIH1lbHNlewogICAgICAgIHByaW50KHBhc3RlKCJUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIEVSIHN0YXR1cyBmb3IiLCBnZW4sICJpcyBub3QgZ3JlYXRlciB0aGFuIDAuMDQiKSkKICAgIH0KfQoKYGBgCgpXaGlsc3QgaXQgaXMgc2xpZ2h0bHkgaW5mb3JtYXRpdmUgdG8ga25vdyB0aGF0IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gbWVhbnMgb2YgRVIgc3RhdHVzIGZvciBlYWNoIGdlbmUuCkl0IHdvdWxkIGJlIG1vcmUgaW50ZXJlc3RpbmcgdG8gcGxvdCB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZXMgdGhhdCBoYXZlIGFuIGFic29sdXRlIGRpZmZlcmVuY2Ugb2YKZ3JlYXRlciB0aGFuIDAuMDQuCgpUaGVyZWZvcmUgd2Ugd2FudCB0byB0YWtlIHRoZSBzYW1lIHN0ZXBzIGFzIGFib3ZlIGJ1dCBjaGFuZ2UgZnJvbSBwcmludGluZyBhIHN0YXRlbWVudCB0byBwbG90dGluZyBhIGdyYXBoCi0gSGVyZSdzIGhvdyB3ZSBjYW4gdXNlIGFuIGBpZmAgc3RhdGVtZW50IHRvIHRlc3QgZm9yIHRoaXMKICAgICsgKipgZm9yYCoqIGVhY2ggaXRlcmF0aW9uIG9mIHRoZSB0aGUgbG9vcDoKICAgICAgICAxLiBJZGVudGlmeSB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBnZW5lIG9mIGludGVyZXN0CiAgICAgICAgMi4gR3JvdXAgdGhlIHNhbXBsZXMgYmFzZWQgb24gdGhlcmUgRVIgc3RhdHVzLCBhbmQgZmluZCB0aGUgbWVhbiBmb3IgZWFjaCBncm91cAogICAgICAgIDMuICoqYGlmYCoqIHRoZSBhYnNvbHV0ZSBkaWZmZXJlbmNlIGlzIGdyZWF0ZXIgdGhhbiAwLjA0LCBwcm9kdWNlIGEgYm94cGxvdCB0byBkZXBpY3QgdGhlIGRpZmZlcmVuY2UKICAgICAgICA0LiAqKmBlbHNlYCoqLCBkbyBub3RoaW5nCiAgICAgICAgCmBgYHtyfQpteWdlbmVzIDwtIGMoIkFNUEQzIiwgIlRFQ1RBIiwgIlRSUFY0IiwgIkNEMjQ0IiwgIkFCSEQxMCIsICJHRU1JTjgiLCAiTUFQMUEiLCAiU01BRDciKQoKZm9yIChnZW4gaW4gbXlnZW5lcyl7CiAgICBmaWx0ZXJlZCA8LSBleHByc0Fubm8gJT4lCiAgICAgICAgZmlsdGVyKEhVR08uZ2VuZS5zeW1ib2wgPT0gZ2VuKSAlPiUKICAgICAgICBzZWxlY3Qoc3RhcnRzX3dpdGgoIk5LSSIpKSAlPiUKICAgICAgICB0KCkgJT4lCiAgICAgICAgZGF0YS5mcmFtZSgpCiAgICBjb2xuYW1lcyhmaWx0ZXJlZCkgPC0gImdlbmUiCiAgICB4IDwtIGZpbHRlcmVkICU+JSAKICAgICAgICBncm91cF9ieShmYWN0b3IocGF0aWVudHNbLCAiZXIiXSkpICU+JQogICAgICAgIHN1bW1hcmlzZSgiZXhwcnNBdiIgPSBtZWFuKGdlbmUsIG5hLnJtID0gVFJVRSkpCiAgICBkaWZmIDwtIHgkZXhwcnNBdlsxXSAtIHgkZXhwcnNBdlsyXQogICAgaWYgKGFicyhkaWZmKSA+IDAuMDQpewogICAgICAgIGcgPC0gZ2dwbG90KGZpbHRlcmVkLCBhZXMoeSA9IGdlbmUsIHggPSBmYWN0b3IocGF0aWVudHNbLCAiZXIiXSksIGZpbGwgPSBmYWN0b3IocGF0aWVudHNbLCAiZXIiXSkpKSArCiAgICAgICAgICAgIGdlb21fYm94cGxvdCgpCiAgICAgICAgcHJpbnQoZykKICAgIH1lbHNlewogICAgICAgIG5leHQKICAgIH0KfQpgYGAKCiMjIEV4ZXJjaXNlCkFkZCBhZGRpdGlvbmFsIGxheWVycyB0byB0aGUgYWJvdmUgcGxvdCBzbyB0aGF0IHRoZSBsYWJlbHMgYXJlIG1vcmUgaW5mb3JtYXRpdmUuClNlZSBwbG90IGJlbG93LCByZXBsYWNlICJ4IiBpbiB0aGUgbGFicyBmb3IgdGhlIGdlbmUgdGhhdCB3ZSBhcmUgaW52ZXN0aWdhdGluZzogIApBbHNvLCBjaGFuZ2UgdGhlIGNvbG91cnMgYXNzb2NpYXRlZCB3aXRoIGVhY2ggRVIgc3RhdHVzLgoKIVtdKEZpbmFsX2JveHBsb3QucG5nKQoKIyNDb2RlIGZvcm1hdHRpbmcgYXZvaWRzIGJ1Z3MhCkNvbXBhcmU6CmBgYHtyIGV2YWw9RkFMU0V9CmZvciAoYSBpbiBhZ2VzKXsKbnVtUm93cyA8LSBwYXRpZW50cyAlPiUKZmlsdGVyKGFnZSA+IGEpICU+JQpzdW1tYXJpc2UobigpKQppZihudW1Sb3dzIDwgMTApewpzIDwtIHBhc3RlKCJUaGVyZSBhcmUgZmV3ZXIgdGhhbiAxMCBwYXRpZW50cyB5b3VuZ2VyIHRoYW4iLCBhKQpwcmludChzKQp9ZWxzZXsKcyA8LSBwYXN0ZSgiVGhlcmUgYXJlIG1vcmUgdGhhbiAxMCBwYXRpZW50cyB5b3VuZ2VyIHRoYW4iLCBhKQpwcmludChzKQp9Cn0KYGBgCnRvOgpgYGB7ciBldmFsPUZBTFNFfQpmb3IgKGEgaW4gYWdlcykgewogICAgbnVtUm93cyA8LSBwYXRpZW50cyAlPiUKICAgICAgICBmaWx0ZXIoYWdlID4gYSkgJT4lCiAgICAgICAgc3VtbWFyaXNlKG4oKSkKICAgIGlmIChudW1Sb3dzIDwgMTApIHsKICAgICAgICBzIDwtIHBhc3RlKCJUaGVyZSBhcmUgZmV3ZXIgdGhhbiAxMCBwYXRpZW50cyB5b3VuZ2VyIHRoYW4iLCBhKQogICAgICAgIHByaW50KHMpCiAgICB9IGVsc2V7CiAgICAgICAgcyA8LSBwYXN0ZSgiVGhlcmUgYXJlIG1vcmUgdGhhbiAxMCBwYXRpZW50cyB5b3VuZ2VyIHRoYW4iLCBhKQogICAgICAgIHByaW50KHMpCiAgICB9Cn0KYGBgCi0gVGhlIGNvZGUgYmV0d2VlbiBicmFja2V0cyBge31gICphbHdheXMqIGlzICppbmRlbnRlZCosIHRoaXMgY2xlYXJseSBzZXBhcmF0ZXMgd2hhdCBpcyBleGVjdXRlZCBvbmNlLCBhbmQgd2hhdCBpcyBydW4gbXVsdGlwbGUgdGltZXMKLSBUcmFpbGluZyBicmFja2V0IGB9YCBhbHdheXMgYWxvbmUgb24gdGhlIGxpbmUgYXQgdGhlIHNhbWUgaW5kZW50YXRpb24gbGV2ZWwgYXMgdGhlIGluaXRpYWwgYnJhY2tldCBge2AKLSBVc2Ugd2hpdGUgc3BhY2VzIHRvIGRpdmlkZSB0aGUgaG9yaXpvbnRhbCBzcGFjZSBiZXR3ZWVuIHVuaXRzIG9mIHlvdXIgY29kZSwgZS5nLiBhcm91bmQgYXNzaWdubWVudHMsIGNvbXBhcmlzb25zCgo=