El presente tutorial está basado en la publicación del blog variance explained de David Robinson. El material ha sido actualizado y adaptado al objetivo del curso.

Objetivo

  • Integrar las herramientas del tidyverse con los flujos de trabajo de Bioconductor para el análisis de microarreglos.

Nota: Todos los outputs deben verse como líneas corridas. Disminuir el zoom de ser necesario.

Contexto

tidy data no lo es todo.

  • From the posts so far, one might get the impression that I think data must be tidy at every stage of an analysis. Not true! That would be an absurd and unnecessary constraint. Lots of mathematical operations are faster on matrices, such as singular value decomposition or linear regression. Jeff Leek rightfully points this out as an issue with my previous modeling gene expression post, where he remarks that the limma package is both faster and takes more statistical considerations (pooling variances among genes) into account.

  • Isn’t it contradictory to do these kinds of operations in a tidy analysis? Not at all. My general recommendation is laid out as follows:

workflow

workflow

  • As long as you’re in that Models “cloud”, you can store your data in whatever way is computationally and practically easiest. However:

    • Before you model, you should use tidy tools to clean, process and wrangle your data (as shown in previous posts)
    • After you’ve performed your modeling, you should turn the model into a tidy output for interpretation, visualization, and communication
  • This requires a new and important tool in our series on tidy bioinformatics analysis: the biobroom package, written and maintained by my former colleagues, particularly Andy Bass and John Storey. In this post I’ll show how to use the limma and biobroom packages in combination to continue a tidy analysis, and consider when and how to use non-tidy data in an analysis.

Dependencies

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats

Importe y limpieza de datos

library(dplyr)
library(tidyr)
original_data <- readRDS("data-raw/tidymicro.rds")
cleaned_data <- original_data %>%
  separate(NAME, c("name", "BP", "MF", "systematic_name", "number"), sep = "\\|\\|") %>%
  mutate_at(vars(name:systematic_name), funs(trimws)) %>%
  select(-number, -GID, -YORF, -GWEIGHT) %>%
  gather(sample, expression, G0.05:U0.3) %>% #dplyr::count(sample)
  separate(sample, c("nutrient", "rate"), sep = 1, convert = TRUE) %>% 
  mutate(nutrient=forcats::fct_recode(nutrient, 
                                      "Glucose" = "G", "Leucine" = "L",
                                      "Phosphate" = "P", "Sulfate" = "S",
                                      "Ammonia" = "N", "Uracil" = "U")) %>% 
  filter(!is.na(expression),systematic_name!="") %>% 
  group_by(systematic_name, nutrient) %>%
  filter(n()==6) %>% 
  ungroup() %>% 
  glimpse()
Observations: 195,222
Variables: 7
$ name            <chr> "SFB2", "", "QRI7", "CFT2", "SSO2", "PSP2", "RIB2", "VMA13", "EDC3"...
$ BP              <chr> "ER to Golgi transport", "biological process unknown", "proteolysis...
$ MF              <chr> "molecular function unknown", "molecular function unknown", "metall...
$ systematic_name <chr> "YNL049C", "YNL095C", "YDL104C", "YLR115W", "YMR183C", "YML017W", "...
$ nutrient        <fctr> Glucose, Glucose, Glucose, Glucose, Glucose, Glucose, Glucose, Glu...
$ rate            <dbl> 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0...
$ expression      <dbl> -0.24, 0.28, -0.02, -0.33, 0.05, -0.69, -0.55, -0.75, -0.24, -0.16,...
  • plot_expression_data function
plot_expression_data <- function(expression_data) {
  ggplot(expression_data, aes(rate, expression, color = nutrient)) +
    geom_point() +
    geom_smooth(method = "lm", se = FALSE) +
    facet_wrap(~name + systematic_name, scales = "free_y")
}

limma

Aplicación de regresión lineal por observación

  • Why a computational biologist should not run a gene expression analysis with traditional statistics?

    • Performing thousands of linear regressions with separate lm calls is slow. It takes about a minute on my computer. There are computational shortcuts we can take when all of our data is in the form of a gene-by-sample matrix.
    • We’re not taking statistical advantage of the shared information. Modern bioinformatics approaches often “share power” across genes, by pooling variance estimates. The approach in the limma package is one notable example for microarray data, and RNA-Seq tools like edgeR and DESeq2 take a similar approach in their negative binomial models.
  • We’d like to take advantage of the sophisticated biological modeling tools in Bioconductor. We’re thus going to convert our data into a non-tidy format (a gene by sample matrix), and run it through limma to create a linear model for each gene. Then when we want to visualize, compare, or otherwise manipulate our models, we’ll tidy the model output using biobroom.

Setup

  • Most gene expression packages in Bioconductor expect data to be in a matrix with one row per gene and one column per sample. We would like to fit one model for each gene and nutrient combination. So let’s set it up that way using reshape2’s acast()

(We could have used tidyr’s spread function, but acast actually saves us a few steps by giving us a matrix with rownames, rather than a data frame, right away.)

library(reshape2)
exprs <- acast(cleaned_data, systematic_name + nutrient ~ rate,
               value.var = "expression")
head(exprs)
                 0.05   0.1  0.15   0.2  0.25  0.3
Q0017_Glucose   -0.21 -0.26 -0.17  0.00  0.10 0.10
Q0017_Leucine    0.46  0.05  0.42  0.35  0.36 0.09
Q0017_Ammonia    0.18  0.73  0.05 -0.14 -0.06 0.24
Q0017_Phosphate -0.24 -0.35 -0.45 -0.24 -0.35 0.11
Q0017_Sulfate    2.44  0.64  0.21  0.40 -0.07 0.31
Q0017_Uracil     1.88  0.03  0.15  0.04  0.14 0.32
  • We then need to extract the experiment design, which in this case is just the growth rate:
rate <- as.numeric(colnames(exprs))
rate
[1] 0.05 0.10 0.15 0.20 0.25 0.30
  • limma (“linear modeling of microarrays”) is one of the most popular Bioconductor packages for performing linear-model based differential expression analyses on microarray data. With the data in this matrix form, we’re ready to use it:
library(limma)
# linear model structure: 
# lmFit(data_matrix, experiment_design)
# check the experiment_design: 
# model.matrix(~rate)
fit <- lmFit(exprs, model.matrix(~rate))
eb <- eBayes(fit)
  • This performs a linear regression for each gene. This operation is both faster and more statistically sophisticated than lm.

  • So now we’ve performed our regression. What output do we get?

#class(fit)
#summary(fit)
class(eb)
[1] "MArrayLM"
attr(,"package")
[1] "limma"
summary(eb)
                 Length Class  Mode     
coefficients     65074  -none- numeric  
rank                 1  -none- numeric  
assign               2  -none- numeric  
qr                   5  qr     list     
df.residual      32537  -none- numeric  
sigma            32537  -none- numeric  
cov.coefficients     4  -none- numeric  
stdev.unscaled   65074  -none- numeric  
pivot                2  -none- numeric  
Amean            32537  -none- numeric  
method               1  -none- character
design              12  -none- numeric  
df.prior             1  -none- numeric  
s2.prior             1  -none- numeric  
var.prior            2  -none- numeric  
proportion           1  -none- numeric  
s2.post          32537  -none- numeric  
t                65074  -none- numeric  
df.total         32537  -none- numeric  
p.value          65074  -none- numeric  
lods             65074  -none- numeric  
F                32537  -none- numeric  
F.p.value        32537  -none- numeric  
  • That’s a lot of outputs, and many of them are matrices of varying shapes. If you want to work with this using tidy tools (and if you’ve been listening, you hopefully do), we need to tidy it:

Tidy model

library(biobroom)
head(tidy(eb, intercept = TRUE))
  • Notice that this is now in one-row-per-coefficient-per-gene form, much like the output of broom’s tidy() on linear models.

  • Like broom, biobroom always returns a table without rownames that we can feed into standard tidy tools like dplyr and ggplot2. (Note that unlike broom, biobroom requires an intercept = TRUE argument to leave the intercept term, simply because in many genomic datasets- though not ours- the intercept term is almost meaningless). biobroom can also tidy model objects from other tools like edgeR or DESeq2, always giving a consistent format similar to this one.

  • Now all we’ve got to do split the systematic name and nutrient back up. tidyr’s separate() can do this:

td <- eb %>% 
  biobroom::tidy.MArrayLM(intercept = TRUE) %>% 
  separate(gene, c("systematic_name","nutrient"), sep="_") %>% glimpse()
