## 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
Preliminary note
NOTE: Please keep in mind that this is a learning exercise and I do not know if my considerations and solutions for the exercises are correct.
This document follows the OpenIntro Statistics course which can be found at https://www.openintro.org/ and with some background material at https://github.com/OpenIntroOrg
There are two lab documents to this chapter available:
It seems to me that the second version is the newer one using dplyr
and ggplot
. But boh documents are helpful as you can even learn in comparing them.
Getting started
We consider real estate data from the city of Ames, Iowa. This is the same dataset used in the previous lab. The details of every real estate transaction in Ames is recorded by the City Assessor’s office. Our particular focus for this lab will be all residential home sales in Ames between 2006 and 2010. This collection represents our population of interest. In this lab we would like to learn about these home sales by taking smaller samples from the full population. Let’s load the data.
The process of laoding data is made as easy as possible. The suggestion is to use the following program code:
> # download.file("http://www.openintro.org/stat/data/ames.RData",
> # destfile = "ames.RData")
> # load("ames.RData")
The main focus of this tutorial is to get some statistical knowledge. But for real applications it is to get more into detail. The above code has two limitations:
- The data is stored with
.RData
Format at the same level as this file. For bigger project one would need a more detailed and ordered file structure.
- The downloaded file also loads two functions
contains()
and plot_ci()
into the memory. I don’t like this hidden addition as it destroys reproducibilty in bigger and real life projects.
Task 1: File structure with ProjectTemplate
For the first question (file structure) exists with ProjectTemplate a public and free support for a sophisticated but at the same time standardised file structure. During the installation process it generates the following file structure automatically.
Directories installed by ProjectTemplate
To install the package one could use install.packages('ProjectTemplate')
, but I prefer require('ProjectTemplate')
because it looks if the packages is already installed and attaches it at the same time, saving the library(ProjectTemplate)
command. (At least that is my understanding of the difference between these two commands.)
After you have loaded ProjectTemplate
you need to call create.project('<your-directory-name>')
to generate the above directory structure. Now you can change into this folder and inspect all the provided utilities. Every folder contains a README.md
explaining the intended purpose of the folder. You have to do this as the very first step, even before you open a markdown file. It is best done from the console.
> # require('ProjectTemplate')
> # create.project('OpenIntro')
Some of the folders have advanced purposes and generally the structure is intended for users who have already some experiences and work on bigger project. But it is good to know that a somewhat differentiated folder structure is helpful for the project organisation. As I am still not have advanced skills it is the best and easiest way to separate my reports, data and generated material (plots). Because I am using git
respectively GitHub
as a version control system the separation between my own work and files generated automatically during the analysis is helpful as the later should not BE pushed into the GitHub repo.
So I use the recommended program code with some changes:
I (re)store the path to the working directory in order to get a standardises situation. I noticed already some problems with the path to the working directory, especially if I change change it via the console and then to recompile the script.
I check if the file is already there. I start the download only if the file is missing.
I store the file in my data
folder, which I have created in an earlier occasion.
> oldwd <- getwd()
> setwd(my.wd)
> if (!(file.exists("data/ames.RData")))
+ {
+ download.file("http://www.openintro.org/stat/data/ames.RData",
+ destfile = "data/ames.RData")
+ }
> load("data/ames.RData")
> setwd(oldwd)
Task 2: Bibliography manager in RMarkdown
Another problem I noticed is the usage of standardised bibliography for reports. The description for the ames
data set is available at the website of the American Stastical Association (ASA). The offical published reference is: De Cock D. 2011. Ames, Iowa: Alternative to the Boston Housing Data as an End of Semester Regression Project. Journal of Statistics Education; 19(3). You can also download the full paper. BTW: To apply real data sets to learn statistics is one of the reasons I am very excited about the use of R. When I learned statistics at the university back in the 70ties, it was a very boring exercise with small and artificial data. Even today some modern books on statistics present their exercises without statistical software (e.g. Statistics for Dummies). More material with beautiful graphs can be found at SemanticScolar.
To use citations and bibliographies in RMarkdown one has to apply certain rules published in document by RStudio.
I am generally using Zotero for my bibliographies. Unfortunately this is not supported by RMarkdown. For me the easiest installation is done with a .bib file. Especially as I have used for the preparation of my last books LyX and had therefore to install BibTeX respectively BibLaTeX. I am using the platform independent bibliography manager JabRef but also BibDesk (just for mac OS).
To cite an author one has to use the unique reference code from the bibliography manager.
Citations go inside square brackets and are separated by semicolons. Each citation must have a key, composed of ‘@’ + the citation identifier from the database, and may optionally have a prefix, a locator, and a suffix.
For instance I have referenced to my biblbiography database in the YAML metadata section with the following line:
bibliography: "~/Documents/Meine\ Bibliographien/baumgartner.bib"
Now I can for example cite my own article about taxonomies of electronic portfolios as @Baumgartner_2009a
in square brackets (Baumgartner 2009). RMarkdown adds the complete bibliography at the end of the document. The header “References” is added by me manually. And here is the reference to the ames dataset (De Cock 2011).
Finally: Starting the lab
Finally we can now start the lab with a simple random sample of size 60 from the population. Specifically, this is a simple random sample of size 60. Note that the data set has information on 82 housing variables, but for the first portion of the lab we’ll focus on the size of the house, represented by the variable Gr.Liv.Area
.
Note: For reproducibility in generating random data I have learned that one has always to use set.seed
. So I will use it in the next program snippet.
There are two ways to generate the sample. The old variant with sample()
taken from this page and another one with sample_n
as explained on the newer lab page.
> # older, traditional method
> population <- ames$Gr.Liv.Area
> set.seed(12345)
> samp.old <- sample(population, 60)
> head(samp.old)
[1] 1314 1584 1696 1312 949 1374
> # other (newer) method
> n <- 60
> set.seed(12345) # for each sample drawn one has to use the `set.seed` function
> samp.new.df <- sample_n(ames, n) # returns a data frame which has to be used for ggplot
> samp.new <- samp.new.df$Gr.Liv.Area # a vector of integers
> head(samp.new)
[1] 1314 1584 1696 1312 949 1374
The function sample()
takes a sample of the specified size from the elements of x (population
in this case) using either with or without replacement.
usage: sample(x, size, replace = FALSE, prob = NULL)
The function sample_n()
is a wrapper around the base sample.int
to make it easy to select random rows from a table. It currently only works for local tbls.
usage: sample_n(tbl, size, replace = FALSE, weight = NULL, .env = parent.frame())
Task 3: Drawing a histogram
For me I had first to clear up some basics about the difference between histogram and barplot.
For a discription of the sample distribution which consists of quantitative data I have to plot a histogram. There are two ways to do it: The old one with hist
or the new one with qplot
or even better with ggplot
. For the newer version there is a nice article How to Make a Histogram with ggplot2 at R-Bloggers which not only explains how to use the basic commands but also how to refine the appearance of the histogram.
> set.seed(12345)
> hist(samp.old,
+ breaks = 15,
+ main = "Histogram of my sample drawn with the `hist` function")

> qplot(samp.old,
+ bins = 15,
+ geom = "histogram",
+ main = "Histogram of my sample drawn with the `qplot` function")

> p <- ggplot(samp.new.df, aes(Gr.Liv.Area)) # samp.new as integer vector does not work, ggplot needs a data.frame
> p <- p + geom_histogram(bins = 15)
> p <- p + labs(title = "Histogram of my sample drawn with the `ggplot` function")
> p

You can see that the ggplot
function is more complex but it has much more parameters to set. The nice thins is that you can add layer by layer. You can see the layers with the different lines of assignments to p
.
ggplot()
is used to construct the initial plot object, and is almost always followed by + to add component to the plot. There are three common ways to invoke ggplot:
- ggplot(df, aes(x, y, ))
- ggplot(df)
- ggplot()
The first method is recommended if all layers use the same data and the same set of aesthetics, although this method can also be used to add a layer using data from another data frame.
The second method specifies the default data frame to use for the plot, but no aesthetics are defined up front. This is useful when one data frame is used predominantly as layers are added, but the aesthetics may vary from one layer to another.
The third method initializes a skeleton ggplot object which is fleshed out as layers are added. This method is useful when multiple data frames are used to produce different layers, as is often the case in complex graphics.
Exercise 1: Describing the distribution of the sample
Describe the distribution of house area in your sample. What would you say is the “typical” size within your sample? Also state precisely what you interpreted “typical” to mean.
My Answer: My distribution is somewaht left skewed and has some outliers at the high end. You will see later that my example is a little extrem as different samples (with other or no seeds) produce a more regular form of the distribution.
Exercise 2: Expectation of distributions
Would you expect another student’s distribution to be identical to yours? Would you expect it to be similar? Why or why not?
My Answer: Not identical but similiar. In order to get exactly the same data to work with another student would have to use set.seed
with the same value as I have used.
Task 4: Plots with different samples
Take away the seed and try to run the plot several times. You will see that the distribution changes it appearances.
Note: Very important for the appearance of the histogramm is also the number of bins. You have to exerpiment what number gives a nice histogramm. For comparisons you have to set it constant in all examples. In my case: bins = 15
.
I will simulate the difference appearances of the distribution in the next program snippet using the same code for drawing the histogramm but with different seeds.
> n <- 60
> set.seed(12345) # for each sample drawn one has to use the `set.seed` function
> samp1 <- sample_n(ames, n)
> p1 <- ggplot(samp1, aes(Gr.Liv.Area))
> p1 <- p1 + geom_histogram(bins = 15)
> p1 <- p1 + labs(title = "Histogram: Version 1, already used above")
> p1

> set.seed(11111) # for each sample drawn one has to use the `set.seed` function
> samp2 <- sample_n(ames, n)
> p2 <- ggplot(samp2, aes(Gr.Liv.Area))
> p2 <- p2 + geom_histogram(bins = 15)
> p2 <- p2 + labs(title = "Histogram: Version 2")
> p2

> set.seed(54321) # for each sample drawn one has to use the `set.seed` function
> samp3 <- sample_n(ames, n)
> p3 <- ggplot(samp3, aes(Gr.Liv.Area))
> p3 <- p3 + geom_histogram(bins = 15)
> p3 <- p3 + labs(title = "Histogram: Version 3")
> p3