Observations: 65,074
Variables: 7
$ systematic_name <chr> "Q0017", "Q0017", "Q0017", "Q0017", "Q0017", "Q0017", "Q0045", "Q00...
$ nutrient        <chr> "Glucose", "Leucine", "Ammonia", "Phosphate", "Sulfate", "Uracil", ...
$ term            <chr> "(Intercept)", "(Intercept)", "(Intercept)", "(Intercept)", "(Inter...
$ estimate        <dbl> -0.35333333, 0.38733333, 0.39266667, -0.44933333, 1.91400000, 1.184...
$ statistic       <dbl> -3.17249838, 2.33936601, 1.59129359, -2.73252887, 3.93706135, 2.476...
$ p.value         <dbl> 1.559960e-02, 5.180100e-02, 1.554594e-01, 2.915825e-02, 5.595522e-0...
$ lod             <dbl> -4.0295194, -5.2780422, -6.3498508, -4.6871409, -2.9298317, -5.0718...

Analyse tidy model

  • Notice that there are two rows for each gene-nutrient combination: an intercept and a slope term. This is simplifying each gene-nutrient combination into two values:

    • Intercept: How highly expressed the gene is when it’s starved of that nutrient.
    • rate: How much the gene’s expression responds to an increasing supply of that nutrient (and therfore an increasing growth rate)
td %>% count(term)
  • We’ll take a brief look at those one at a time.

Intercepts

intercept_terms <- td %>% filter(term=="(Intercept)") 
intercept_terms %>% head()
  • The p-values aren’t actually interesting to us here: they’re testing whether the intercept is equal to 0, which is not a particularly special number in terms of these normalized gene expressions. (Confidence intervals and standard errors would be).

  • What we’re really interested in is the value of each intercept relative to the other nutrients in that gene. For example, let’s again consider our favorite gene, LEU1.

cleaned_data %>% 
  filter(name=="LEU1") %>% 
  ggplot(aes(rate,expression,colour=nutrient))+
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)+
  geom_hline(aes(yintercept=mean(expression)),linetype="dashed")

  • This gene has a low intercept term for all nutrients except leucine. I’ve marked the average intercept with a horizontal dashed line to demonstrate this. Suppose we want to look for other genes like this, with a single outlying intercept term. We could do this by centering the intercepts around the average for each gene, using a group_by and mutate:
centered_intercepts <- intercept_terms %>%
  group_by(systematic_name) %>%
  mutate(centered_intercept = estimate - mean(estimate)) %>%
  ungroup()
  • Now we are interested in the most extreme cases, where the intercept is very far from the other nutrients. The top_n function is useful for this.
top_intercept <- centered_intercepts %>%
  top_n(20, centered_intercept)
  • Note that here I’m looking for cases where a single nutrient was greatly overexpressed in starvation (to look for underexpressed nutrients, we could have used -centered_intercept instead). We can then pull these genes out of the original data with the useful semi_join, at which point we can graph it with our plot_expression_data function:
cleaned_data %>%
  semi_join(top_intercept, by = "systematic_name") %>%
  plot_expression_data()

  • These certainly do look like interesting genes! We notice that some genes, like PHO11, only one nutrient is highly expressed while the rest show low expression, while other genes, such as ADH2, show varying levels of expression for each nutrient. We also notice that in most cases the highly expressed nutrient is moving back down towards the others as growth rate increases (that is, as the yeast is less starved). This makes sense, since it’s the starvation that is eliciting the unusual behavior.

  • What do these genes do? Beats me; I’m not a biologist, I just play one on my degree. But it certainly looks promising that PHO11 and PHO12 are both much higher expressed when phosphorus is the limiting nutrient, as well as SUL1 when sulfur is rare- and indeed each gene is involved in transport of that nutrient. (And we do see our Gene of the Week, LEU1).

  • Looking up the others in yeastgenome.org, we see that a lot of them are involved in transport across membranes (e.g. DAL5, GAP1, QDR2). This makes sense: the cell notices that it is missing a nutrient, and puts more energy into importing it. Notice that this would be a great way to make inferences about genes whose function we don’t yet know. (This is the focus of functional genomics).

Slopes

  • Now let’s take a look at the slope terms, which shows whether each gene increased or decreased its growth rate in a particular condition.
slope_terms <- td %>% filter(term=="rate") 
slope_terms %>% head()
  • Here, we’ll focus a bit more on statistical significance. First we can make a histogram of the p-values. These p-values are spread across six different nutrients, so we’ll facet our histogram by those nutrients:
ggplot(slope_terms, aes(p.value)) +
  geom_histogram(binwidth = .05) +
  facet_wrap(~nutrient)

  • link para el post de @drob para la interpretación de histogramas de p_values

  • In this case, we can see that the tests are generally well-behaved, with a mix of nulls (genes that don’t respond to growth rate) and alternatives (genes that do). Thus, we can use p-value correction to identify significant genes.

  • Or make a volcano plot, comparing statistical significance to effect size (here let’s say just on the slope terms):
td %>%
  filter(term == "rate") %>%
  ggplot(aes(estimate, p.value)) +
  geom_point() +
  facet_wrap(~ nutrient, scales = "free") +
  scale_y_log10() 

  • We could easily perform for multiple hypothesis testing within each group, and filter for significant (say, FDR < 1%) changes:
td_filtered <- td %>%
  group_by(term, nutrient) %>%
  mutate(fdr = p.adjust(p.value, method = "fdr")) %>%
  ungroup() %>%
  filter(fdr < .01)

Or finding the top few significant changes in each group using dplyr’s top_n:

top_3 <- td_filtered %>%
  filter(term == "rate") %>%
  group_by(nutrient) %>%
  top_n(3, abs(estimate))
top_3
  • We could join this with our original data, which would let us visualize the trends for only the most significant genes:
top_3 %>%
  rename(significant_nutrient = nutrient) %>%
  inner_join(cleaned_data, by = "systematic_name") %>%
  mutate(highlight = nutrient == significant_nutrient) %>%
  ggplot(aes(rate, expression, color = nutrient)) +
  geom_point() +
  geom_smooth(aes(lty = !highlight), method = "lm", se = FALSE, show.legend = FALSE) +
  facet_wrap(significant_nutrient ~ systematic_name, ncol = 3, scales = "free_y")

  • In short, you can once again use the suite of “tidy tools” that we’ve found powerful in genomic analyses.

Conclusion: La data está bajo tu poder

  • There’s a classic proverb of computer science from Abelman & Sussman: “Programs must be written for people to read, and only incidentally for machines to execute.” I’d say this is even more true for data it is for code. Data scientists need to be very comfortable engaging with their data, not fighting with the representation.

EXTRA: Gene-sets

  • These per-gene models can still be difficult to interpret biologically if you’re not familiar with the functions of specific genes. What we really want is a way to summarize the results into “genes involved in this biological process changed their expression.” This is where annotations of gene sets become useful.
gene_sets <- distinct(cleaned_data, systematic_name, BP, MF)
td %>%
  inner_join(gene_sets) %>%
  filter(BP == "leucine biosynthesis", term == "(Intercept)") %>%
  mutate(nutrient = reorder(nutrient, estimate, median)) %>%
  ggplot(aes(nutrient, estimate)) +
  geom_boxplot() +
  geom_point() +
  geom_text(aes(label = systematic_name), vjust = 1, hjust = 1) +
  xlab("Limiting nutrient") +
  ylab("Intercept (expression at low concentration)") +
  ggtitle("Genes involved in leucine biosynthesis")
Joining, by = "systematic_name"

  • Notice how clear it is that these genes respond to leucine starvation in particular. This can be applied to gene sets containing dozens or even hundreds of genes while still making the general trend apparent. Furthermore, we could use these summaries to look at many gene sets at once, and even use statistical tests to discover new gene sets that respond to starvation.

Computer environment

devtools::session_info()
Session info ----------------------------------------------------------------------------------
 setting  value                       
 version  R version 3.4.1 (2017-06-30)
 system   x86_64, linux-gnu           
 ui       RStudio (1.0.153)           
 language en_US                       
 collate  en_US.UTF-8                 
 tz       America/Lima                
 date     2017-08-03                  
Packages --------------------------------------------------------------------------------------
 package      * version date       source         
 assertthat     0.2.0   2017-04-11 CRAN (R 3.4.0) 
 base         * 3.4.1   2017-07-08 local          
 bindr          0.1     2016-11-13 cran (@0.1)    
 bindrcpp     * 0.2     2017-06-17 CRAN (R 3.4.1) 
 Biobase        2.32.0  2017-08-02 Bioconductor   
 biobroom     * 1.4.2   2017-08-02 Bioconductor   
 BiocGenerics   0.18.0  2016-06-23 Bioconductor   
 broom        * 0.4.2   2017-02-13 CRAN (R 3.4.0) 
 cellranger     1.1.0   2016-07-27 CRAN (R 3.4.0) 
 colorspace     1.3-2   2016-12-14 CRAN (R 3.4.0) 
 compiler       3.4.1   2017-07-08 local          
 datasets     * 3.4.1   2017-07-08 local          
 devtools       1.13.2  2017-06-02 CRAN (R 3.4.1) 
 digest         0.6.12  2017-01-27 CRAN (R 3.4.0) 
 dplyr        * 0.7.2   2017-07-20 CRAN (R 3.4.1) 
 forcats        0.2.0   2017-01-23 CRAN (R 3.4.0) 
 foreign        0.8-69  2017-06-21 CRAN (R 3.4.1) 
 ggplot2      * 2.2.1   2016-12-30 CRAN (R 3.4.0) 
 glue           1.1.1   2017-06-21 CRAN (R 3.4.1) 
 graphics     * 3.4.1   2017-07-08 local          
 grDevices    * 3.4.1   2017-07-08 local          
 grid           3.4.1   2017-07-08 local          
 gtable         0.2.0   2016-02-26 CRAN (R 3.4.0) 
 haven          1.1.0   2017-07-09 CRAN (R 3.4.1) 
 hms            0.3     2016-11-22 CRAN (R 3.4.0) 
 httr           1.2.1   2016-07-03 CRAN (R 3.4.0) 
 jsonlite       1.5     2017-06-01 cran (@1.5)    
 knitr          1.16    2017-05-18 cran (@1.16)   
 labeling       0.3     2014-08-23 CRAN (R 3.4.0) 
 lattice        0.20-35 2017-03-25 CRAN (R 3.3.3) 
 lazyeval       0.2.0   2016-06-12 CRAN (R 3.4.0) 
 limma        * 3.28.21 2017-08-02 Bioconductor   
 lubridate      1.6.0   2016-09-13 CRAN (R 3.4.0) 
 magrittr       1.5     2014-11-22 CRAN (R 3.4.0) 
 memoise        1.1.0   2017-04-21 CRAN (R 3.4.0) 
 methods      * 3.4.1   2017-07-08 local          
 mnormt         1.5-5   2016-10-15 CRAN (R 3.4.0) 
 modelr         0.1.1   2017-07-24 CRAN (R 3.4.1) 
 munsell        0.4.3   2016-02-13 CRAN (R 3.4.0) 
 nlme           3.1-131 2017-02-06 CRAN (R 3.4.0) 
 parallel       3.4.1   2017-07-08 local          
 pkgconfig      2.0.1   2017-03-21 cran (@2.0.1)  
 plyr           1.8.4   2016-06-08 CRAN (R 3.4.0) 
 psych          1.7.5   2017-05-03 CRAN (R 3.4.0) 
 purrr        * 0.2.2.2 2017-05-11 cran (@0.2.2.2)
 R6             2.2.2   2017-06-17 CRAN (R 3.4.1) 
 Rcpp           0.12.12 2017-07-15 CRAN (R 3.4.1) 
 readr        * 1.1.1   2017-05-16 CRAN (R 3.4.1) 
 readxl         1.0.0   2017-04-18 CRAN (R 3.4.0) 
 reshape2     * 1.4.2   2016-10-22 CRAN (R 3.4.0) 
 rlang          0.1.1   2017-05-18 cran (@0.1.1)  
 rvest          0.3.2   2016-06-17 CRAN (R 3.4.0) 
 scales         0.4.1   2016-11-09 CRAN (R 3.4.0) 
 stats        * 3.4.1   2017-07-08 local          
 stringi        1.1.5   2017-04-07 CRAN (R 3.4.0) 
 stringr        1.2.0   2017-02-18 CRAN (R 3.4.0) 
 tibble       * 1.3.3   2017-05-28 cran (@1.3.3)  
 tidyr        * 0.6.3   2017-05-15 CRAN (R 3.4.1) 
 tidyverse    * 1.1.1   2017-01-27 CRAN (R 3.4.0) 
 tools          3.4.1   2017-07-08 local          
 utils        * 3.4.1   2017-07-08 local          
 withr          2.0.0   2017-07-28 CRAN (R 3.4.1) 
 xml2           1.1.1   2017-01-24 CRAN (R 3.4.0) 

References