Calulating the confidence intervals
Lets return for a moment to the question that first motivated this lab: based on this sample, what can we infer about the population? Based only on this single sample, the best estimate of the average living area of houses sold in Ames would be the sample mean, usually denoted as \(\bar{x}\) (here we’re calling it x_bar
). That serves as a good point estimate but it would be useful to also communicate how uncertain we are of that estimate. This uncertainty can be quantified using a confidence interval.
A confidence interval for a population mean is of the following form
\(\bar{x} + z^\star \frac{s}{\sqrt{n}}\)
Task 5: Equations in RMarkdown
Here is a little primer how to write equations in RMarkdown.
You should by now be comfortable with calculating the mean and standard deviation of a sample in R. And we know that the sample size is 60. So the only remaining building block is finding the appropriate critical value for a given confidence level. We can use the qnorm function for this task, which will give the critical value associated with a given percentile under the normal distribution. Remember that confidence levels and percentiles are not equivalent. For example, a 95% confidence level refers to the middle 95% of the distribution, and the critical value associated with this area will correspond to the 97.5th percentile.
Again there are two methods here as illustrated in the two labs docoument mentioned above.
Confidence intervals calculated manually
One of the most common ways to describe the typical or central value of a distribution is to use the mean. In this case we can calculate the mean of the sample using,
We can calculate a 95% confidence interval for a sample mean by adding and subtracting 1.96 standard errors to the point estimate (See Section 4.2.3 if you are unfamiliar with this formula).
> sample_mean <- mean(samp.old)
> se <- sd(samp.old) / sqrt(60)
> lower <- sample_mean - 1.96 * se
> upper <- sample_mean + 1.96 * se
> c(lower, upper)
[1] 1366.04 1585.16
Confidence intervals calculated with the qnorm
function
We can use the qnorm
function for this task, which will give the critical value associated with a given percentile under the normal distribution. Remember that confidence levels and percentiles are not equivalent. For example, a 95% confidence level refers to the middle 95% of the distribution, and the critical value associated with this area will correspond to the 97.5th percentile.
We can find the critical value for a 95% confidence interval using
> z_star_95 <- qnorm(0.975)
> z_star_95
[1] 1.959964
which is roughly equal to the value critical value 1.96 that I amn familiar with by now.
Let’s finally calculate the confidence interval:
> samp.new.df %>%
+ summarise(lower = mean(samp.new) - z_star_95 * (sd(samp.new) / sqrt(n)),
+ upper = mean(samp.new) + z_star_95 * (sd(samp.new) / sqrt(n)))
lower upper
1 1366.042 1585.158
You see that the result is the same in both variants.
To recap: even though we don’t know what the full population looks like, we’re 95% confident that the true average size of houses in Ames lies between the values lower and upper. There are a few conditions that must be met for this interval to be valid.
Exercise 3: Conditions to be met
For the confidence interval to be valid, the sample mean must be normally distributed and have standard error \(s / \sqrt{n}\). What conditions must be met for this to be true?
Confidence levels
Exercise 4: Meaning of confidence interval
What does “95% confidence” mean?
In the case of our data set we have the rare luxury of knowing the true population mean since we have data on the entire population. Let’s calculate this value so that we can determine if our confidence intervals actually capture it. We’ll store it in a data frame called params
(short for population parameters), and name it mu
.
Exercise 5:
Does my confidence interval capture the true average size of houses in Ames?
> params <- ames %>%
+ summarise(mu = mean(Gr.Liv.Area))
> sprintf("mu = %f; sample mean = %f, lower = %f, upper = %f", params$mu, sample_mean, lower, upper)
[1] "mu = 1499.690444; sample mean = 1475.600000, lower = 1366.039581, upper = 1585.160419"
> ci.result <- ifelse(lower < params$mu & upper > params$mu, "YES!", "NO!")
> sprintf("Does my confidence interval capture the true average size of houses in Ames? %s", ci.result )
[1] "Does my confidence interval capture the true average size of houses in Ames? YES!"
YES!
Exercise 6:
Each sample taken with a different set.seed
should have gotten a slightly different confidence interval. What proportion of those intervals would you expect to capture the true population mean? Why?
About 95% of the sample mean should be between the confidence interval.
Collecting many samples programmatically
Using R, we’re going to collect many samples to learn more about how sample means and confidence intervals vary from one sample to another.
Here is the rough outline:
Obtain a random sample.
Calculate the sample’s mean and standard deviation, and use these to calculate and store the lower and upper bounds of the confidence intervals.
Repeat these steps 50 times.
We can accomplish this using the rep_sample_n
function. (This is a function of oilabs
package!) The following lines of code takes 50 random samples of size n from population (and remember we defined n=60 earlier), and computes the upper and lower bounds of the confidence intervals based on these samples.
> ci <- ames %>%
+ rep_sample_n(size = n, reps = 50, replace = TRUE) %>%
+ summarise(lower = mean(Gr.Liv.Area) - z_star_95 * (sd(Gr.Liv.Area) / sqrt(n)),
+ upper = mean(Gr.Liv.Area) + z_star_95 * (sd(Gr.Liv.Area) / sqrt(n)))
Let’s view the first five intervals:
# A tibble: 5 × 3
replicate lower upper
<int> <dbl> <dbl>
1 1 1468.223 1724.510
2 2 1336.127 1581.573
3 3 1332.359 1589.708
4 4 1356.698 1612.469
5 5 1366.526 1587.807
Next we’ll create a plot similar to Figure 4.8 on page 175 of OpenIntro Statistics, 3rd Edition. First step will be to create a new variable in the ci
data frame that indicates whether the interval does or does not capture the true population mean. Note that capturing this value would mean the lower bound of the confidence interval is below the value and upper bound of the confidence interval is above the value. Remember that we create new variables using the mutate
function.
> ci <- ci %>%
+ mutate(capture_mu = ifelse(lower < params$mu & upper > params$mu, "yes", "no"))
The ifelse
function is new. It takes three arguments: first is a logical statement, second is the value we want if the logical statement yields a true result, and the third is the value we want if the logical statement yields a false result.
We now have all the information we need to create the plot, but we need to re-organize our data a bit for easy plotting. Specifically, we need to organize the data in a new data frame where each row represents one bound, as opposed to one interval. So this
# A tibble: 6 × 4
replicate lower upper capture_mu
<int> <dbl> <dbl> <chr>
1 1 1468.223 1724.510 yes
2 2 1336.127 1581.573 yes
3 3 1332.359 1589.708 yes
4 4 1356.698 1612.469 yes
5 5 1366.526 1587.807 yes
6 6 1424.184 1673.850 yes
should like this
> ci_data <- data.frame(ci_id = c(1:50, 1:50),
+ ci_bounds = c(ci$lower, ci$upper),
+ capture_mu = c(ci$capture_mu, ci$capture_mu))
> head(ci_data)
ci_id ci_bounds capture_mu
1 1 1468.223 yes
2 2 1336.127 yes
3 3 1332.359 yes
4 4 1356.698 yes
5 5 1366.526 yes
6 6 1424.184 yes
The structure of ci_data
is interesting. It has 100 rows because of adding with the combine function (ci_id = c(1:50, 1:50)
. ci_id counts from 1 to 50 and then starts again. ci_id
is the id of the sample which is drawn in the following plot as y-axis. This means that row 51 has the same y-value as the first row and therefore a straight horizontal line can be drawn - but only after the data are grouped by ci_id
.
And finally we can create this pretty complicated plot using the following program code:
> qplot(data = ci_data, x = ci_bounds, y = ci_id,
+ group = ci_id, color = capture_mu) +
+ geom_point(size = 2) + # add points at the ends, size = 2
+ geom_line() + # connect with lines
+ geom_vline(xintercept = params$mu, color = "darkgray") # draw vertical line

> ci.old <- ames %>%
+ rep_sample_n(size = n, reps = 50, replace = TRUE) %>%
+ summarise(x_bar = mean(Gr.Liv.Area),
+ se = sd(Gr.Liv.Area) / sqrt(n),
+ me = z_star_95 * se,
+ lower = x_bar - me,
+ upper = x_bar + me)
>
> ci.old <- ci.old %>%
+ mutate(capture_mu.old = ifelse(lower < params$mu & upper > params$mu, "yes", "no"))
>
> qplot(data = ci.old, x = replicate, y = x_bar, color = ci.old$capture_mu.old) +
+ geom_errorbar(aes(ymin = lower, ymax = upper)) +
+ geom_hline(data = params, aes(yintercept = mu), color = "darkgray") + # draw vertical line
+ coord_flip()

Exercise 7:
What proportion of your confidence intervals include the true population mean? Is this proportion exactly equal to the confidence level? If not, explain why. Make sure to include your plot in your answer.
In the above graphs I can detect 3 respecitvly 2 cases out of 50 where the sample mean is not within the confidence interval. This is according to the 95% level.
More Practice
The next exercises have still to be done.
Exercise 8:
Pick a confidence level of your choosing, provided it is not 95%. What is the appropriate critical value?
I will choose a 90% confidence level. e.g. it refers to the middle 90% of the distribution, and the critical value associated with this area will correspond to the 95th percentile.
Exercise 9:
Calculate 50 confidence intervals at the confidence level you chose in the previous question, and plot all intervals on one plot, and calculate the proportion of intervals that include the true population mean. How does this percentage compare to the confidence level selected for the intervals? Make sure to include your plot in your answer.
> n <- 60
> set.seed(11111) # for each sample drawn one has to use the `set.seed` function
> samp.new.df <- sample_n(ames, n) # returns a data frame which has to be used for ggplot
>
>
> z_star_90 <- qnorm(0.95)
>
> samp.new.df %>%
+ summarise(lower.90 = mean(samp.new) - z_star_90 * (sd(samp.new) / sqrt(n)),
+ upper.90 = mean(samp.new) + z_star_90 * (sd(samp.new) / sqrt(n)))
lower.90 upper.90
1 1383.656 1567.544
> ci.90 <- ames %>%
+ rep_sample_n(size = n, reps = 50, replace = TRUE) %>%
+ summarise(lower.90 = mean(Gr.Liv.Area) - z_star_90 * (sd(Gr.Liv.Area) / sqrt(n)),
+ upper.90 = mean(Gr.Liv.Area) + z_star_90 * (sd(Gr.Liv.Area) / sqrt(n)))
>
> ci.90 <- ci.90 %>%
+ mutate(capture_mu.90 = ifelse(lower.90 < params$mu & upper.90 > params$mu, "yes", "no"))
>
>
> ci_data.90 <- data.frame(ci_id = c(1:50, 1:50),
+ ci_bounds.90 = c(ci.90$lower.90, ci.90$upper.90),
+ capture_mu.90 = c(ci.90$capture_mu.90, ci.90$capture_mu.90))
>
> qplot(data = ci_data.90, x = ci_bounds.90, y = ci_id,
+ group = ci_id, color = capture_mu.90) +
+ geom_point(size = 2) + # add points at the ends, size = 2
+ geom_line() + # connect with lines
+ geom_vline(xintercept = params$mu, color = "darkgray") # draw vertical line

> by.ci <- ci.90 %>% group_by(capture_mu.90)
> how.many <- by.ci %>% count(capture_mu.90) %>% mutate(prop = n / 50 * 100)
> how.many
# A tibble: 2 × 3
capture_mu.90 n prop
<chr> <int> <dbl>
1 no 3 6
2 yes 47 94
The proportion of means outside of the confidence interval is 6% which is expected.
LS0tCnRpdGxlOiAiTGFiIDY6IENvbmZpZGVuY2UgaW50ZXJ2YWxzIgphdXRob3I6ICJQZXRlciBCYXVtZ2FydG5lciIKZGF0ZTogIjIwMTctMDMtMTEiCmJpYmxpb2dyYXBoeTogIn4vRG9jdW1lbnRzL01laW5lXCBCaWJsaW9ncmFwaGllbi9iYXVtZ2FydG5lci5iaWIiCm91dHB1dDogb2lsYWJzOjpsYWJfcmVwb3J0CiMgb3V0cHV0OgojICAgICAgICAgaHRtbF9kb2N1bWVudDoKIyAgICAgICAgICAgICAgICAgY3NzOiAifi9Eb2N1bWVudHMvX1BCLURhdGEvUHJvZ3JhbW1pbmcvUi9MZWFybmluZy1SL2xhYi5jc3MiCiMgICAgICAgICAgICAgICAgIGhpZ2hsaWdodDogcHlnbWVudHMKIyAgICAgICAgICAgICAgICAgdGhlbWU6IGNlcnVsZWFuCiMgICAgICAgICAgICAgICAgIHRvYzogdHJ1ZQojICAgICAgICAgICAgICAgICB0b2NfZGVwdGg6IDIKCi0tLQoKCmBgYHtyIGxhYmVsID0gImdsb2JhbC1vcHRpb25zIiwgZWNobz1GQUxTRSwgaGlnaGxpZ2h0PVRSVUV9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICAgICAgICBtZXNzYWdlID0gRiwKICAgICAgICBlcnJvciA9IEYsCiAgICAgICAgd2FybmluZyA9IEYsCiAgICAgICAgY29tbWVudCA9IE5BLAogICAgICAgIGhpZ2hsaWdodCA9IFQsCiAgICAgICAgcHJvbXB0ID0gVAogICAgICAgICkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob2lsYWJzKQoKbXkud2QgPC0gIn4vRG9jdW1lbnRzL19QQi1EYXRhL1Byb2dyYW1taW5nL1IvTGVhcm5pbmctUi9vcGVuaW50cm8iCnNldHdkKG15LndkKQpgYGAKCiogKiAqCgojIFByZWxpbWluYXJ5IG5vdGUKCj5OT1RFOiBQbGVhc2Uga2VlcCBpbiBtaW5kIHRoYXQgdGhpcyBpcyBhIGxlYXJuaW5nIGV4ZXJjaXNlIGFuZCBJIGRvIG5vdCBrbm93IGlmIG15IGNvbnNpZGVyYXRpb25zIGFuZCBzb2x1dGlvbnMgZm9yIHRoZSBleGVyY2lzZXMgYXJlIGNvcnJlY3QuIAoKVGhpcyBkb2N1bWVudCBmb2xsb3dzIHRoZSBPcGVuSW50cm8gU3RhdGlzdGljcyBjb3Vyc2Ugd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGh0dHBzOi8vd3d3Lm9wZW5pbnRyby5vcmcvIGFuZCB3aXRoIHNvbWUgYmFja2dyb3VuZCBtYXRlcmlhbCBhdCBodHRwczovL2dpdGh1Yi5jb20vT3BlbkludHJvT3JnCgpUaGVyZSBhcmUgdHdvIGxhYiBkb2N1bWVudHMgdG8gdGhpcyBjaGFwdGVyIGF2YWlsYWJsZToKCiogW0NvbmZpZGVuY2UgaW50ZXJ2YWxzIGF0IE9wZW5JbnRyb10oaHR0cDovL2h0bWxwcmV2aWV3LmdpdGh1Yi5pby8/aHR0cHM6Ly9naXRodWIuY29tL2FuZHJld3BicmF5L29pTGFicy1iYXNlLVIvYmxvYi9tYXN0ZXIvY29uZmlkZW5jZV9pbnRlcnZhbHMvY29uZmlkZW5jZV9pbnRlcnZhbHMuaHRtbCkgCgoqIFtDb25maWRlbmNlIGludGVydmFscyBhdCBHaXRIdWJdKGh0dHA6Ly9odG1scHJldmlldy5naXRodWIuaW8vP2h0dHBzOi8vZ2l0aHViLmNvbS9hbmRyZXdwYnJheS9vaUxhYnMtYmFzZS1SL2Jsb2IvbWFzdGVyL2NvbmZpZGVuY2VfaW50ZXJ2YWxzL2NvbmZpZGVuY2VfaW50ZXJ2YWxzLmh0bWwpOiAKCkl0IHNlZW1zIHRvIG1lIHRoYXQgdGhlIHNlY29uZCB2ZXJzaW9uIGlzIHRoZSBuZXdlciBvbmUgdXNpbmcgYGRwbHlyYCBhbmQgYGdncGxvdGAuIEJ1dCBib2ggZG9jdW1lbnRzIGFyZSBoZWxwZnVsIGFzIHlvdSBjYW4gZXZlbiBsZWFybiBpbiBjb21wYXJpbmcgdGhlbS4KCiogaHR0cHM6Ly9naXRodWIuY29tL2FuZHJld3BicmF5L29pTGFicy1kcGx5ci1nZ3Bsb3QgVGhpcyBpcyBhIG5ld2VyIHZlcnNpb24gb2YgdGhlIE9wZW5JbnRybyBTdGF0aXN0aWNzIGxhYiwgIHVzaW5nIHRoZSBwYWNrYWdlcyBgZ2dwbG90YCBhbmQgYGRwbHlyYC4KCiMgR2V0dGluZyBzdGFydGVkCgpXZSBjb25zaWRlciByZWFsIGVzdGF0ZSBkYXRhIGZyb20gdGhlIGNpdHkgb2YgQW1lcywgSW93YS4gVGhpcyBpcyB0aGUgc2FtZSBkYXRhc2V0IHVzZWQgaW4gdGhlIHByZXZpb3VzIGxhYi4gVGhlIGRldGFpbHMgb2YgZXZlcnkgcmVhbCBlc3RhdGUgdHJhbnNhY3Rpb24gaW4gQW1lcyBpcyByZWNvcmRlZCBieSB0aGUgQ2l0eSBBc3Nlc3NvcuKAmXMgb2ZmaWNlLiBPdXIgcGFydGljdWxhciBmb2N1cyBmb3IgdGhpcyBsYWIgd2lsbCBiZSBhbGwgcmVzaWRlbnRpYWwgaG9tZSBzYWxlcyBpbiBBbWVzIGJldHdlZW4gMjAwNiBhbmQgMjAxMC4gVGhpcyBjb2xsZWN0aW9uIHJlcHJlc2VudHMgb3VyIHBvcHVsYXRpb24gb2YgaW50ZXJlc3QuIEluIHRoaXMgbGFiIHdlIHdvdWxkIGxpa2UgdG8gbGVhcm4gYWJvdXQgdGhlc2UgaG9tZSBzYWxlcyBieSB0YWtpbmcgc21hbGxlciBzYW1wbGVzIGZyb20gdGhlIGZ1bGwgcG9wdWxhdGlvbi4gTGV04oCZcyBsb2FkIHRoZSBkYXRhLgoKClRoZSBwcm9jZXNzIG9mIGxhb2RpbmcgZGF0YSBpcyBtYWRlIGFzIGVhc3kgYXMgcG9zc2libGUuIFRoZSBzdWdnZXN0aW9uIGlzIHRvIHVzZSB0aGUgZm9sbG93aW5nIHByb2dyYW0gY29kZToKCmBgYHtyIHNhbXBsZS1jb2RlLWZvci1kb3dubG9hZGluZy1kYXRhfQojIGRvd25sb2FkLmZpbGUoImh0dHA6Ly93d3cub3BlbmludHJvLm9yZy9zdGF0L2RhdGEvYW1lcy5SRGF0YSIsCiMgICAgICAgICAgICAgICBkZXN0ZmlsZSA9ICJhbWVzLlJEYXRhIikKIyBsb2FkKCJhbWVzLlJEYXRhIikKYGBgCgpUaGUgbWFpbiBmb2N1cyBvZiB0aGlzIHR1dG9yaWFsIGlzIHRvIGdldCBzb21lIHN0YXRpc3RpY2FsIGtub3dsZWRnZS4gQnV0IGZvciByZWFsIGFwcGxpY2F0aW9ucyBpdCBpcyB0byBnZXQgbW9yZSBpbnRvIGRldGFpbC4gVGhlIGFib3ZlIGNvZGUgaGFzIHR3byBsaW1pdGF0aW9uczoKCiogVGhlIGRhdGEgaXMgc3RvcmVkIHdpdGggYC5SRGF0YWAgRm9ybWF0IGF0IHRoZSBzYW1lIGxldmVsIGFzIHRoaXMgZmlsZS4gRm9yIGJpZ2dlciBwcm9qZWN0IG9uZSB3b3VsZCBuZWVkIGEgbW9yZSBkZXRhaWxlZCBhbmQgb3JkZXJlZCBmaWxlIHN0cnVjdHVyZS4KKiBUaGUgZG93bmxvYWRlZCBmaWxlIGFsc28gbG9hZHMgdHdvIGZ1bmN0aW9ucyBgY29udGFpbnMoKWBhbmQgYHBsb3RfY2koKWAgaW50byB0aGUgbWVtb3J5LiBJIGRvbid0IGxpa2UgdGhpcyBoaWRkZW4gYWRkaXRpb24gYXMgaXQgZGVzdHJveXMgcmVwcm9kdWNpYmlsdHkgaW4gYmlnZ2VyIGFuZCByZWFsIGxpZmUgcHJvamVjdHMuCgojIyBUYXNrIDE6IEZpbGUgc3RydWN0dXJlIHdpdGggUHJvamVjdFRlbXBsYXRlCgpGb3IgdGhlIGZpcnN0IHF1ZXN0aW9uIChmaWxlIHN0cnVjdHVyZSkgZXhpc3RzIHdpdGggW1Byb2plY3RUZW1wbGF0ZV0oaHR0cDovL3Byb2plY3R0ZW1wbGF0ZS5uZXQvZ2V0dGluZ19zdGFydGVkLmh0bWwpIGEgcHVibGljIGFuZCBmcmVlIHN1cHBvcnQgZm9yIGEgc29waGlzdGljYXRlZCBidXQgYXQgdGhlIHNhbWUgdGltZSBzdGFuZGFyZGlzZWQgZmlsZSBzdHJ1Y3R1cmUuIER1cmluZyB0aGUgaW5zdGFsbGF0aW9uIHByb2Nlc3MgaXQgZ2VuZXJhdGVzIHRoZSBmb2xsb3dpbmcgZmlsZSBzdHJ1Y3R1cmUgYXV0b21hdGljYWxseS4gCgo8cCBzdHlsZT0idGV4dC1hbGlnbjpjZW50ZXI7Ij4hW0RpcmVjdG9yaWVzIGluc3RhbGxlZCBieSBQcm9qZWN0VGVtcGxhdGVdKGRhdGEvZGlyZWN0b3JpZXMucG5nKTwvYnI+CkRpcmVjdG9yaWVzIGluc3RhbGxlZCBieSBQcm9qZWN0VGVtcGxhdGU8L3A+CgpUbyBpbnN0YWxsIHRoZSBwYWNrYWdlIG9uZSBjb3VsZCB1c2UgYGluc3RhbGwucGFja2FnZXMoJ1Byb2plY3RUZW1wbGF0ZScpYCwgYnV0IEkgcHJlZmVyIGByZXF1aXJlKCdQcm9qZWN0VGVtcGxhdGUnKWAgYmVjYXVzZSBpdCBsb29rcyBpZiB0aGUgcGFja2FnZXMgaXMgYWxyZWFkeSBpbnN0YWxsZWQgYW5kIGF0dGFjaGVzIGl0IGF0IHRoZSBzYW1lIHRpbWUsIHNhdmluZyB0aGUgYGxpYnJhcnkoUHJvamVjdFRlbXBsYXRlKWAgY29tbWFuZC4gKEF0IGxlYXN0IHRoYXQgaXMgbXkgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHR3byBjb21tYW5kcy4pCgpBZnRlciB5b3UgaGF2ZSBsb2FkZWQgYFByb2plY3RUZW1wbGF0ZWAgeW91IG5lZWQgdG8gY2FsbCBgY3JlYXRlLnByb2plY3QoJzx5b3VyLWRpcmVjdG9yeS1uYW1lPicpYCB0byBnZW5lcmF0ZSB0aGUgYWJvdmUgZGlyZWN0b3J5IHN0cnVjdHVyZS4gTm93IHlvdSBjYW4gY2hhbmdlIGludG8gdGhpcyBmb2xkZXIgYW5kIGluc3BlY3QgYWxsIHRoZSBwcm92aWRlZCB1dGlsaXRpZXMuIEV2ZXJ5IGZvbGRlciBjb250YWlucyBhIGBSRUFETUUubWRgIGV4cGxhaW5pbmcgdGhlIGludGVuZGVkIHB1cnBvc2Ugb2YgdGhlIGZvbGRlci4gWW91IGhhdmUgdG8gZG8gdGhpcyBhcyB0aGUgdmVyeSBmaXJzdCBzdGVwLCBldmVuIGJlZm9yZSB5b3Ugb3BlbiBhIG1hcmtkb3duIGZpbGUuIEl0IGlzIGJlc3QgZG9uZSBmcm9tIHRoZSBjb25zb2xlLiAKCgpgYGB7ciBzYW1wbGUtY29kZS1mb3ItaW5zdGFsbGluZy1Qcm9qZWN0VGVtcGxhdGV9CiMgcmVxdWlyZSgnUHJvamVjdFRlbXBsYXRlJykKIyBjcmVhdGUucHJvamVjdCgnT3BlbkludHJvJykKYGBgCgpTb21lIG9mIHRoZSBmb2xkZXJzIGhhdmUgYWR2YW5jZWQgcHVycG9zZXMgYW5kIGdlbmVyYWxseSB0aGUgc3RydWN0dXJlIGlzIGludGVuZGVkIGZvciB1c2VycyB3aG8gaGF2ZSBhbHJlYWR5IHNvbWUgZXhwZXJpZW5jZXMgYW5kIHdvcmsgb24gYmlnZ2VyIHByb2plY3QuIEJ1dCBpdCBpcyBnb29kIHRvIGtub3cgdGhhdCBhIHNvbWV3aGF0IGRpZmZlcmVudGlhdGVkIGZvbGRlciBzdHJ1Y3R1cmUgaXMgaGVscGZ1bCBmb3IgdGhlIHByb2plY3Qgb3JnYW5pc2F0aW9uLiBBcyBJIGFtIHN0aWxsIG5vdCBoYXZlIGFkdmFuY2VkIHNraWxscyBpdCBpcyB0aGUgYmVzdCBhbmQgZWFzaWVzdCB3YXkgdG8gc2VwYXJhdGUgbXkgcmVwb3J0cywgZGF0YSBhbmQgZ2VuZXJhdGVkIG1hdGVyaWFsIChwbG90cykuIEJlY2F1c2UgSSBhbSB1c2luZyBgZ2l0YCByZXNwZWN0aXZlbHkgYEdpdEh1YmAgYXMgYSB2ZXJzaW9uIGNvbnRyb2wgc3lzdGVtIHRoZSBzZXBhcmF0aW9uIGJldHdlZW4gbXkgb3duIHdvcmsgYW5kIGZpbGVzIGdlbmVyYXRlZCBhdXRvbWF0aWNhbGx5IGR1cmluZyB0aGUgYW5hbHlzaXMgaXMgaGVscGZ1bCBhcyB0aGUgbGF0ZXIgc2hvdWxkIG5vdCBCRSBwdXNoZWQgaW50byB0aGUgR2l0SHViIHJlcG8uCgpTbyBJIHVzZSB0aGUgcmVjb21tZW5kZWQgcHJvZ3JhbSBjb2RlIHdpdGggc29tZSBjaGFuZ2VzOgoKKiBJIChyZSlzdG9yZSB0aGUgcGF0aCB0byB0aGUgd29ya2luZyBkaXJlY3RvcnkgaW4gb3JkZXIgdG8gZ2V0IGEgc3RhbmRhcmRpc2VzIHNpdHVhdGlvbi4gSSBub3RpY2VkIGFscmVhZHkgc29tZSBwcm9ibGVtcyB3aXRoIHRoZSBwYXRoIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeSwgZXNwZWNpYWxseSBpZiBJIGNoYW5nZSBjaGFuZ2UgaXQgdmlhIHRoZSBjb25zb2xlIGFuZCB0aGVuIHRvIHJlY29tcGlsZSB0aGUgc2NyaXB0LgoKKiBJIGNoZWNrIGlmIHRoZSBmaWxlIGlzIGFscmVhZHkgdGhlcmUuIEkgc3RhcnQgdGhlIGRvd25sb2FkIG9ubHkgaWYgdGhlIGZpbGUgaXMgbWlzc2luZy4KCiogSSBzdG9yZSB0aGUgZmlsZSBpbiBteSBgZGF0YWAgZm9sZGVyLCB3aGljaCBJIGhhdmUgY3JlYXRlZCBpbiBhbiBlYXJsaWVyIG9jY2FzaW9uLgoKYGBge3IgZG93bmxvYWQtZGF0YX0Kb2xkd2QgPC0gZ2V0d2QoKQpzZXR3ZChteS53ZCkKaWYgKCEoZmlsZS5leGlzdHMoImRhdGEvYW1lcy5SRGF0YSIpKSkgCiAgICAgICAgewogICAgICAgIGRvd25sb2FkLmZpbGUoImh0dHA6Ly93d3cub3BlbmludHJvLm9yZy9zdGF0L2RhdGEvYW1lcy5SRGF0YSIsCiAgICAgICAgICAgICAgZGVzdGZpbGUgPSAiZGF0YS9hbWVzLlJEYXRhIikKICAgICAgICB9CmxvYWQoImRhdGEvYW1lcy5SRGF0YSIpCnNldHdkKG9sZHdkKQpgYGAKCiMjIFRhc2sgMjogQmlibGlvZ3JhcGh5IG1hbmFnZXIgaW4gUk1hcmtkb3duCgpBbm90aGVyIHByb2JsZW0gSSBub3RpY2VkIGlzIHRoZSB1c2FnZSBvZiBzdGFuZGFyZGlzZWQgYmlibGlvZ3JhcGh5IGZvciByZXBvcnRzLiBUaGUgZGVzY3JpcHRpb24gZm9yIHRoZSBgYW1lc2AgZGF0YSBzZXQgaXMgYXZhaWxhYmxlIGF0IHRoZSB3ZWJzaXRlIG9mIHRoZSBbQW1lcmljYW4gU3Rhc3RpY2FsIEFzc29jaWF0aW9uIChBU0EpXShodHRwczovL3d3Mi5hbXN0YXQub3JnL3B1YmxpY2F0aW9ucy9qc2UvdjE5bjMvZGVjb2NrL0RhdGFEb2N1bWVudGF0aW9uLnR4dCkuIFRoZSBvZmZpY2FsIHB1Ymxpc2hlZCByZWZlcmVuY2UgaXM6IERlIENvY2sgRC4gMjAxMS4gQW1lcywgSW93YTogQWx0ZXJuYXRpdmUgdG8gdGhlIEJvc3RvbiBIb3VzaW5nIERhdGEgYXMgYW4gRW5kIG9mIFNlbWVzdGVyIFJlZ3Jlc3Npb24gUHJvamVjdC4gSm91cm5hbCBvZiBTdGF0aXN0aWNzIEVkdWNhdGlvbjsgMTkoMykuIFlvdSBjYW4gYWxzbyBbZG93bmxvYWQgdGhlIGZ1bGwgcGFwZXJdKGh0dHA6Ly93dzIuYW1zdGF0Lm9yZy9wdWJsaWNhdGlvbnMvanNlL3YxOW4zL2RlY29jay5wZGYpLiBCVFc6IFRvIGFwcGx5IHJlYWwgZGF0YSBzZXRzIHRvIGxlYXJuIHN0YXRpc3RpY3MgaXMgb25lIG9mIHRoZSByZWFzb25zIEkgYW0gdmVyeSBleGNpdGVkIGFib3V0IHRoZSB1c2Ugb2YgUi4gV2hlbiBJIGxlYXJuZWQgc3RhdGlzdGljcyBhdCB0aGUgdW5pdmVyc2l0eSBiYWNrIGluIHRoZSA3MHRpZXMsIGl0IHdhcyBhIHZlcnkgYm9yaW5nIGV4ZXJjaXNlIHdpdGggc21hbGwgYW5kIGFydGlmaWNpYWwgZGF0YS4gRXZlbiB0b2RheSBzb21lIG1vZGVybiBib29rcyBvbiBzdGF0aXN0aWNzIHByZXNlbnQgdGhlaXIgZXhlcmNpc2VzIHdpdGhvdXQgc3RhdGlzdGljYWwgc29mdHdhcmUgKGUuZy4gW1N0YXRpc3RpY3MgZm9yIER1bW1pZXNdKGh0dHBzOi8vd3d3LmFtYXpvbi5kZS9TdGF0aXN0aWNzLUR1bW1pZXMtRGVib3JhaC1KLVJ1bXNleS9kcC8xMTE5MjkzNTI5L3JlZj1zcl8xXzE/aWU9VVRGOCZxaWQ9MTQ4OTI1NTk1MyZzcj04LTEma2V5d29yZHM9ZHVtbXkrc3RhdGlzdGljcykpLiBNb3JlIG1hdGVyaWFsIHdpdGggYmVhdXRpZnVsIGdyYXBocyBjYW4gYmUgZm91bmQgYXQgW1NlbWFudGljU2NvbGFyXShodHRwczovL3d3dy5zZW1hbnRpY3NjaG9sYXIub3JnL3BhcGVyL0FtZXMtSW93YS1BbHRlcm5hdGl2ZS10by10aGUtQm9zdG9uLUhvdXNpbmctRGF0YS1Db2NrLzJkN2RiYmUwZTFhNTYwNjIwM2EwNjUxNzVjMTQxNWEzZGRhOWRjYzQpLgoKVG8gdXNlIGNpdGF0aW9ucyBhbmQgYmlibGlvZ3JhcGhpZXMgaW4gUk1hcmtkb3duIG9uZSBoYXMgdG8gYXBwbHkgY2VydGFpbiBydWxlcyBwdWJsaXNoZWQgaW4gW2RvY3VtZW50IGJ5IFJTdHVkaW9dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vYXV0aG9yaW5nX2JpYmxpb2dyYXBoaWVzX2FuZF9jaXRhdGlvbnMuaHRtbCkuIAoKSSBhbSBnZW5lcmFsbHkgdXNpbmcgWm90ZXJvIGZvciBteSBiaWJsaW9ncmFwaGllcy4gVW5mb3J0dW5hdGVseSB0aGlzIGlzIG5vdCBzdXBwb3J0ZWQgYnkgUk1hcmtkb3duLiBGb3IgbWUgdGhlIGVhc2llc3QgaW5zdGFsbGF0aW9uIGlzIGRvbmUgd2l0aCBhIC5iaWIgZmlsZS4gRXNwZWNpYWxseSBhcyBJIGhhdmUgdXNlZCBmb3IgdGhlIHByZXBhcmF0aW9uIG9mIG15IGxhc3QgYm9va3MgTHlYIGFuZCBoYWQgdGhlcmVmb3JlIHRvIGluc3RhbGwgQmliVGVYIHJlc3BlY3RpdmVseSBCaWJMYVRlWC4gSSBhbSB1c2luZyAgdGhlIHBsYXRmb3JtIGluZGVwZW5kZW50IGJpYmxpb2dyYXBoeSBtYW5hZ2VyIEphYlJlZiBidXQgYWxzbyBCaWJEZXNrIChqdXN0IGZvciBtYWMgT1MpLgoKVG8gY2l0ZSBhbiBhdXRob3Igb25lIGhhcyB0byB1c2UgdGhlIHVuaXF1ZSByZWZlcmVuY2UgY29kZSBmcm9tIHRoZSBiaWJsaW9ncmFwaHkgbWFuYWdlci4gCgo+IENpdGF0aW9ucyBnbyBpbnNpZGUgc3F1YXJlIGJyYWNrZXRzIGFuZCBhcmUgc2VwYXJhdGVkIGJ5IHNlbWljb2xvbnMuIEVhY2ggY2l0YXRpb24gbXVzdCBoYXZlIGEga2V5LCBjb21wb3NlZCBvZiDigJhA4oCZICsgdGhlIGNpdGF0aW9uIGlkZW50aWZpZXIgZnJvbSB0aGUgZGF0YWJhc2UsIGFuZCBtYXkgb3B0aW9uYWxseSBoYXZlIGEgcHJlZml4LCBhIGxvY2F0b3IsIGFuZCBhIHN1ZmZpeC4KCkZvciBpbnN0YW5jZSBJIGhhdmUgcmVmZXJlbmNlZCB0byBteSBiaWJsYmlvZ3JhcGh5IGRhdGFiYXNlIGluIHRoZSBZQU1MIG1ldGFkYXRhIHNlY3Rpb24gd2l0aCB0aGUgZm9sbG93aW5nIGxpbmU6CgpgYmlibGlvZ3JhcGh5OiAifi9Eb2N1bWVudHMvTWVpbmVcIEJpYmxpb2dyYXBoaWVuL2JhdW1nYXJ0bmVyLmJpYiJgCgpOb3cgSSBjYW4gZm9yIGV4YW1wbGUgY2l0ZSBteSBvd24gYXJ0aWNsZSBhYm91dCB0YXhvbm9taWVzIG9mIGVsZWN0cm9uaWMgcG9ydGZvbGlvcyBhcyBgQEJhdW1nYXJ0bmVyXzIwMDlhYCBpbiBzcXVhcmUgYnJhY2tldHMgW0BCYXVtZ2FydG5lcl8yMDA5YV0uIFJNYXJrZG93biBhZGRzIHRoZSBjb21wbGV0ZSBiaWJsaW9ncmFwaHkgYXQgdGhlIGVuZCBvZiB0aGUgZG9jdW1lbnQuIFRoZSBoZWFkZXIgIlJlZmVyZW5jZXMiIGlzIGFkZGVkIGJ5IG1lIG1hbnVhbGx5LiBBbmQgaGVyZSBpcyB0aGUgcmVmZXJlbmNlIHRvIHRoZSBhbWVzIGRhdGFzZXQgW0BkZV9jb2NrX2FtZXNfMjAxMV0uCgojIEZpbmFsbHk6IFN0YXJ0aW5nIHRoZSBsYWIKCkZpbmFsbHkgd2UgY2FuIG5vdyBzdGFydCB0aGUgbGFiIHdpdGggYSBzaW1wbGUgcmFuZG9tIHNhbXBsZSBvZiBzaXplIDYwIGZyb20gdGhlIHBvcHVsYXRpb24uIFNwZWNpZmljYWxseSwgdGhpcyBpcyBhIHNpbXBsZSByYW5kb20gc2FtcGxlIG9mIHNpemUgNjAuIE5vdGUgdGhhdCB0aGUgZGF0YSBzZXQgaGFzIGluZm9ybWF0aW9uIG9uIGByIG5jb2woYW1lcylgIGhvdXNpbmcgdmFyaWFibGVzLCBidXQgZm9yIHRoZSBmaXJzdCBwb3J0aW9uIG9mIHRoZSBsYWIgd2XigJlsbCBmb2N1cyBvbiB0aGUgc2l6ZSBvZiB0aGUgaG91c2UsIHJlcHJlc2VudGVkIGJ5IHRoZSB2YXJpYWJsZSBgR3IuTGl2LkFyZWFgLgoKKipOb3RlOioqIEZvciByZXByb2R1Y2liaWxpdHkgaW4gZ2VuZXJhdGluZyByYW5kb20gZGF0YSBJIGhhdmUgbGVhcm5lZCB0aGF0IG9uZSBoYXMgYWx3YXlzIHRvIHVzZSBgc2V0LnNlZWRgLiBTbyBJIHdpbGwgdXNlIGl0IGluIHRoZSBuZXh0IHByb2dyYW0gc25pcHBldC4gCgpUaGVyZSBhcmUgdHdvIHdheXMgdG8gZ2VuZXJhdGUgdGhlIHNhbXBsZS4gVGhlIG9sZCB2YXJpYW50IHdpdGggYHNhbXBsZSgpYCB0YWtlbiBmcm9tIFt0aGlzIHBhZ2VdKGh0dHA6Ly9odG1scHJldmlldy5naXRodWIuaW8vP2h0dHBzOi8vZ2l0aHViLmNvbS9hbmRyZXdwYnJheS9vaUxhYnMtYmFzZS1SL2Jsb2IvbWFzdGVyL2NvbmZpZGVuY2VfaW50ZXJ2YWxzL2NvbmZpZGVuY2VfaW50ZXJ2YWxzLmh0bWwpIGFuZCBhbm90aGVyIG9uZSB3aXRoIGBzYW1wbGVfbmAgYXMgZXhwbGFpbmVkIG9uIHRoZSBbbmV3ZXIgbGFiIHBhZ2VdKGh0dHA6Ly9odG1scHJldmlldy5naXRodWIuaW8vP2h0dHBzOi8vZ2l0aHViLmNvbS9wZXR6aTUzL29pTGFicy1kcGx5ci1nZ3Bsb3QvYmxvYi9tYXN0ZXIvMDZfY29uZmlkZW5jZV9pbnRlcnZhbHMvY29uZmlkZW5jZV9pbnRlcnZhbHMuaHRtbCkuCgpgYGB7ciBnZW5lcmF0ZS1zYW1wbGV9CiMgb2xkZXIsIHRyYWRpdGlvbmFsIG1ldGhvZApwb3B1bGF0aW9uIDwtIGFtZXMkR3IuTGl2LkFyZWEKc2V0LnNlZWQoMTIzNDUpCnNhbXAub2xkIDwtIHNhbXBsZShwb3B1bGF0aW9uLCA2MCkKaGVhZChzYW1wLm9sZCkKCiMgb3RoZXIgKG5ld2VyKSBtZXRob2QKbiA8LSA2MApzZXQuc2VlZCgxMjM0NSkgIyBmb3IgZWFjaCBzYW1wbGUgZHJhd24gb25lIGhhcyB0byB1c2UgdGhlIGBzZXQuc2VlZGAgZnVuY3Rpb24Kc2FtcC5uZXcuZGYgPC0gc2FtcGxlX24oYW1lcywgbikgIyByZXR1cm5zIGEgZGF0YSBmcmFtZSB3aGljaCBoYXMgdG8gYmUgdXNlZCBmb3IgZ2dwbG90CnNhbXAubmV3IDwtIHNhbXAubmV3LmRmJEdyLkxpdi5BcmVhICMgYSB2ZWN0b3Igb2YgaW50ZWdlcnMKaGVhZChzYW1wLm5ldykKYGBgCgpUaGUgZnVuY3Rpb24gYHNhbXBsZSgpYCB0YWtlcyBhIHNhbXBsZSBvZiB0aGUgc3BlY2lmaWVkIHNpemUgZnJvbSB0aGUgZWxlbWVudHMgb2YgeCAoYHBvcHVsYXRpb25gIGluIHRoaXMgY2FzZSkgdXNpbmcgZWl0aGVyIHdpdGggb3Igd2l0aG91dCByZXBsYWNlbWVudC4gCgo+IHVzYWdlOiBgc2FtcGxlKHgsIHNpemUsIHJlcGxhY2UgPSBGQUxTRSwgcHJvYiA9IE5VTEwpYAoKVGhlIGZ1bmN0aW9uIGBzYW1wbGVfbigpYCBpcyBhIHdyYXBwZXIgYXJvdW5kIHRoZSBiYXNlIGBzYW1wbGUuaW50YCB0byBtYWtlIGl0IGVhc3kgdG8gc2VsZWN0IHJhbmRvbSByb3dzIGZyb20gYSB0YWJsZS4gSXQgY3VycmVudGx5IG9ubHkgd29ya3MgZm9yIGxvY2FsIHRibHMuCgo+IHVzYWdlOiBgc2FtcGxlX24odGJsLCBzaXplLCByZXBsYWNlID0gRkFMU0UsIHdlaWdodCA9IE5VTEwsIC5lbnYgPSBwYXJlbnQuZnJhbWUoKSlgCgojIyBUYXNrIDM6IERyYXdpbmcgYSBoaXN0b2dyYW0KCkZvciBtZSBJIGhhZCBmaXJzdCB0byBjbGVhciB1cCBzb21lIGJhc2ljcyBhYm91dCB0aGUgW2RpZmZlcmVuY2UgYmV0d2VlbiBoaXN0b2dyYW0gYW5kIGJhcnBsb3RdKGh0dHA6Ly9zdGF0dHJlay5jb20vc3RhdGlzdGljcy9jaGFydHMvaGlzdG9ncmFtLmFzcHg/VHV0b3JpYWw9QVApLgoKRm9yIGEgZGlzY3JpcHRpb24gb2YgdGhlIHNhbXBsZSBkaXN0cmlidXRpb24gd2hpY2ggY29uc2lzdHMgb2YgcXVhbnRpdGF0aXZlIGRhdGEgSSBoYXZlIHRvIHBsb3QgYSBoaXN0b2dyYW0uIFRoZXJlIGFyZSB0d28gd2F5cyB0byBkbyBpdDogVGhlIG9sZCBvbmUgd2l0aCBgaGlzdGAgb3IgdGhlIG5ldyBvbmUgd2l0aCBgcXBsb3RgIG9yIGV2ZW4gYmV0dGVyIHdpdGggYGdncGxvdGAuIEZvciB0aGUgbmV3ZXIgdmVyc2lvbiB0aGVyZSBpcyBhIG5pY2UgYXJ0aWNsZSBbSG93IHRvIE1ha2UgYSBIaXN0b2dyYW0gd2l0aCBnZ3Bsb3QyXShIb3cgdG8gTWFrZSBhIEhpc3RvZ3JhbSB3aXRoIGdncGxvdDIpIGF0IFItQmxvZ2dlcnMgd2hpY2ggbm90IG9ubHkgZXhwbGFpbnMgaG93IHRvIHVzZSB0aGUgYmFzaWMgY29tbWFuZHMgYnV0IGFsc28gaG93IHRvIHJlZmluZSB0aGUgYXBwZWFyYW5jZSBvZiB0aGUgaGlzdG9ncmFtLgoKCmBgYHtyIGRyYXdpbmctdGhlLWhpc3RvZ3JhbX0Kc2V0LnNlZWQoMTIzNDUpCmhpc3Qoc2FtcC5vbGQsIAogICAgIGJyZWFrcyA9IDE1LCAKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBteSBzYW1wbGUgZHJhd24gd2l0aCB0aGUgYGhpc3RgIGZ1bmN0aW9uIikKcXBsb3Qoc2FtcC5vbGQsIAogICAgICBiaW5zID0gMTUsIAogICAgICBnZW9tID0gImhpc3RvZ3JhbSIsCiAgICAgIG1haW4gPSAiSGlzdG9ncmFtIG9mIG15IHNhbXBsZSBkcmF3biB3aXRoIHRoZSBgcXBsb3RgIGZ1bmN0aW9uIikKcCA8LSBnZ3Bsb3Qoc2FtcC5uZXcuZGYsIGFlcyhHci5MaXYuQXJlYSkpICMgc2FtcC5uZXcgYXMgaW50ZWdlciB2ZWN0b3IgZG9lcyBub3Qgd29yaywgZ2dwbG90IG5lZWRzIGEgZGF0YS5mcmFtZSAKcCA8LSBwICsgIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSkKcCA8LSBwICsgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2YgbXkgc2FtcGxlIGRyYXduIHdpdGggdGhlIGBnZ3Bsb3RgIGZ1bmN0aW9uIikKcApgYGAKCllvdSBjYW4gc2VlIHRoYXQgdGhlIGBnZ3Bsb3RgIGZ1bmN0aW9uIGlzIG1vcmUgY29tcGxleCBidXQgaXQgaGFzIG11Y2ggbW9yZSBwYXJhbWV0ZXJzIHRvIHNldC4gVGhlIG5pY2UgdGhpbnMgaXMgdGhhdCB5b3UgY2FuIGFkZCBsYXllciBieSBsYXllci4gWW91IGNhbiBzZWUgdGhlIGxheWVycyB3aXRoIHRoZSBkaWZmZXJlbnQgbGluZXMgb2YgYXNzaWdubWVudHMgdG8gYHBgLiAKCmBnZ3Bsb3QoKWAgaXMgdXNlZCB0byBjb25zdHJ1Y3QgdGhlIGluaXRpYWwgcGxvdCBvYmplY3QsIGFuZCBpcyBhbG1vc3QgYWx3YXlzIGZvbGxvd2VkIGJ5ICsgdG8gYWRkIGNvbXBvbmVudCB0byB0aGUgcGxvdC4gVGhlcmUgYXJlIHRocmVlIGNvbW1vbiB3YXlzIHRvIGludm9rZSBnZ3Bsb3Q6Cgo+IDEuIGdncGxvdChkZiwgYWVzKHgsIHksIDxvdGhlciBhZXN0aGV0aWNzPikpCgo+IDIuIGdncGxvdChkZikKCj4gMy4gZ2dwbG90KCkKCiogKipUaGUgZmlyc3QgbWV0aG9kKiogaXMgcmVjb21tZW5kZWQgaWYgYWxsIGxheWVycyB1c2UgdGhlIHNhbWUgZGF0YSBhbmQgdGhlIHNhbWUgc2V0IG9mIGFlc3RoZXRpY3MsIGFsdGhvdWdoIHRoaXMgbWV0aG9kIGNhbiBhbHNvIGJlIHVzZWQgdG8gYWRkIGEgbGF5ZXIgdXNpbmcgZGF0YSBmcm9tIGFub3RoZXIgZGF0YSBmcmFtZS4gCgoqICoqVGhlIHNlY29uZCBtZXRob2QqKiBzcGVjaWZpZXMgdGhlIGRlZmF1bHQgZGF0YSBmcmFtZSB0byB1c2UgZm9yIHRoZSBwbG90LCBidXQgbm8gYWVzdGhldGljcyBhcmUgZGVmaW5lZCB1cCBmcm9udC4gVGhpcyBpcyB1c2VmdWwgd2hlbiBvbmUgZGF0YSBmcmFtZSBpcyB1c2VkIHByZWRvbWluYW50bHkgYXMgbGF5ZXJzIGFyZSBhZGRlZCwgYnV0IHRoZSBhZXN0aGV0aWNzIG1heSB2YXJ5IGZyb20gb25lIGxheWVyIHRvIGFub3RoZXIuIAoKKiAqKlRoZSB0aGlyZCBtZXRob2QqKiBpbml0aWFsaXplcyBhIHNrZWxldG9uIGdncGxvdCBvYmplY3Qgd2hpY2ggaXMgZmxlc2hlZCBvdXQgYXMgbGF5ZXJzIGFyZSBhZGRlZC4gVGhpcyBtZXRob2QgaXMgdXNlZnVsIHdoZW4gbXVsdGlwbGUgZGF0YSBmcmFtZXMgYXJlIHVzZWQgdG8gcHJvZHVjZSBkaWZmZXJlbnQgbGF5ZXJzLCBhcyBpcyBvZnRlbiB0aGUgY2FzZSBpbiBjb21wbGV4IGdyYXBoaWNzLgoKCiMjIEV4ZXJjaXNlIDE6IERlc2NyaWJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2FtcGxlCkRlc2NyaWJlIHRoZSBkaXN0cmlidXRpb24gb2YgaG91c2UgYXJlYSBpbiB5b3VyIHNhbXBsZS4gV2hhdCB3b3VsZCB5b3Ugc2F5IGlzIHRoZSAidHlwaWNhbCIgc2l6ZSB3aXRoaW4geW91ciBzYW1wbGU/IEFsc28gc3RhdGUgcHJlY2lzZWx5IHdoYXQgeW91IGludGVycHJldGVkICJ0eXBpY2FsIiB0byBtZWFuLgoKKipNeSBBbnN3ZXI6KiogTXkgZGlzdHJpYnV0aW9uIGlzIHNvbWV3YWh0IGxlZnQgc2tld2VkIGFuZCBoYXMgc29tZSBvdXRsaWVycyBhdCB0aGUgaGlnaCBlbmQuIFlvdSB3aWxsIHNlZSBsYXRlciB0aGF0IG15IGV4YW1wbGUgaXMgYSBsaXR0bGUgZXh0cmVtIGFzIGRpZmZlcmVudCBzYW1wbGVzICh3aXRoIG90aGVyIG9yIG5vIHNlZWRzKSBwcm9kdWNlIGEgbW9yZSByZWd1bGFyIGZvcm0gb2YgdGhlIGRpc3RyaWJ1dGlvbi4KCgojIyBFeGVyY2lzZSAyOiBFeHBlY3RhdGlvbiBvZiBkaXN0cmlidXRpb25zCldvdWxkIHlvdSBleHBlY3QgYW5vdGhlciBzdHVkZW50J3MgZGlzdHJpYnV0aW9uIHRvIGJlIGlkZW50aWNhbCB0byB5b3Vycz8gV291bGQgeW91IGV4cGVjdCBpdCB0byBiZSBzaW1pbGFyPyBXaHkgb3Igd2h5IG5vdD8KCioqTXkgQW5zd2VyOioqIE5vdCBpZGVudGljYWwgYnV0IHNpbWlsaWFyLiBJbiBvcmRlciB0byBnZXQgZXhhY3RseSB0aGUgc2FtZSBkYXRhIHRvIHdvcmsgd2l0aCBhbm90aGVyIHN0dWRlbnQgd291bGQgaGF2ZSB0byB1c2UgYHNldC5zZWVkYCB3aXRoIHRoZSBzYW1lIHZhbHVlIGFzIEkgaGF2ZSB1c2VkLiAKCgojIyBUYXNrIDQ6IFBsb3RzIHdpdGggZGlmZmVyZW50IHNhbXBsZXMKVGFrZSBhd2F5IHRoZSBzZWVkIGFuZCB0cnkgdG8gcnVuIHRoZSBwbG90IHNldmVyYWwgdGltZXMuIFlvdSB3aWxsIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gY2hhbmdlcyBpdCBhcHBlYXJhbmNlcy4gCgpOb3RlOiBWZXJ5IGltcG9ydGFudCBmb3IgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGhpc3RvZ3JhbW0gaXMgYWxzbyB0aGUgbnVtYmVyIG9mIGJpbnMuIFlvdSBoYXZlIHRvIGV4ZXJwaW1lbnQgd2hhdCBudW1iZXIgZ2l2ZXMgYSBuaWNlIGhpc3RvZ3JhbW0uIEZvciBjb21wYXJpc29ucyB5b3UgaGF2ZSB0byBzZXQgaXQgY29uc3RhbnQgaW4gYWxsIGV4YW1wbGVzLiBJbiBteSBjYXNlOiBgYmlucyA9IDE1YC4gCgpJIHdpbGwgc2ltdWxhdGUgdGhlIGRpZmZlcmVuY2UgYXBwZWFyYW5jZXMgb2YgdGhlIGRpc3RyaWJ1dGlvbiBpbiB0aGUgbmV4dCBwcm9ncmFtIHNuaXBwZXQgdXNpbmcgdGhlIHNhbWUgY29kZSBmb3IgZHJhd2luZyB0aGUgaGlzdG9ncmFtbSBidXQgd2l0aCBkaWZmZXJlbnQgc2VlZHMuIAoKYGBge3Igc2FtcGxlcy13aXRoLWRpZmZlcmVudC1zZWVkc30KbiA8LSA2MApzZXQuc2VlZCgxMjM0NSkgIyBmb3IgZWFjaCBzYW1wbGUgZHJhd24gb25lIGhhcyB0byB1c2UgdGhlIGBzZXQuc2VlZGAgZnVuY3Rpb24Kc2FtcDEgPC0gc2FtcGxlX24oYW1lcywgbikKcDEgPC0gZ2dwbG90KHNhbXAxLCBhZXMoR3IuTGl2LkFyZWEpKQpwMSA8LSBwMSArICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTUpCnAxIDwtIHAxICsgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW06IFZlcnNpb24gMSwgYWxyZWFkeSB1c2VkIGFib3ZlIikKcDEKCnNldC5zZWVkKDExMTExKSAjIGZvciBlYWNoIHNhbXBsZSBkcmF3biBvbmUgaGFzIHRvIHVzZSB0aGUgYHNldC5zZWVkYCBmdW5jdGlvbgpzYW1wMiA8LSBzYW1wbGVfbihhbWVzLCBuKQpwMiA8LSBnZ3Bsb3Qoc2FtcDIsIGFlcyhHci5MaXYuQXJlYSkpCnAyIDwtIHAyICsgIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSkKcDIgPC0gcDIgKyBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbTogVmVyc2lvbiAyIikKcDIKCnNldC5zZWVkKDU0MzIxKSAjIGZvciBlYWNoIHNhbXBsZSBkcmF3biBvbmUgaGFzIHRvIHVzZSB0aGUgYHNldC5zZWVkYCBmdW5jdGlvbgpzYW1wMyA8LSBzYW1wbGVfbihhbWVzLCBuKQpwMyA8LSBnZ3Bsb3Qoc2FtcDMsIGFlcyhHci5MaXYuQXJlYSkpCnAzIDwtIHAzICsgIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSkKcDMgPC0gcDMgKyBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbTogVmVyc2lvbiAzIikKcDMKCmBgYAoKIyBDYWx1bGF0aW5nIHRoZSBjb25maWRlbmNlIGludGVydmFscwoKTGV0cyByZXR1cm4gZm9yIGEgbW9tZW50IHRvIHRoZSBxdWVzdGlvbiB0aGF0IGZpcnN0IG1vdGl2YXRlZCB0aGlzIGxhYjogYmFzZWQgb24gdGhpcyBzYW1wbGUsIHdoYXQgY2FuIHdlIGluZmVyIGFib3V0IHRoZSBwb3B1bGF0aW9uPyBCYXNlZCBvbmx5IG9uIHRoaXMgc2luZ2xlIHNhbXBsZSwgdGhlIGJlc3QgZXN0aW1hdGUgb2YgdGhlIGF2ZXJhZ2UgbGl2aW5nIGFyZWEgb2YgaG91c2VzIHNvbGQgaW4gQW1lcyB3b3VsZCBiZSB0aGUgc2FtcGxlIG1lYW4sIHVzdWFsbHkgZGVub3RlZCBhcyAkXGJhcnt4fSQgKGhlcmUgd2UncmUgY2FsbGluZyBpdCBgeF9iYXJgKS4gVGhhdCBzZXJ2ZXMgYXMgYSBnb29kICoqcG9pbnQgZXN0aW1hdGUqKiBidXQgaXQgd291bGQgYmUgdXNlZnVsIHRvIGFsc28gY29tbXVuaWNhdGUgaG93IHVuY2VydGFpbiB3ZSBhcmUgb2YgdGhhdCBlc3RpbWF0ZS4gVGhpcyB1bmNlcnRhaW50eSBjYW4gYmUgcXVhbnRpZmllZCB1c2luZyBhICoqY29uZmlkZW5jZSBpbnRlcnZhbCoqLgoKQSBjb25maWRlbmNlIGludGVydmFsIGZvciBhIHBvcHVsYXRpb24gbWVhbiBpcyBvZiB0aGUgZm9sbG93aW5nIGZvcm0KCj4gJFxiYXJ7eH0gKyB6XlxzdGFyIFxmcmFje3N9e1xzcXJ0e259fSQgCgojIyBUYXNrIDU6IEVxdWF0aW9ucyBpbiBSTWFya2Rvd24KSGVyZSBpcyBhIGxpdHRsZSBwcmltZXIgW2hvdyB0byB3cml0ZSBlcXVhdGlvbnMgaW4gUk1hcmtkb3duXShodHRwOi8vd3d3Lm1vbnRhbmEuZWR1L3JvdGVsbGEvZG9jdW1lbnRzLzUwMi9NYXJrZG93bkVxbkV4YW1wbGVzLlJtZCkuCgpZb3Ugc2hvdWxkIGJ5IG5vdyBiZSBjb21mb3J0YWJsZSB3aXRoIGNhbGN1bGF0aW5nIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgYSBzYW1wbGUgaW4gUi4gQW5kIHdlIGtub3cgdGhhdCB0aGUgc2FtcGxlIHNpemUgaXMgNjAuIFNvIHRoZSBvbmx5IHJlbWFpbmluZyBidWlsZGluZyBibG9jayBpcyBmaW5kaW5nIHRoZSBhcHByb3ByaWF0ZSBjcml0aWNhbCB2YWx1ZSBmb3IgYSBnaXZlbiBjb25maWRlbmNlIGxldmVsLiBXZSBjYW4gdXNlIHRoZSBxbm9ybSBmdW5jdGlvbiBmb3IgdGhpcyB0YXNrLCB3aGljaCB3aWxsIGdpdmUgdGhlIGNyaXRpY2FsIHZhbHVlIGFzc29jaWF0ZWQgd2l0aCBhIGdpdmVuIHBlcmNlbnRpbGUgdW5kZXIgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24uIFJlbWVtYmVyIHRoYXQgY29uZmlkZW5jZSBsZXZlbHMgYW5kIHBlcmNlbnRpbGVzIGFyZSBub3QgZXF1aXZhbGVudC4gRm9yIGV4YW1wbGUsIGEgOTUlIGNvbmZpZGVuY2UgbGV2ZWwgcmVmZXJzIHRvIHRoZSBtaWRkbGUgOTUlIG9mIHRoZSBkaXN0cmlidXRpb24sIGFuZCB0aGUgY3JpdGljYWwgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgYXJlYSB3aWxsIGNvcnJlc3BvbmQgdG8gdGhlIDk3LjV0aCBwZXJjZW50aWxlLgoKQWdhaW4gdGhlcmUgYXJlIHR3byBtZXRob2RzIGhlcmUgYXMgaWxsdXN0cmF0ZWQgaW4gdGhlIHR3byBsYWJzIGRvY291bWVudCBtZW50aW9uZWQgYWJvdmUuCgojIyBDb25maWRlbmNlIGludGVydmFscyBjYWxjdWxhdGVkIG1hbnVhbGx5CgpPbmUgb2YgdGhlIG1vc3QgY29tbW9uIHdheXMgdG8gZGVzY3JpYmUgdGhlIHR5cGljYWwgb3IgY2VudHJhbCB2YWx1ZSBvZiBhIGRpc3RyaWJ1dGlvbiBpcyB0byB1c2UgdGhlIG1lYW4uIEluIHRoaXMgY2FzZSB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBtZWFuIG9mIHRoZSBzYW1wbGUgdXNpbmcsCgpXZSBjYW4gY2FsY3VsYXRlIGEgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGEgc2FtcGxlIG1lYW4gYnkgYWRkaW5nIGFuZCBzdWJ0cmFjdGluZyAxLjk2IHN0YW5kYXJkIGVycm9ycyB0byB0aGUgcG9pbnQgZXN0aW1hdGUgKFNlZSBTZWN0aW9uIDQuMi4zIGlmIHlvdSBhcmUgdW5mYW1pbGlhciB3aXRoIHRoaXMgZm9ybXVsYSkuCgpgYGB7ciBjYWxjdWxhdGluZy1jb25maWRlbmNlLWludGVydmFsLW9sZGVyLW1ldGhvZH0Kc2FtcGxlX21lYW4gPC0gbWVhbihzYW1wLm9sZCkKc2UgPC0gc2Qoc2FtcC5vbGQpIC8gc3FydCg2MCkKbG93ZXIgPC0gc2FtcGxlX21lYW4gLSAxLjk2ICogc2UKdXBwZXIgPC0gc2FtcGxlX21lYW4gKyAxLjk2ICogc2UKYyhsb3dlciwgdXBwZXIpCmBgYAoKIyMgQ29uZmlkZW5jZSBpbnRlcnZhbHMgY2FsY3VsYXRlZCB3aXRoIHRoZSBgcW5vcm1gIGZ1bmN0aW9uCgpXZSBjYW4gdXNlIHRoZSBgcW5vcm1gIGZ1bmN0aW9uIGZvciB0aGlzIHRhc2ssIHdoaWNoIHdpbGwgZ2l2ZSB0aGUgY3JpdGljYWwgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIGEgZ2l2ZW4gcGVyY2VudGlsZSB1bmRlciB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gUmVtZW1iZXIgdGhhdCAqKmNvbmZpZGVuY2UgbGV2ZWxzIGFuZCBwZXJjZW50aWxlcyBhcmUgbm90IGVxdWl2YWxlbnQqKi4gRm9yIGV4YW1wbGUsIGEgOTUlIGNvbmZpZGVuY2UgbGV2ZWwgcmVmZXJzIHRvIHRoZSBtaWRkbGUgOTUlIG9mIHRoZSBkaXN0cmlidXRpb24sIGFuZCB0aGUgY3JpdGljYWwgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgYXJlYSB3aWxsIGNvcnJlc3BvbmQgdG8gdGhlIDk3LjV0aCBwZXJjZW50aWxlLgoKV2UgY2FuIGZpbmQgdGhlIGNyaXRpY2FsIHZhbHVlIGZvciBhIDk1JSBjb25maWRlbmNlIGludGVydmFsIHVzaW5nCgpgYGB7ciBjYWxjdWxhdGUtel9zdGFyfQp6X3N0YXJfOTUgPC0gcW5vcm0oMC45NzUpCnpfc3Rhcl85NQpgYGAKd2hpY2ggaXMgcm91Z2hseSBlcXVhbCB0byB0aGUgdmFsdWUgY3JpdGljYWwgdmFsdWUgMS45NiB0aGF0IEkgYW1uIGZhbWlsaWFyIHdpdGggYnkgbm93LgoKTGV04oCZcyBmaW5hbGx5IGNhbGN1bGF0ZSB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbDoKCmBgYHtyIGNvbmZpZGVuY2UtaW50ZXJ2YWx9CnNhbXAubmV3LmRmICU+JQogIHN1bW1hcmlzZShsb3dlciA9IG1lYW4oc2FtcC5uZXcpIC0gel9zdGFyXzk1ICogKHNkKHNhbXAubmV3KSAvIHNxcnQobikpLAogICAgICAgICAgICB1cHBlciA9IG1lYW4oc2FtcC5uZXcpICsgel9zdGFyXzk1ICogKHNkKHNhbXAubmV3KSAvIHNxcnQobikpKQpgYGAKCllvdSBzZWUgdGhhdCB0aGUgcmVzdWx0IGlzIHRoZSBzYW1lIGluIGJvdGggdmFyaWFudHMuCgpUbyByZWNhcDogZXZlbiB0aG91Z2ggd2UgZG9u4oCZdCBrbm93IHdoYXQgdGhlIGZ1bGwgcG9wdWxhdGlvbiBsb29rcyBsaWtlLCB3ZeKAmXJlIDk1JSBjb25maWRlbnQgdGhhdCB0aGUgdHJ1ZSBhdmVyYWdlIHNpemUgb2YgaG91c2VzIGluIEFtZXMgbGllcyBiZXR3ZWVuIHRoZSB2YWx1ZXMgbG93ZXIgYW5kIHVwcGVyLiBUaGVyZSBhcmUgYSBmZXcgY29uZGl0aW9ucyB0aGF0IG11c3QgYmUgbWV0IGZvciB0aGlzIGludGVydmFsIHRvIGJlIHZhbGlkLgoKIyMgRXhlcmNpc2UgMzogQ29uZGl0aW9ucyB0byBiZSBtZXQKRm9yIHRoZSBjb25maWRlbmNlIGludGVydmFsIHRvIGJlIHZhbGlkLCB0aGUgc2FtcGxlIG1lYW4gbXVzdCBiZSBub3JtYWxseSBkaXN0cmlidXRlZCBhbmQgaGF2ZSBzdGFuZGFyZCBlcnJvciAkcyAvIFxzcXJ0e259JC4gV2hhdCBjb25kaXRpb25zIG11c3QgYmUgbWV0IGZvciB0aGlzIHRvIGJlIHRydWU/CgojIENvbmZpZGVuY2UgbGV2ZWxzCgojIyBFeGVyY2lzZSA0OiBNZWFuaW5nIG9mIGNvbmZpZGVuY2UgaW50ZXJ2YWwKV2hhdCBkb2VzIOKAnDk1JSBjb25maWRlbmNl4oCdIG1lYW4/CgoKSW4gdGhlIGNhc2Ugb2Ygb3VyIGRhdGEgc2V0IHdlIGhhdmUgdGhlIHJhcmUgbHV4dXJ5IG9mIGtub3dpbmcgdGhlIHRydWUgcG9wdWxhdGlvbiBtZWFuIHNpbmNlIHdlIGhhdmUgZGF0YSBvbiB0aGUgZW50aXJlIHBvcHVsYXRpb24uIExldOKAmXMgY2FsY3VsYXRlIHRoaXMgdmFsdWUgc28gdGhhdCB3ZSBjYW4gZGV0ZXJtaW5lIGlmIG91ciBjb25maWRlbmNlIGludGVydmFscyBhY3R1YWxseSBjYXB0dXJlIGl0LiBXZeKAmWxsIHN0b3JlIGl0IGluIGEgZGF0YSBmcmFtZSBjYWxsZWQgYHBhcmFtc2AgKHNob3J0IGZvciBwb3B1bGF0aW9uIHBhcmFtZXRlcnMpLCBhbmQgbmFtZSBpdCBgbXVgLgoKIyMgRXhlcmNpc2UgNToKRG9lcyBteSBjb25maWRlbmNlIGludGVydmFsIGNhcHR1cmUgdGhlIHRydWUgYXZlcmFnZSBzaXplIG9mIGhvdXNlcyBpbiBBbWVzPyAKCmBgYHtyIGNhcHR1cmVzLW15LWNvbmZpZGVuY2UtbGV2ZWxzLW11fQpwYXJhbXMgPC0gYW1lcyAlPiUKICBzdW1tYXJpc2UobXUgPSBtZWFuKEdyLkxpdi5BcmVhKSkKc3ByaW50ZigibXUgPSAlZjsgc2FtcGxlIG1lYW4gPSAlZiwgbG93ZXIgPSAlZiwgdXBwZXIgPSAlZiIsIHBhcmFtcyRtdSwgc2FtcGxlX21lYW4sIGxvd2VyLCB1cHBlcikKCmNpLnJlc3VsdCA8LSBpZmVsc2UobG93ZXIgPCBwYXJhbXMkbXUgJiB1cHBlciA+IHBhcmFtcyRtdSwgIllFUyEiLCAiTk8hIikKc3ByaW50ZigiRG9lcyBteSBjb25maWRlbmNlIGludGVydmFsIGNhcHR1cmUgdGhlIHRydWUgYXZlcmFnZSBzaXplIG9mIGhvdXNlcyBpbiBBbWVzPyAlcyIsIGNpLnJlc3VsdCApCmBgYAoKWUVTIQoKIyMgRXhlcmNpc2UgNjoKRWFjaCBzYW1wbGUgdGFrZW4gd2l0aCBhIGRpZmZlcmVudCBgc2V0LnNlZWRgIHNob3VsZCBoYXZlIGdvdHRlbiBhIHNsaWdodGx5IGRpZmZlcmVudCBjb25maWRlbmNlIGludGVydmFsLiBXaGF0IHByb3BvcnRpb24gb2YgdGhvc2UgaW50ZXJ2YWxzIHdvdWxkIHlvdSBleHBlY3QgdG8gY2FwdHVyZSB0aGUgdHJ1ZSBwb3B1bGF0aW9uIG1lYW4/IFdoeT8KCkFib3V0IDk1JSBvZiB0aGUgc2FtcGxlIG1lYW4gc2hvdWxkIGJlIGJldHdlZW4gdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwuCgojIENvbGxlY3RpbmcgbWFueSBzYW1wbGVzIHByb2dyYW1tYXRpY2FsbHkKClVzaW5nIFIsIHdl4oCZcmUgZ29pbmcgdG8gY29sbGVjdCBtYW55IHNhbXBsZXMgdG8gbGVhcm4gbW9yZSBhYm91dCBob3cgc2FtcGxlIG1lYW5zIGFuZCBjb25maWRlbmNlIGludGVydmFscyB2YXJ5IGZyb20gb25lIHNhbXBsZSB0byBhbm90aGVyLgoKSGVyZSBpcyB0aGUgcm91Z2ggb3V0bGluZToKCiogT2J0YWluIGEgcmFuZG9tIHNhbXBsZS4KCiogQ2FsY3VsYXRlIHRoZSBzYW1wbGXigJlzIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHVzZSB0aGVzZSB0byBjYWxjdWxhdGUgYW5kIHN0b3JlIHRoZSBsb3dlciBhbmQgdXBwZXIgYm91bmRzIG9mIHRoZSBjb25maWRlbmNlIGludGVydmFscy4KCiogUmVwZWF0IHRoZXNlIHN0ZXBzIDUwIHRpbWVzLgoKV2UgY2FuIGFjY29tcGxpc2ggdGhpcyB1c2luZyB0aGUgYHJlcF9zYW1wbGVfbmAgZnVuY3Rpb24uIChUaGlzIGlzIGEgZnVuY3Rpb24gb2YgYG9pbGFic2AgcGFja2FnZSEpIFRoZSBmb2xsb3dpbmcgbGluZXMgb2YgY29kZSB0YWtlcyA1MCByYW5kb20gc2FtcGxlcyBvZiBzaXplIG4gZnJvbSBwb3B1bGF0aW9uIChhbmQgcmVtZW1iZXIgd2UgZGVmaW5lZCBuPTYwIGVhcmxpZXIpLCBhbmQgY29tcHV0ZXMgdGhlIHVwcGVyIGFuZCBsb3dlciBib3VuZHMgb2YgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGJhc2VkIG9uIHRoZXNlIHNhbXBsZXMuCgpgYGB7ciBjb2xsZWN0aW5nLXNhbXBsZXMtcHJvZ3JhbW1hdGljYWxseX0KY2kgPC0gYW1lcyAlPiUKICAgICAgICByZXBfc2FtcGxlX24oc2l6ZSA9IG4sIHJlcHMgPSA1MCwgcmVwbGFjZSA9IFRSVUUpICU+JQogICAgICAgIHN1bW1hcmlzZShsb3dlciA9IG1lYW4oR3IuTGl2LkFyZWEpIC0gel9zdGFyXzk1ICogKHNkKEdyLkxpdi5BcmVhKSAvIHNxcnQobikpLAogICAgICAgICAgICAgICAgICB1cHBlciA9IG1lYW4oR3IuTGl2LkFyZWEpICsgel9zdGFyXzk1ICogKHNkKEdyLkxpdi5BcmVhKSAvIHNxcnQobikpKQpgYGAKCkxldOKAmXMgdmlldyB0aGUgZmlyc3QgZml2ZSBpbnRlcnZhbHM6CgpgYGB7ciBzaG93LWZpcnN0LWludGVydmFsc30KY2kgJT4lCiAgc2xpY2UoMTo1KQpgYGAKCgpOZXh0IHdl4oCZbGwgY3JlYXRlIGEgcGxvdCBzaW1pbGFyIHRvIEZpZ3VyZSA0Ljggb24gcGFnZSAxNzUgb2YgT3BlbkludHJvIFN0YXRpc3RpY3MsIDNyZCBFZGl0aW9uLiBGaXJzdCBzdGVwIHdpbGwgYmUgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGluIHRoZSBgY2lgIGRhdGEgZnJhbWUgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciB0aGUgaW50ZXJ2YWwgZG9lcyBvciBkb2VzIG5vdCBjYXB0dXJlIHRoZSB0cnVlIHBvcHVsYXRpb24gbWVhbi4gTm90ZSB0aGF0IGNhcHR1cmluZyB0aGlzIHZhbHVlIHdvdWxkIG1lYW4gdGhlIGxvd2VyIGJvdW5kIG9mIHRoZSBjb25maWRlbmNlIGludGVydmFsIGlzIGJlbG93IHRoZSB2YWx1ZSBhbmQgdXBwZXIgYm91bmQgb2YgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaXMgYWJvdmUgdGhlIHZhbHVlLiBSZW1lbWJlciB0aGF0IHdlIGNyZWF0ZSBuZXcgdmFyaWFibGVzIHVzaW5nIHRoZSBgbXV0YXRlYCBmdW5jdGlvbi4KCmBgYHtyIGNpLWNhcHR1cmVzLXllcy1vci1ub30KY2kgPC0gY2kgJT4lCiAgbXV0YXRlKGNhcHR1cmVfbXUgPSBpZmVsc2UobG93ZXIgPCBwYXJhbXMkbXUgJiB1cHBlciA+IHBhcmFtcyRtdSwgInllcyIsICJubyIpKQpgYGAKClRoZSBgaWZlbHNlYCBmdW5jdGlvbiBpcyBuZXcuIEl0IHRha2VzIHRocmVlIGFyZ3VtZW50czogZmlyc3QgaXMgYSBsb2dpY2FsIHN0YXRlbWVudCwgc2Vjb25kIGlzIHRoZSB2YWx1ZSB3ZSB3YW50IGlmIHRoZSBsb2dpY2FsIHN0YXRlbWVudCB5aWVsZHMgYSB0cnVlIHJlc3VsdCwgYW5kIHRoZSB0aGlyZCBpcyB0aGUgdmFsdWUgd2Ugd2FudCBpZiB0aGUgbG9naWNhbCBzdGF0ZW1lbnQgeWllbGRzIGEgZmFsc2UgcmVzdWx0LgoKV2Ugbm93IGhhdmUgYWxsIHRoZSBpbmZvcm1hdGlvbiB3ZSBuZWVkIHRvIGNyZWF0ZSB0aGUgcGxvdCwgYnV0IHdlIG5lZWQgdG8gcmUtb3JnYW5pemUgb3VyIGRhdGEgYSBiaXQgZm9yIGVhc3kgcGxvdHRpbmcuIFNwZWNpZmljYWxseSwgd2UgbmVlZCB0byBvcmdhbml6ZSB0aGUgZGF0YSBpbiBhIG5ldyBkYXRhIGZyYW1lIHdoZXJlIGVhY2ggcm93IHJlcHJlc2VudHMgb25lIGJvdW5kLCBhcyBvcHBvc2VkIHRvIG9uZSBpbnRlcnZhbC4gU28gdGhpcwoKYGBge3Igc2hvdy1jaX0KaGVhZChjaSkKYGBgCnNob3VsZCBsaWtlIHRoaXMKCgpgYGB7ciBzaG93LWNpLWRhdGEtYWZ0ZXItdHJhbnNmb3JtaW5nfQpjaV9kYXRhIDwtIGRhdGEuZnJhbWUoY2lfaWQgPSBjKDE6NTAsIDE6NTApLAogICAgICAgICAgICAgICAgICAgICAgY2lfYm91bmRzID0gYyhjaSRsb3dlciwgY2kkdXBwZXIpLAogICAgICAgICAgICAgICAgICAgICAgY2FwdHVyZV9tdSA9IGMoY2kkY2FwdHVyZV9tdSwgY2kkY2FwdHVyZV9tdSkpCmhlYWQoY2lfZGF0YSkKYGBgCgpUaGUgc3RydWN0dXJlIG9mIGBjaV9kYXRhYCBpcyBpbnRlcmVzdGluZy4gSXQgaGFzIDEwMCByb3dzIGJlY2F1c2Ugb2YgYWRkaW5nIHdpdGggdGhlIGNvbWJpbmUgZnVuY3Rpb24gKGBjaV9pZCA9IGMoMTo1MCwgMTo1MClgLiBjaV9pZCBjb3VudHMgZnJvbSAxIHRvIDUwIGFuZCB0aGVuIHN0YXJ0cyBhZ2Fpbi4gYGNpX2lkYCBpcyB0aGUgaWQgb2YgdGhlIHNhbXBsZSB3aGljaCBpcyBkcmF3biBpbiB0aGUgZm9sbG93aW5nIHBsb3QgYXMgeS1heGlzLiBUaGlzIG1lYW5zIHRoYXQgcm93IDUxIGhhcyB0aGUgc2FtZSB5LXZhbHVlIGFzIHRoZSBmaXJzdCByb3cgYW5kIHRoZXJlZm9yZSBhIHN0cmFpZ2h0IGhvcml6b250YWwgbGluZSBjYW4gYmUgZHJhd24gLSBidXQgb25seSBhZnRlciB0aGUgZGF0YSBhcmUgZ3JvdXBlZCBieSBgY2lfaWRgLiAKCkFuZCBmaW5hbGx5IHdlIGNhbiBjcmVhdGUgdGhpcyBwcmV0dHkgY29tcGxpY2F0ZWQgcGxvdCB1c2luZyB0aGUgZm9sbG93aW5nIHByb2dyYW0gY29kZToKCmBgYHtyIGRyYXdpbmctY29uZmlkZW5jZS1pbnRlcnZhbC1wbG90fQpxcGxvdChkYXRhID0gY2lfZGF0YSwgeCA9IGNpX2JvdW5kcywgeSA9IGNpX2lkLCAKICAgICAgZ3JvdXAgPSBjaV9pZCwgY29sb3IgPSBjYXB0dXJlX211KSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKyAgIyBhZGQgcG9pbnRzIGF0IHRoZSBlbmRzLCBzaXplID0gMgogIGdlb21fbGluZSgpICsgICAgICAgICAgICMgY29ubmVjdCB3aXRoIGxpbmVzCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gcGFyYW1zJG11LCBjb2xvciA9ICJkYXJrZ3JheSIpICMgZHJhdyB2ZXJ0aWNhbCBsaW5lCgpgYGAKYGBge3IgZHJhd2luZy1jb25maWRlbmNlLWludGVydmFsLXBsb3Qtb2xkZXItYXBwcm9hY2h9CgpjaS5vbGQgPC0gYW1lcyAlPiUKICAgICAgICByZXBfc2FtcGxlX24oc2l6ZSA9IG4sIHJlcHMgPSA1MCwgcmVwbGFjZSA9IFRSVUUpICU+JQogICAgICAgIHN1bW1hcmlzZSh4X2JhciA9IG1lYW4oR3IuTGl2LkFyZWEpLCAKICAgICAgICAgICAgICAgICAgc2UgPSBzZChHci5MaXYuQXJlYSkgLyBzcXJ0KG4pLAogICAgICAgICAgICAgICAgICBtZSA9IHpfc3Rhcl85NSAqIHNlLAogICAgICAgICAgICAgICAgICBsb3dlciA9IHhfYmFyIC0gbWUsCiAgICAgICAgICAgICAgICAgIHVwcGVyID0geF9iYXIgKyBtZSkKCmNpLm9sZCA8LSBjaS5vbGQgJT4lCiAgbXV0YXRlKGNhcHR1cmVfbXUub2xkID0gaWZlbHNlKGxvd2VyIDwgcGFyYW1zJG11ICYgdXBwZXIgPiBwYXJhbXMkbXUsICJ5ZXMiLCAibm8iKSkKCnFwbG90KGRhdGEgPSBjaS5vbGQsIHggPSByZXBsaWNhdGUsIHkgPSB4X2JhciwgY29sb3IgPSBjaS5vbGQkY2FwdHVyZV9tdS5vbGQpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbG93ZXIsIHltYXggPSB1cHBlcikpICsgCiAgZ2VvbV9obGluZShkYXRhID0gcGFyYW1zLCBhZXMoeWludGVyY2VwdCA9IG11KSwgY29sb3IgPSAiZGFya2dyYXkiKSArICMgZHJhdyB2ZXJ0aWNhbCBsaW5lCiAgY29vcmRfZmxpcCgpCmBgYAoKIyMgRXhlcmNpc2UgNzoKV2hhdCBwcm9wb3J0aW9uIG9mIHlvdXIgY29uZmlkZW5jZSBpbnRlcnZhbHMgaW5jbHVkZSB0aGUgdHJ1ZSBwb3B1bGF0aW9uIG1lYW4/IElzIHRoaXMgcHJvcG9ydGlvbiBleGFjdGx5IGVxdWFsIHRvIHRoZSBjb25maWRlbmNlIGxldmVsPyBJZiBub3QsIGV4cGxhaW4gd2h5LiBNYWtlIHN1cmUgdG8gaW5jbHVkZSB5b3VyIHBsb3QgaW4geW91ciBhbnN3ZXIuCgpJbiB0aGUgYWJvdmUgZ3JhcGhzIEkgY2FuIGRldGVjdCAzIHJlc3BlY2l0dmx5IDIgY2FzZXMgb3V0IG9mIDUwIHdoZXJlIHRoZSBzYW1wbGUgbWVhbiBpcyBub3Qgd2l0aGluIHRoZSBjb25maWRlbmNlIGludGVydmFsLiBUaGlzIGlzIGFjY29yZGluZyB0byB0aGUgOTUlIGxldmVsLgoKIyBNb3JlIFByYWN0aWNlCgpUaGUgbmV4dCBleGVyY2lzZXMgaGF2ZSBzdGlsbCB0byBiZSBkb25lLgoKIyMgRXhlcmNpc2UgODoKUGljayBhIGNvbmZpZGVuY2UgbGV2ZWwgb2YgeW91ciBjaG9vc2luZywgcHJvdmlkZWQgaXQgaXMgbm90IDk1JS4gV2hhdCBpcyB0aGUgYXBwcm9wcmlhdGUgY3JpdGljYWwgdmFsdWU/CgpJIHdpbGwgY2hvb3NlIGEgOTAlIGNvbmZpZGVuY2UgbGV2ZWwuIGUuZy4gaXQgcmVmZXJzIHRvIHRoZSBtaWRkbGUgOTAlIG9mIHRoZSBkaXN0cmlidXRpb24sIGFuZCB0aGUgY3JpdGljYWwgdmFsdWUgYXNzb2NpYXRlZCB3aXRoIHRoaXMgYXJlYSB3aWxsIGNvcnJlc3BvbmQgdG8gdGhlIDk1dGggcGVyY2VudGlsZS4KCiMjIEV4ZXJjaXNlIDk6CkNhbGN1bGF0ZSA1MCBjb25maWRlbmNlIGludGVydmFscyBhdCB0aGUgY29uZmlkZW5jZSBsZXZlbCB5b3UgY2hvc2UgaW4gdGhlIHByZXZpb3VzIHF1ZXN0aW9uLCBhbmQgcGxvdCBhbGwgaW50ZXJ2YWxzIG9uIG9uZSBwbG90LCBhbmQgY2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIGludGVydmFscyB0aGF0IGluY2x1ZGUgdGhlIHRydWUgcG9wdWxhdGlvbiBtZWFuLiBIb3cgZG9lcyB0aGlzIHBlcmNlbnRhZ2UgY29tcGFyZSB0byB0aGUgY29uZmlkZW5jZSBsZXZlbCBzZWxlY3RlZCBmb3IgdGhlIGludGVydmFscz8gTWFrZSBzdXJlIHRvIGluY2x1ZGUgeW91ciBwbG90IGluIHlvdXIgYW5zd2VyLgoKYGBge3J9CgpuIDwtIDYwCnNldC5zZWVkKDExMTExKSAjIGZvciBlYWNoIHNhbXBsZSBkcmF3biBvbmUgaGFzIHRvIHVzZSB0aGUgYHNldC5zZWVkYCBmdW5jdGlvbgpzYW1wLm5ldy5kZiA8LSBzYW1wbGVfbihhbWVzLCBuKSAjIHJldHVybnMgYSBkYXRhIGZyYW1lIHdoaWNoIGhhcyB0byBiZSB1c2VkIGZvciBnZ3Bsb3QKCgp6X3N0YXJfOTAgPC0gcW5vcm0oMC45NSkKCnNhbXAubmV3LmRmICU+JQogIHN1bW1hcmlzZShsb3dlci45MCA9IG1lYW4oc2FtcC5uZXcpIC0gel9zdGFyXzkwICogKHNkKHNhbXAubmV3KSAvIHNxcnQobikpLAogICAgICAgICAgICB1cHBlci45MCA9IG1lYW4oc2FtcC5uZXcpICsgel9zdGFyXzkwICogKHNkKHNhbXAubmV3KSAvIHNxcnQobikpKQoKY2kuOTAgPC0gYW1lcyAlPiUKICAgICAgICByZXBfc2FtcGxlX24oc2l6ZSA9IG4sIHJlcHMgPSA1MCwgcmVwbGFjZSA9IFRSVUUpICU+JQogICAgICAgIHN1bW1hcmlzZShsb3dlci45MCA9IG1lYW4oR3IuTGl2LkFyZWEpIC0gel9zdGFyXzkwICogKHNkKEdyLkxpdi5BcmVhKSAvIHNxcnQobikpLAogICAgICAgICAgICAgICAgICB1cHBlci45MCA9IG1lYW4oR3IuTGl2LkFyZWEpICsgel9zdGFyXzkwICogKHNkKEdyLkxpdi5BcmVhKSAvIHNxcnQobikpKQoKY2kuOTAgPC0gY2kuOTAgJT4lCiAgbXV0YXRlKGNhcHR1cmVfbXUuOTAgPSBpZmVsc2UobG93ZXIuOTAgPCBwYXJhbXMkbXUgJiB1cHBlci45MCA+IHBhcmFtcyRtdSwgInllcyIsICJubyIpKQoKCmNpX2RhdGEuOTAgPC0gZGF0YS5mcmFtZShjaV9pZCA9IGMoMTo1MCwgMTo1MCksCiAgICAgICAgICAgICAgICAgICAgICBjaV9ib3VuZHMuOTAgPSBjKGNpLjkwJGxvd2VyLjkwLCBjaS45MCR1cHBlci45MCksCiAgICAgICAgICAgICAgICAgICAgICBjYXB0dXJlX211LjkwID0gYyhjaS45MCRjYXB0dXJlX211LjkwLCBjaS45MCRjYXB0dXJlX211LjkwKSkKCnFwbG90KGRhdGEgPSBjaV9kYXRhLjkwLCB4ID0gY2lfYm91bmRzLjkwLCB5ID0gY2lfaWQsCiAgICAgIGdyb3VwID0gY2lfaWQsIGNvbG9yID0gY2FwdHVyZV9tdS45MCkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsgICMgYWRkIHBvaW50cyBhdCB0aGUgZW5kcywgc2l6ZSA9IDIKICBnZW9tX2xpbmUoKSArICAgICAgICAgICAjIGNvbm5lY3Qgd2l0aCBsaW5lcwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHBhcmFtcyRtdSwgY29sb3IgPSAiZGFya2dyYXkiKSAjIGRyYXcgdmVydGljYWwgbGluZQoKYGBgCgpgYGB7cn0KYnkuY2kgPC0gY2kuOTAgJT4lIGdyb3VwX2J5KGNhcHR1cmVfbXUuOTApCmhvdy5tYW55IDwtIGJ5LmNpICU+JSBjb3VudChjYXB0dXJlX211LjkwKSAlPiUgbXV0YXRlKHByb3AgPSBuIC8gNTAgKiAxMDApCmhvdy5tYW55CmBgYAoKVGhlIHByb3BvcnRpb24gb2YgbWVhbnMgb3V0c2lkZSBvZiB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCBpcyBgciBob3cubWFueSRwcm9wWzFdYCUgd2hpY2ggaXMgYHIgaWZlbHNlKGhvdy5tYW55JHByb3BbMV0gPD0gMTAsICJleHBlY3RlZCIsICJoaWdoZXIgdGhhbiBleHBlY3RlZCIpYC4gCgojIFJlZmVyZW5jZXMKCgo=