LS0tCnRpdGxlOiAiQ2FzbyBJSTogTGltcGllemEgZGUgTW9kZWxvcyBlbiBCaW9sb2fDrWEgQ29tcHV0YWNpb25hbCIKYXV0aG9yOiAiYXZhbGxlY2FtIgpkYXRlOiAnYHIgU3lzLkRhdGUoKWAnCm91dHB1dDoKICAjaHRtbF9kb2N1bWVudDoKICAjcGRmX2RvY3VtZW50OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogeWVzCiAgICAjdGhlbWU6IHVuaXRlZAogICAgI2NvZGVfZm9sZGluZzogImhpZGUiCiAgICAjZmlnX2NhcHRpb246IFRSVUUKICAgICNudW1iZXJfc2VjdGlvbnM6IFRSVUUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpvcHRpb25zKHdpZHRoID0gOTApICMgZXhwYW5kIGxpbWl0cyBvZiBDT05TT0xFIG91dHB1dApgYGAKCkVsIHByZXNlbnRlIHR1dG9yaWFsIGVzdMOhIGJhc2FkbyBlbiBsYSAKW3B1YmxpY2FjacOzbl0oaHR0cDovL3ZhcmlhbmNlZXhwbGFpbmVkLm9yZy9yL3RpZHktZ2Vub21pY3MtYmlvYnJvb20vI2ZucmVmOnNwcmVhZCkgZGVsIApibG9nIFt2YXJpYW5jZSBleHBsYWluZWRdKGh0dHA6Ly92YXJpYW5jZWV4cGxhaW5lZC5vcmcvKSAKZGUgW0RhdmlkIFJvYmluc29uXShodHRwczovL3R3aXR0ZXIuY29tL2Ryb2IpLgpFbCBtYXRlcmlhbCBoYSBzaWRvIGFjdHVhbGl6YWRvIHkgYWRhcHRhZG8gYWwgb2JqZXRpdm8gZGVsIGN1cnNvLgoKIyMgT2JqZXRpdm8KCi0gSW50ZWdyYXIgbGFzIGhlcnJhbWllbnRhcyBkZWwgYHRpZHl2ZXJzZWAgY29uIGxvcyBmbHVqb3MgZGUgdHJhYmFqbyBkZSBgQmlvY29uZHVjdG9yYCBwYXJhIGVsIGFuw6FsaXNpcyBkZSBtaWNyb2FycmVnbG9zLgoKX19Ob3RhOl9fIFRvZG9zIGxvcyBvdXRwdXRzIGRlYmVuIHZlcnNlIGNvbW8gbMOtbmVhcyBjb3JyaWRhcy4gX0Rpc21pbnVpciBlbCB6b29tIGRlIHNlciBuZWNlc2FyaW8uXwoKIyMgQ29udGV4dG8KCl9fX3RpZHkgZGF0YV8gbm8gbG8gZXMgdG9kby5fXwoKLSBGcm9tIHRoZSBwb3N0cyBzbyBmYXIsIG9uZSBtaWdodCBnZXQgdGhlIGltcHJlc3Npb24gdGhhdCBJIHRoaW5rIGRhdGEgbXVzdCBiZSB0aWR5IGF0IGV2ZXJ5IHN0YWdlIG9mIGFuIGFuYWx5c2lzLiBOb3QgdHJ1ZSEgVGhhdCB3b3VsZCBiZSBhbiBhYnN1cmQgYW5kIHVubmVjZXNzYXJ5IGNvbnN0cmFpbnQuIExvdHMgb2YgbWF0aGVtYXRpY2FsIG9wZXJhdGlvbnMgYXJlIGZhc3RlciBvbiBtYXRyaWNlcywgc3VjaCBhcyBzaW5ndWxhciB2YWx1ZSBkZWNvbXBvc2l0aW9uIG9yIGxpbmVhciByZWdyZXNzaW9uLiBKZWZmIExlZWsgcmlnaHRmdWxseSBwb2ludHMgdGhpcyBvdXQgYXMgYW4gaXNzdWUgd2l0aCBteSBwcmV2aW91cyBtb2RlbGluZyBnZW5lIGV4cHJlc3Npb24gcG9zdCwgd2hlcmUgaGUgcmVtYXJrcyB0aGF0IHRoZSBsaW1tYSBwYWNrYWdlIGlzIGJvdGggZmFzdGVyIGFuZCB0YWtlcyBtb3JlIHN0YXRpc3RpY2FsIGNvbnNpZGVyYXRpb25zIChwb29saW5nIHZhcmlhbmNlcyBhbW9uZyBnZW5lcykgaW50byBhY2NvdW50LgoKLSBJc27igJl0IGl0IGNvbnRyYWRpY3RvcnkgdG8gZG8gdGhlc2Uga2luZHMgb2Ygb3BlcmF0aW9ucyBpbiBhIHRpZHkgYW5hbHlzaXM/IE5vdCBhdCBhbGwuIE15IGdlbmVyYWwgcmVjb21tZW5kYXRpb24gaXMgbGFpZCBvdXQgYXMgZm9sbG93czoKCiFbd29ya2Zsb3ddKGZpZ3VyZS90aWR5XzAxLmpwZykKCi0gQXMgbG9uZyBhcyB5b3XigJlyZSBpbiB0aGF0IE1vZGVscyDigJxjbG91ZOKAnSwgeW91IGNhbiBzdG9yZSB5b3VyIGRhdGEgaW4gd2hhdGV2ZXIgd2F5IGlzIGNvbXB1dGF0aW9uYWxseSBhbmQgcHJhY3RpY2FsbHkgZWFzaWVzdC4gSG93ZXZlcjoKICAgIAogICAgLSBfX0JlZm9yZSB5b3UgbW9kZWxfXywgeW91IHNob3VsZCB1c2UgdGlkeSB0b29scyB0byBjbGVhbiwgcHJvY2VzcyBhbmQgd3JhbmdsZSB5b3VyIGRhdGEgKGFzIHNob3duIGluIHByZXZpb3VzIHBvc3RzKQogICAgLSBfX0FmdGVyIHlvdeKAmXZlIHBlcmZvcm1lZCB5b3VyIG1vZGVsaW5nX18sIHlvdSBzaG91bGQgdHVybiB0aGUgbW9kZWwgaW50byBhIHRpZHkgb3V0cHV0IGZvciBpbnRlcnByZXRhdGlvbiwgdmlzdWFsaXphdGlvbiwgYW5kIGNvbW11bmljYXRpb24KCi0gVGhpcyByZXF1aXJlcyBhIG5ldyBhbmQgaW1wb3J0YW50IHRvb2wgaW4gb3VyIHNlcmllcyBvbiB0aWR5IGJpb2luZm9ybWF0aWNzIGFuYWx5c2lzOiBfX3RoZSBgYmlvYnJvb21gIHBhY2thZ2VfXywgd3JpdHRlbiBhbmQgbWFpbnRhaW5lZCBieSBteSBmb3JtZXIgY29sbGVhZ3VlcywgcGFydGljdWxhcmx5IEFuZHkgQmFzcyBhbmQgSm9obiBTdG9yZXkuIEluIHRoaXMgcG9zdCBJ4oCZbGwgc2hvdyBob3cgdG8gdXNlIHRoZSBfX2BsaW1tYWAgYW5kIGBiaW9icm9vbWAgcGFja2FnZXNfXyBpbiBjb21iaW5hdGlvbiB0byBjb250aW51ZSBhIHRpZHkgYW5hbHlzaXMsIGFuZCBjb25zaWRlciBfX3doZW4gYW5kIGhvdyB0byB1c2Ugbm9uLXRpZHkgZGF0YSBpbiBhbiBhbmFseXNpc19fLgoKIyMgRGVwZW5kZW5jaWVzCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyBJbXBvcnRlIHkgbGltcGllemEgZGUgZGF0b3MKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQoKb3JpZ2luYWxfZGF0YSA8LSByZWFkUkRTKCJkYXRhLXJhdy90aWR5bWljcm8ucmRzIikKCmNsZWFuZWRfZGF0YSA8LSBvcmlnaW5hbF9kYXRhICU+JQogIHNlcGFyYXRlKE5BTUUsIGMoIm5hbWUiLCAiQlAiLCAiTUYiLCAic3lzdGVtYXRpY19uYW1lIiwgIm51bWJlciIpLCBzZXAgPSAiXFx8XFx8IikgJT4lCiAgbXV0YXRlX2F0KHZhcnMobmFtZTpzeXN0ZW1hdGljX25hbWUpLCBmdW5zKHRyaW13cykpICU+JQogIHNlbGVjdCgtbnVtYmVyLCAtR0lELCAtWU9SRiwgLUdXRUlHSFQpICU+JQogIGdhdGhlcihzYW1wbGUsIGV4cHJlc3Npb24sIEcwLjA1OlUwLjMpICU+JSAjZHBseXI6OmNvdW50KHNhbXBsZSkKICBzZXBhcmF0ZShzYW1wbGUsIGMoIm51dHJpZW50IiwgInJhdGUiKSwgc2VwID0gMSwgY29udmVydCA9IFRSVUUpICU+JSAKICBtdXRhdGUobnV0cmllbnQ9Zm9yY2F0czo6ZmN0X3JlY29kZShudXRyaWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdsdWNvc2UiID0gIkciLCAiTGV1Y2luZSIgPSAiTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBob3NwaGF0ZSIgPSAiUCIsICJTdWxmYXRlIiA9ICJTIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQW1tb25pYSIgPSAiTiIsICJVcmFjaWwiID0gIlUiKSkgJT4lIAogIGZpbHRlcighaXMubmEoZXhwcmVzc2lvbiksc3lzdGVtYXRpY19uYW1lIT0iIikgJT4lIAogIGdyb3VwX2J5KHN5c3RlbWF0aWNfbmFtZSwgbnV0cmllbnQpICU+JQogIGZpbHRlcihuKCk9PTYpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdsaW1wc2UoKQpgYGAKCi0gYHBsb3RfZXhwcmVzc2lvbl9kYXRhYCBmdW5jdGlvbgoKYGBge3J9CnBsb3RfZXhwcmVzc2lvbl9kYXRhIDwtIGZ1bmN0aW9uKGV4cHJlc3Npb25fZGF0YSkgewogIGdncGxvdChleHByZXNzaW9uX2RhdGEsIGFlcyhyYXRlLCBleHByZXNzaW9uLCBjb2xvciA9IG51dHJpZW50KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICAgIGZhY2V0X3dyYXAofm5hbWUgKyBzeXN0ZW1hdGljX25hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKQp9CmBgYAoKCiMjIGxpbW1hCgpfX0FwbGljYWNpw7NuIGRlIHJlZ3Jlc2nDs24gbGluZWFsIHBvciBvYnNlcnZhY2nDs25fXwoKLSBXaHkgYSBjb21wdXRhdGlvbmFsIGJpb2xvZ2lzdCBzaG91bGQgbm90IHJ1biBhIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcyB3aXRoIHRyYWRpdGlvbmFsIHN0YXRpc3RpY3M/CiAgICAKICAgIC0gX19QZXJmb3JtaW5nIHRob3VzYW5kcyBvZiBsaW5lYXIgcmVncmVzc2lvbnMgd2l0aCBzZXBhcmF0ZSBgbG1gIGNhbGxzIGlzIHNsb3cuX18gSXQgdGFrZXMgYWJvdXQgYSBtaW51dGUgb24gbXkgY29tcHV0ZXIuIFRoZXJlIGFyZSBjb21wdXRhdGlvbmFsIHNob3J0Y3V0cyB3ZSBjYW4gdGFrZSB3aGVuIGFsbCBvZiBvdXIgZGF0YSBpcyBpbiB0aGUgZm9ybSBvZiBhIF9nZW5lLWJ5LXNhbXBsZSBtYXRyaXhfLgogICAgLSBfX1dl4oCZcmUgbm90IHRha2luZyBzdGF0aXN0aWNhbCBhZHZhbnRhZ2Ugb2YgdGhlIHNoYXJlZCBpbmZvcm1hdGlvbi5fXyBNb2Rlcm4gYmlvaW5mb3JtYXRpY3MgYXBwcm9hY2hlcyBvZnRlbiBf4oCcc2hhcmUgcG93ZXLigJ0gYWNyb3NzIGdlbmVzLCBieSBwb29saW5nIHZhcmlhbmNlIGVzdGltYXRlc18uIFRoZSBhcHByb2FjaCBpbiB0aGUgbGltbWEgcGFja2FnZSBpcyBvbmUgbm90YWJsZSBleGFtcGxlIGZvciBfX21pY3JvYXJyYXkgZGF0YV9fLCBhbmQgX19STkEtU2VxIHRvb2xzX18gbGlrZSBgZWRnZVJgIGFuZCBgREVTZXEyYCB0YWtlIGEgc2ltaWxhciBhcHByb2FjaCBpbiB0aGVpciBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbHMuCgotIFdl4oCZZCBsaWtlIHRvIF9fdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIHNvcGhpc3RpY2F0ZWQgYmlvbG9naWNhbCBtb2RlbGluZyB0b29scyBpbiBgQmlvY29uZHVjdG9yYF9fLiBXZeKAmXJlIHRodXMgZ29pbmcgdG8gX19jb252ZXJ0X18gb3VyIGRhdGEgaW50byBhIG5vbi10aWR5IGZvcm1hdCAoYSBnZW5lIGJ5IHNhbXBsZSBtYXRyaXgpLCBhbmQgX19ydW4gaXQgdGhyb3VnaCBsaW1tYV9fIHRvIGNyZWF0ZSBhIGxpbmVhciBtb2RlbCBmb3IgZWFjaCBnZW5lLiBUaGVuIHdoZW4gd2Ugd2FudCB0byB2aXN1YWxpemUsIGNvbXBhcmUsIG9yIG90aGVyd2lzZSBtYW5pcHVsYXRlIG91ciBtb2RlbHMsIHdl4oCZbGwgX190aWR5IHRoZSBtb2RlbF9fIG91dHB1dCB1c2luZyBiaW9icm9vbS4KCiMjIyBTZXR1cAoKLSBNb3N0IGdlbmUgZXhwcmVzc2lvbiBwYWNrYWdlcyBpbiBgQmlvY29uZHVjdG9yYCBleHBlY3QgZGF0YSB0byBiZSBpbiBhIF9fbWF0cml4X18gd2l0aCBfX29uZSByb3cgcGVyIGdlbmUgYW5kIG9uZSBjb2x1bW4gcGVyIHNhbXBsZV9fLiBXZSB3b3VsZCBsaWtlIHRvIF9fZml0IG9uZSBtb2RlbCBmb3IgZWFjaCBnZW5lIGFuZCBudXRyaWVudCBjb21iaW5hdGlvbl9fLiBTbyBsZXTigJlzIHNldCBpdCB1cCB0aGF0IHdheSB1c2luZyByZXNoYXBlMuKAmXMgYGFjYXN0KClgCgooV2UgY291bGQgaGF2ZSB1c2VkIHRpZHly4oCZcyBgc3ByZWFkYCBmdW5jdGlvbiwgYnV0IGBhY2FzdGAgYWN0dWFsbHkgc2F2ZXMgdXMgYSBmZXcgc3RlcHMgYnkgZ2l2aW5nIHVzIGEgbWF0cml4IHdpdGggcm93bmFtZXMsIHJhdGhlciB0aGFuIGEgZGF0YSBmcmFtZSwgcmlnaHQgYXdheS4pCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShyZXNoYXBlMikKZXhwcnMgPC0gYWNhc3QoY2xlYW5lZF9kYXRhLCBzeXN0ZW1hdGljX25hbWUgKyBudXRyaWVudCB+IHJhdGUsCiAgICAgICAgICAgICAgIHZhbHVlLnZhciA9ICJleHByZXNzaW9uIikKCmhlYWQoZXhwcnMpCmBgYAoKLSBXZSB0aGVuIG5lZWQgdG8gZXh0cmFjdCB0aGUgX19leHBlcmltZW50IGRlc2lnbl9fLCB3aGljaCBpbiB0aGlzIGNhc2UgaXMganVzdCB0aGUgZ3Jvd3RoIHJhdGU6CmBgYHtyfQpyYXRlIDwtIGFzLm51bWVyaWMoY29sbmFtZXMoZXhwcnMpKQpyYXRlCmBgYAoKLSBfX2BsaW1tYWAgKOKAnGxpbmVhciBtb2RlbGluZyBvZiBtaWNyb2FycmF5c+KAnSlfXyBpcyBvbmUgb2YgdGhlIG1vc3QgcG9wdWxhciBgQmlvY29uZHVjdG9yYCBwYWNrYWdlcyBmb3IgcGVyZm9ybWluZyBfX2xpbmVhci1tb2RlbCBiYXNlZCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlcyBvbiBtaWNyb2FycmF5IGRhdGFfXy4gV2l0aCB0aGUgZGF0YSBpbiB0aGlzIG1hdHJpeCBmb3JtLCB3ZeKAmXJlIHJlYWR5IHRvIHVzZSBpdDoKCmBgYHtyfQpsaWJyYXJ5KGxpbW1hKQoKIyBsaW5lYXIgbW9kZWwgc3RydWN0dXJlOiAKIyBsbUZpdChkYXRhX21hdHJpeCwgZXhwZXJpbWVudF9kZXNpZ24pCgojIGNoZWNrIHRoZSBleHBlcmltZW50X2Rlc2lnbjogCiMgbW9kZWwubWF0cml4KH5yYXRlKQoKZml0IDwtIGxtRml0KGV4cHJzLCBtb2RlbC5tYXRyaXgofnJhdGUpKQplYiA8LSBlQmF5ZXMoZml0KQpgYGAKCi0gVGhpcyBwZXJmb3JtcyBhIF9fbGluZWFyIHJlZ3Jlc3Npb24gZm9yIGVhY2ggZ2VuZV9fLiBUaGlzIG9wZXJhdGlvbiBpcyBib3RoIF9mYXN0ZXJfIGFuZCBtb3JlIF9zdGF0aXN0aWNhbGx5IHNvcGhpc3RpY2F0ZWRfIHRoYW4gYGxtYC4KCi0gU28gbm93IHdl4oCZdmUgcGVyZm9ybWVkIG91ciByZWdyZXNzaW9uLiBXaGF0IG91dHB1dCBkbyB3ZSBnZXQ/CmBgYHtyfQojY2xhc3MoZml0KQojc3VtbWFyeShmaXQpCmNsYXNzKGViKQpzdW1tYXJ5KGViKQpgYGAKCi0gVGhhdOKAmXMgYSBsb3Qgb2Ygb3V0cHV0cywgYW5kIG1hbnkgb2YgdGhlbSBhcmUgbWF0cmljZXMgb2YgdmFyeWluZyBzaGFwZXMuIElmIHlvdSB3YW50IHRvIHdvcmsgd2l0aCB0aGlzIHVzaW5nIF9fdGlkeSB0b29sc19fIChhbmQgaWYgeW914oCZdmUgYmVlbiBsaXN0ZW5pbmcsIHlvdSBob3BlZnVsbHkgZG8pLCB3ZSBuZWVkIHRvIHRpZHkgaXQ6CgojIyMgVGlkeSBtb2RlbAoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoYmlvYnJvb20pCmhlYWQodGlkeShlYiwgaW50ZXJjZXB0ID0gVFJVRSkpCmBgYAoKLSBOb3RpY2UgdGhhdCB0aGlzIGlzIG5vdyBpbiBfX29uZS1yb3ctcGVyLWNvZWZmaWNpZW50LXBlci1nZW5lX18gZm9ybSwgbXVjaCBsaWtlIHRoZSBvdXRwdXQgb2YgYnJvb23igJlzIHRpZHkoKSBvbiBsaW5lYXIgbW9kZWxzLgoKLSBMaWtlIGJyb29tLCBgYmlvYnJvb21gIGFsd2F5cyByZXR1cm5zIGEgdGFibGUgd2l0aG91dCByb3duYW1lcyB0aGF0IF9fd2UgY2FuIGZlZWQgaW50byBzdGFuZGFyZCB0aWR5IHRvb2xzIGxpa2UgYGRwbHlyYCBhbmQgYGdncGxvdDJgX18uIChOb3RlIHRoYXQgdW5saWtlIGJyb29tLCBiaW9icm9vbSByZXF1aXJlcyBhbiBpbnRlcmNlcHQgPSBUUlVFIGFyZ3VtZW50IHRvIGxlYXZlIHRoZSBpbnRlcmNlcHQgdGVybSwgc2ltcGx5IGJlY2F1c2UgaW4gbWFueSBnZW5vbWljIGRhdGFzZXRzLSB0aG91Z2ggbm90IG91cnMtIHRoZSBpbnRlcmNlcHQgdGVybSBpcyBhbG1vc3QgbWVhbmluZ2xlc3MpLiBgYmlvYnJvb21gIGNhbiBhbHNvIHRpZHkgbW9kZWwgb2JqZWN0cyBmcm9tIG90aGVyIHRvb2xzIGxpa2UgYGVkZ2VSYCBvciBgREVTZXEyYCwgYWx3YXlzIGdpdmluZyBhIGNvbnNpc3RlbnQgZm9ybWF0IHNpbWlsYXIgdG8gdGhpcyBvbmUuCgotIE5vdyBhbGwgd2XigJl2ZSBnb3QgdG8gZG8gX19zcGxpdCB0aGUgc3lzdGVtYXRpYyBuYW1lIGFuZCBudXRyaWVudCBiYWNrIHVwX18uIHRpZHly4oCZcyBgc2VwYXJhdGUoKWAgY2FuIGRvIHRoaXM6CgpgYGB7cn0KdGQgPC0gZWIgJT4lIAogIGJpb2Jyb29tOjp0aWR5Lk1BcnJheUxNKGludGVyY2VwdCA9IFRSVUUpICU+JSAKICBzZXBhcmF0ZShnZW5lLCBjKCJzeXN0ZW1hdGljX25hbWUiLCJudXRyaWVudCIpLCBzZXA9Il8iKSAlPiUgZ2xpbXBzZSgpCmBgYAoKIyMjIEFuYWx5c2UgdGlkeSBtb2RlbAoKLSBOb3RpY2UgdGhhdCB0aGVyZSBhcmUgdHdvIHJvd3MgZm9yIGVhY2ggZ2VuZS1udXRyaWVudCBjb21iaW5hdGlvbjogYW4gX19pbnRlcmNlcHRfXyBhbmQgYSBfX3Nsb3BlX18gdGVybS4gVGhpcyBpcyBzaW1wbGlmeWluZyBlYWNoIGdlbmUtbnV0cmllbnQgY29tYmluYXRpb24gaW50byB0d28gdmFsdWVzOgogICAgCiAgICAtIF9fSW50ZXJjZXB0Ol9fIEhvdyBoaWdobHkgZXhwcmVzc2VkIHRoZSBnZW5lIGlzIHdoZW4gaXTigJlzIHN0YXJ2ZWQgb2YgdGhhdCBudXRyaWVudC4KICAgIC0gX19yYXRlOl9fIEhvdyBtdWNoIHRoZSBnZW5l4oCZcyBleHByZXNzaW9uIHJlc3BvbmRzIHRvIGFuIGluY3JlYXNpbmcgc3VwcGx5IG9mIHRoYXQgbnV0cmllbnQgKGFuZCB0aGVyZm9yZSBhbiBpbmNyZWFzaW5nIGdyb3d0aCByYXRlKQoKYGBge3J9CnRkICU+JSBjb3VudCh0ZXJtKQpgYGAKCi0gV2XigJlsbCB0YWtlIGEgYnJpZWYgbG9vayBhdCB0aG9zZSBvbmUgYXQgYSB0aW1lLgoKIyMjIyBJbnRlcmNlcHRzCmBgYHtyfQppbnRlcmNlcHRfdGVybXMgPC0gdGQgJT4lIGZpbHRlcih0ZXJtPT0iKEludGVyY2VwdCkiKSAKCmludGVyY2VwdF90ZXJtcyAlPiUgaGVhZCgpCmBgYAoKLSBUaGUgcC12YWx1ZXMgYXJlbuKAmXQgYWN0dWFsbHkgaW50ZXJlc3RpbmcgdG8gdXMgaGVyZTogX3RoZXnigJlyZSB0ZXN0aW5nIHdoZXRoZXIgdGhlIGludGVyY2VwdCBpcyBlcXVhbCB0byAwXywgd2hpY2ggaXMgbm90IGEgcGFydGljdWxhcmx5IHNwZWNpYWwgbnVtYmVyIGluIHRlcm1zIG9mIHRoZXNlIG5vcm1hbGl6ZWQgZ2VuZSBleHByZXNzaW9ucy4gKENvbmZpZGVuY2UgaW50ZXJ2YWxzIGFuZCBzdGFuZGFyZCBlcnJvcnMgd291bGQgYmUpLgoKLSBXaGF0IHdl4oCZcmUgcmVhbGx5IGludGVyZXN0ZWQgaW4gaXMgdGhlIF9fdmFsdWUgb2YgZWFjaCBpbnRlcmNlcHQgcmVsYXRpdmUgdG8gdGhlIG90aGVyIG51dHJpZW50cyBpbiB0aGF0IGdlbmVfXy4gRm9yIGV4YW1wbGUsIGxldOKAmXMgYWdhaW4gY29uc2lkZXIgb3VyIGZhdm9yaXRlIGdlbmUsIExFVTEuCgpgYGB7cn0KY2xlYW5lZF9kYXRhICU+JSAKICBmaWx0ZXIobmFtZT09IkxFVTEiKSAlPiUgCiAgZ2dwbG90KGFlcyhyYXRlLGV4cHJlc3Npb24sY29sb3VyPW51dHJpZW50KSkrCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PW1lYW4oZXhwcmVzc2lvbikpLGxpbmV0eXBlPSJkYXNoZWQiKQpgYGAKCi0gVGhpcyBnZW5lIGhhcyBhIGxvdyBpbnRlcmNlcHQgdGVybSBmb3IgYWxsIG51dHJpZW50cyBleGNlcHQgbGV1Y2luZS4gSeKAmXZlIG1hcmtlZCB0aGUgYXZlcmFnZSBpbnRlcmNlcHQgd2l0aCBhIGhvcml6b250YWwgZGFzaGVkIGxpbmUgdG8gZGVtb25zdHJhdGUgdGhpcy4gU3VwcG9zZSB3ZSB3YW50IHRvIGxvb2sgZm9yIG90aGVyIGdlbmVzIGxpa2UgdGhpcywgd2l0aCBhIHNpbmdsZSBvdXRseWluZyBpbnRlcmNlcHQgdGVybS4gV2UgY291bGQgZG8gdGhpcyBieSBfY2VudGVyaW5nXyB0aGUgaW50ZXJjZXB0cyBhcm91bmQgdGhlIGF2ZXJhZ2UgZm9yIGVhY2ggZ2VuZSwgX191c2luZyBhIGBncm91cF9ieWAgYW5kIGBtdXRhdGVgX186CgpgYGB7cn0KY2VudGVyZWRfaW50ZXJjZXB0cyA8LSBpbnRlcmNlcHRfdGVybXMgJT4lCiAgZ3JvdXBfYnkoc3lzdGVtYXRpY19uYW1lKSAlPiUKICBtdXRhdGUoY2VudGVyZWRfaW50ZXJjZXB0ID0gZXN0aW1hdGUgLSBtZWFuKGVzdGltYXRlKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKLSBOb3cgd2UgYXJlIGludGVyZXN0ZWQgaW4gdGhlIF9fbW9zdCBleHRyZW1lIGNhc2VzX18sIHdoZXJlIHRoZSBpbnRlcmNlcHQgaXMgdmVyeSBmYXIgZnJvbSB0aGUgb3RoZXIgbnV0cmllbnRzLiBUaGUgYHRvcF9uYCBmdW5jdGlvbiBpcyB1c2VmdWwgZm9yIHRoaXMuCgpgYGB7cn0KdG9wX2ludGVyY2VwdCA8LSBjZW50ZXJlZF9pbnRlcmNlcHRzICU+JQogIHRvcF9uKDIwLCBjZW50ZXJlZF9pbnRlcmNlcHQpCmBgYAoKLSBOb3RlIHRoYXQgaGVyZSBJ4oCZbSBsb29raW5nIGZvciBfX2Nhc2VzIHdoZXJlIGEgc2luZ2xlIG51dHJpZW50IHdhcyBncmVhdGx5IF9vdmVyZXhwcmVzc2VkXyBpbiBzdGFydmF0aW9uX18gKHRvIGxvb2sgZm9yIHVuZGVyZXhwcmVzc2VkIG51dHJpZW50cywgd2UgY291bGQgaGF2ZSB1c2VkIGAtY2VudGVyZWRfaW50ZXJjZXB0YCBpbnN0ZWFkKS4gV2UgY2FuIHRoZW4gcHVsbCB0aGVzZSBnZW5lcyBvdXQgb2YgdGhlIG9yaWdpbmFsIGRhdGEgd2l0aCB0aGUgdXNlZnVsIGBzZW1pX2pvaW5gLCBhdCB3aGljaCBwb2ludCB3ZSBjYW4gX19ncmFwaCBpdF9fIHdpdGggb3VyIGBwbG90X2V4cHJlc3Npb25fZGF0YWAgZnVuY3Rpb246CgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpjbGVhbmVkX2RhdGEgJT4lCiAgc2VtaV9qb2luKHRvcF9pbnRlcmNlcHQsIGJ5ID0gInN5c3RlbWF0aWNfbmFtZSIpICU+JQogIHBsb3RfZXhwcmVzc2lvbl9kYXRhKCkKYGBgCi0gVGhlc2UgY2VydGFpbmx5IGRvIGxvb2sgbGlrZSBpbnRlcmVzdGluZyBnZW5lcyEgV2Ugbm90aWNlIHRoYXQgc29tZSBnZW5lcywgbGlrZSBQSE8xMSwgb25seSBvbmUgbnV0cmllbnQgaXMgaGlnaGx5IGV4cHJlc3NlZCB3aGlsZSB0aGUgcmVzdCBzaG93IGxvdyBleHByZXNzaW9uLCB3aGlsZSBvdGhlciBnZW5lcywgc3VjaCBhcyBBREgyLCBzaG93IHZhcnlpbmcgbGV2ZWxzIG9mIGV4cHJlc3Npb24gZm9yIGVhY2ggbnV0cmllbnQuIFdlIGFsc28gbm90aWNlIHRoYXQgaW4gbW9zdCBjYXNlcyB0aGUgaGlnaGx5IGV4cHJlc3NlZCBudXRyaWVudCBpcyBtb3ZpbmcgYmFjayBkb3duIHRvd2FyZHMgdGhlIG90aGVycyBhcyBncm93dGggcmF0ZSBpbmNyZWFzZXMgKHRoYXQgaXMsIGFzIHRoZSB5ZWFzdCBpcyBsZXNzIHN0YXJ2ZWQpLiBUaGlzIG1ha2VzIHNlbnNlLCBzaW5jZSBpdOKAmXMgdGhlIHN0YXJ2YXRpb24gdGhhdCBpcyBlbGljaXRpbmcgdGhlIHVudXN1YWwgYmVoYXZpb3IuCgotIFdoYXQgZG8gdGhlc2UgZ2VuZXMgZG8/IEJlYXRzIG1lOyBJ4oCZbSBub3QgYSBiaW9sb2dpc3QsIEkganVzdCBwbGF5IG9uZSBvbiBteSBkZWdyZWUuIEJ1dCBpdCBjZXJ0YWlubHkgbG9va3MgcHJvbWlzaW5nIHRoYXQgUEhPMTEgYW5kIFBITzEyIGFyZSBib3RoIG11Y2ggaGlnaGVyIGV4cHJlc3NlZCB3aGVuIHBob3NwaG9ydXMgaXMgdGhlIGxpbWl0aW5nIG51dHJpZW50LCBhcyB3ZWxsIGFzIFNVTDEgd2hlbiBzdWxmdXIgaXMgcmFyZS0gYW5kIGluZGVlZCBlYWNoIGdlbmUgaXMgaW52b2x2ZWQgaW4gdHJhbnNwb3J0IG9mIHRoYXQgbnV0cmllbnQuIChBbmQgd2UgZG8gc2VlIG91ciBHZW5lIG9mIHRoZSBXZWVrLCBMRVUxKS4KCi0gTG9va2luZyB1cCB0aGUgb3RoZXJzIGluIHllYXN0Z2Vub21lLm9yZywgd2Ugc2VlIHRoYXQgYSBsb3Qgb2YgdGhlbSBhcmUgaW52b2x2ZWQgaW4gdHJhbnNwb3J0IGFjcm9zcyBtZW1icmFuZXMgKGUuZy4gREFMNSwgR0FQMSwgUURSMikuIFRoaXMgbWFrZXMgc2Vuc2U6IHRoZSBjZWxsIG5vdGljZXMgdGhhdCBpdCBpcyBtaXNzaW5nIGEgbnV0cmllbnQsIGFuZCBwdXRzIG1vcmUgZW5lcmd5IGludG8gaW1wb3J0aW5nIGl0LiBOb3RpY2UgdGhhdCB0aGlzIHdvdWxkIGJlIGEgZ3JlYXQgd2F5IHRvIG1ha2UgaW5mZXJlbmNlcyBhYm91dCBnZW5lcyB3aG9zZSBmdW5jdGlvbiB3ZSBkb27igJl0IHlldCBrbm93LiAoVGhpcyBpcyB0aGUgZm9jdXMgb2YgZnVuY3Rpb25hbCBnZW5vbWljcykuCgojIyMjIFNsb3BlcwoKLSBOb3cgbGV04oCZcyB0YWtlIGEgbG9vayBhdCB0aGUgc2xvcGUgdGVybXMsIHdoaWNoIHNob3dzIHdoZXRoZXIgZWFjaCBnZW5lIF9faW5jcmVhc2VkIG9yIGRlY3JlYXNlZCBpdHMgZ3Jvd3RoIHJhdGUgaW4gYSBwYXJ0aWN1bGFyIGNvbmRpdGlvbl9fLgoKYGBge3J9CnNsb3BlX3Rlcm1zIDwtIHRkICU+JSBmaWx0ZXIodGVybT09InJhdGUiKSAKCnNsb3BlX3Rlcm1zICU+JSBoZWFkKCkKYGBgCgotIEhlcmUsIHdl4oCZbGwgZm9jdXMgYSBiaXQgbW9yZSBvbiBfX3N0YXRpc3RpY2FsIHNpZ25pZmljYW5jZV9fLiBGaXJzdCB3ZSBjYW4gbWFrZSBhIGhpc3RvZ3JhbSBvZiB0aGUgcC12YWx1ZXMuIFRoZXNlIHAtdmFsdWVzIGFyZSBzcHJlYWQgYWNyb3NzIHNpeCBkaWZmZXJlbnQgbnV0cmllbnRzLCBzbyB3ZeKAmWxsIGZhY2V0IG91ciBoaXN0b2dyYW0gYnkgdGhvc2UgbnV0cmllbnRzOgoKYGBge3J9CmdncGxvdChzbG9wZV90ZXJtcywgYWVzKHAudmFsdWUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAuMDUpICsKICBmYWNldF93cmFwKH5udXRyaWVudCkKYGBgCgoqIFtsaW5rXShodHRwOi8vdmFyaWFuY2VleHBsYWluZWQub3JnL3N0YXRpc3RpY3MvaW50ZXJwcmV0aW5nLXB2YWx1ZS1oaXN0b2dyYW0vKSBwYXJhIGVsIHBvc3QgZGUgQGRyb2IgcGFyYSBsYSBpbnRlcnByZXRhY2nDs24gZGUgaGlzdG9ncmFtYXMgZGUgcF92YWx1ZXMKCi0gSW4gdGhpcyBjYXNlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIHRlc3RzIGFyZSBnZW5lcmFsbHkgd2VsbC1iZWhhdmVkLCB3aXRoIGEgbWl4IG9mIG51bGxzIChnZW5lcyB0aGF0IGRvbuKAmXQgcmVzcG9uZCB0byBncm93dGggcmF0ZSkgYW5kIGFsdGVybmF0aXZlcyAoZ2VuZXMgdGhhdCBkbykuIFRodXMsIHdlIGNhbiB1c2UgcC12YWx1ZSBjb3JyZWN0aW9uIHRvIGlkZW50aWZ5IHNpZ25pZmljYW50IGdlbmVzLgoKPCEtLSBXZSBjYW4gbm93IHVzZSB0aGUgdGlkeSBhcHByb2FjaGVzIHRvIHZpc3VhbGl6YXRpb24gYW5kIGludGVycHJldGF0aW9uIHRoYXQgd2VyZSBleHBsb3JlZCBpbiBwcmV2aW91cyBwb3N0cy4gV2UgY291bGQgY3JlYXRlIGEgcC12YWx1ZSBoaXN0b2dyYW0gLS0+CgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0KZ2dwbG90KHRkLCBhZXMocC52YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBmYWNldF9ncmlkKHRlcm0gfiBudXRyaWVudCwgc2NhbGVzID0gImZyZWVfeSIpCmBgYAoKLSBPciBtYWtlIGEgX192b2xjYW5vIHBsb3RfXywgY29tcGFyaW5nIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSB0byBlZmZlY3Qgc2l6ZSAoaGVyZSBsZXTigJlzIHNheSBqdXN0IG9uIHRoZSBzbG9wZSB0ZXJtcyk6CgpgYGB7cn0KdGQgJT4lCiAgZmlsdGVyKHRlcm0gPT0gInJhdGUiKSAlPiUKICBnZ3Bsb3QoYWVzKGVzdGltYXRlLCBwLnZhbHVlKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCh+IG51dHJpZW50LCBzY2FsZXMgPSAiZnJlZSIpICsKICBzY2FsZV95X2xvZzEwKCkgCmBgYAoKLSBXZSBjb3VsZCBlYXNpbHkgcGVyZm9ybSBmb3IgX19tdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RpbmdfXyB3aXRoaW4gZWFjaCBncm91cCwgYW5kIGZpbHRlciBmb3Igc2lnbmlmaWNhbnQgKHNheSwgRkRSIDwgMSUpIGNoYW5nZXM6CmBgYHtyfQp0ZF9maWx0ZXJlZCA8LSB0ZCAlPiUKICBncm91cF9ieSh0ZXJtLCBudXRyaWVudCkgJT4lCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KHAudmFsdWUsIG1ldGhvZCA9ICJmZHIiKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGZpbHRlcihmZHIgPCAuMDEpCmBgYAoKT3IgZmluZGluZyB0aGUgX190b3AgZmV3IHNpZ25pZmljYW50IGNoYW5nZXMgaW4gZWFjaCBncm91cF9fIHVzaW5nIGRwbHly4oCZcyBgdG9wX25gOgpgYGB7cn0KdG9wXzMgPC0gdGRfZmlsdGVyZWQgJT4lCiAgZmlsdGVyKHRlcm0gPT0gInJhdGUiKSAlPiUKICBncm91cF9ieShudXRyaWVudCkgJT4lCiAgdG9wX24oMywgYWJzKGVzdGltYXRlKSkKCnRvcF8zCmBgYAoKLSBXZSBjb3VsZCBfX2pvaW5fXyB0aGlzIHdpdGggb3VyIG9yaWdpbmFsIGRhdGEsIHdoaWNoIHdvdWxkIGxldCB1cyBfX3Zpc3VhbGl6ZSB0aGUgdHJlbmRzIGZvciBvbmx5IHRoZSBtb3N0IHNpZ25pZmljYW50IGdlbmVzX186CgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQp0b3BfMyAlPiUKICByZW5hbWUoc2lnbmlmaWNhbnRfbnV0cmllbnQgPSBudXRyaWVudCkgJT4lCiAgaW5uZXJfam9pbihjbGVhbmVkX2RhdGEsIGJ5ID0gInN5c3RlbWF0aWNfbmFtZSIpICU+JQogIG11dGF0ZShoaWdobGlnaHQgPSBudXRyaWVudCA9PSBzaWduaWZpY2FudF9udXRyaWVudCkgJT4lCiAgZ2dwbG90KGFlcyhyYXRlLCBleHByZXNzaW9uLCBjb2xvciA9IG51dHJpZW50KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGx0eSA9ICFoaWdobGlnaHQpLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcChzaWduaWZpY2FudF9udXRyaWVudCB+IHN5c3RlbWF0aWNfbmFtZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlX3kiKQpgYGAKCi0gSW4gc2hvcnQsIHlvdSBjYW4gb25jZSBhZ2FpbiB1c2UgdGhlIHN1aXRlIG9mIF9f4oCcdGlkeSB0b29sc+KAnV9fIHRoYXQgd2XigJl2ZSBmb3VuZCBfX3Bvd2VyZnVsIGluIGdlbm9taWMgYW5hbHlzZXNfXy4KCiMjIENvbmNsdXNpb246IExhIGRhdGEgZXN0w6EgYmFqbyB0dSBwb2RlcgoKLSBUaGVyZeKAmXMgYSBjbGFzc2ljIHByb3ZlcmIgb2YgY29tcHV0ZXIgc2NpZW5jZSBmcm9tIF9fQWJlbG1hbiAmIFN1c3NtYW46IOKAnFByb2dyYW1zIG11c3QgYmUgd3JpdHRlbiBmb3IgcGVvcGxlIHRvIHJlYWQsIGFuZCBvbmx5IGluY2lkZW50YWxseSBmb3IgbWFjaGluZXMgdG8gZXhlY3V0ZS7igJ1fXyBJ4oCZZCBzYXkgdGhpcyBpcyBldmVuIG1vcmUgdHJ1ZSBmb3IgZGF0YSBpdCBpcyBmb3IgY29kZS4gRGF0YSBzY2llbnRpc3RzIG5lZWQgdG8gYmUgdmVyeSBjb21mb3J0YWJsZSBlbmdhZ2luZyB3aXRoIHRoZWlyIGRhdGEsIG5vdCBmaWdodGluZyB3aXRoIHRoZSByZXByZXNlbnRhdGlvbi4KCiMjIEVYVFJBOiBHZW5lLXNldHMKCi0gVGhlc2UgcGVyLWdlbmUgbW9kZWxzIGNhbiBzdGlsbCBiZSBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0IGJpb2xvZ2ljYWxseSBpZiB5b3XigJlyZSBub3QgZmFtaWxpYXIgd2l0aCB0aGUgZnVuY3Rpb25zIG9mIHNwZWNpZmljIGdlbmVzLiBXaGF0IHdlIHJlYWxseSB3YW50IGlzIGEgd2F5IHRvIHN1bW1hcml6ZSB0aGUgcmVzdWx0cyBpbnRvIOKAnGdlbmVzIGludm9sdmVkIGluIHRoaXMgYmlvbG9naWNhbCBwcm9jZXNzIGNoYW5nZWQgdGhlaXIgZXhwcmVzc2lvbi7igJ0gVGhpcyBpcyB3aGVyZSBhbm5vdGF0aW9ucyBvZiBnZW5lIHNldHMgYmVjb21lIHVzZWZ1bC4KCmBgYHtyfQpnZW5lX3NldHMgPC0gZGlzdGluY3QoY2xlYW5lZF9kYXRhLCBzeXN0ZW1hdGljX25hbWUsIEJQLCBNRikKCnRkICU+JQogIGlubmVyX2pvaW4oZ2VuZV9zZXRzKSAlPiUKICBmaWx0ZXIoQlAgPT0gImxldWNpbmUgYmlvc3ludGhlc2lzIiwgdGVybSA9PSAiKEludGVyY2VwdCkiKSAlPiUKICBtdXRhdGUobnV0cmllbnQgPSByZW9yZGVyKG51dHJpZW50LCBlc3RpbWF0ZSwgbWVkaWFuKSkgJT4lCiAgZ2dwbG90KGFlcyhudXRyaWVudCwgZXN0aW1hdGUpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHN5c3RlbWF0aWNfbmFtZSksIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSArCiAgeGxhYigiTGltaXRpbmcgbnV0cmllbnQiKSArCiAgeWxhYigiSW50ZXJjZXB0IChleHByZXNzaW9uIGF0IGxvdyBjb25jZW50cmF0aW9uKSIpICsKICBnZ3RpdGxlKCJHZW5lcyBpbnZvbHZlZCBpbiBsZXVjaW5lIGJpb3N5bnRoZXNpcyIpCmBgYAoKLSBOb3RpY2UgaG93IGNsZWFyIGl0IGlzIHRoYXQgdGhlc2UgZ2VuZXMgcmVzcG9uZCB0byBsZXVjaW5lIHN0YXJ2YXRpb24gaW4gcGFydGljdWxhci4gVGhpcyBjYW4gYmUgYXBwbGllZCB0byBnZW5lIHNldHMgY29udGFpbmluZyBkb3plbnMgb3IgZXZlbiBodW5kcmVkcyBvZiBnZW5lcyB3aGlsZSBzdGlsbCBtYWtpbmcgdGhlIGdlbmVyYWwgdHJlbmQgYXBwYXJlbnQuIEZ1cnRoZXJtb3JlLCB3ZSBjb3VsZCB1c2UgdGhlc2Ugc3VtbWFyaWVzIHRvIGxvb2sgYXQgbWFueSBnZW5lIHNldHMgYXQgb25jZSwgYW5kIGV2ZW4gdXNlIHN0YXRpc3RpY2FsIHRlc3RzIHRvIGRpc2NvdmVyIG5ldyBnZW5lIHNldHMgdGhhdCByZXNwb25kIHRvIHN0YXJ2YXRpb24uCgojIyBDb21wdXRlciBlbnZpcm9ubWVudApgYGB7cn0KZGV2dG9vbHM6OnNlc3Npb25faW5mbygpCmBgYAoKIyMgUmVmZXJlbmNlcw==