This document is an extension and explanation of the R exercises in the book Genomics with R for Dr. Wiggins’ Introductory Genomics course at Oklahoma State University.
The Genomics with R book contains critical information, explains the statistics used, and provides the exercises.
This document is intended as a companion to the Genomics with R book for students who are R beginners.
These instructions are for RStudio. In this course we are running RStudio through the Cyverse platform
This section begins with 3.1
Here I use the package::function() structure.
This provides you with information about which package each function comes from. It makes code much more understandable and repeatable.
runif()
First we assign the variable ‘x’ to the output of runif(10)
Anytime you want to know more about a function use the ‘?’ before the name of the function as below:
?runif
In the panel to the right -> you will see information about this function appear.
Reading Documentation
Each function help page follows the same format.
They start with a brief DESCRIPTION of what the package does.
USAGE shows the various arguments you need to specify when using the function - some will be compulsory, some will be optional.
ARGUMENTS - very briefly describes each of the arguments that you need to specify.
DETAILS - very variable - the author of the function often gives more details about the function.
VALUE - this tells you what the outputs of the function will be.
AUTHORS, REFERENCES - self explanatory
SEE ALSO - points you in the direction of related functions
EXAMPLES - gives you some code snippets to show you the function being used in practice. I find these to be the most helpful and often scroll here first
Some packages will also have “vignettes”, which give more detailed descriptions of how the functions within a package should be used. ________
This documentation takes some getting used to. It is often helpful to google ‘tutorials function_name’ if you are struggling. There will be a lot of examples of how to use it. Play with the function and see what happens. This will be your best teacher.
In the runif() documentation under usage:
runif(n, min = 0, max = 1). n = how many random numbers you want. min = 0: the default lowest number is 0. max = 1: the default highest number is 1.
This is useful for simulating data.
The stats package comes standard with R distributions so we do not need to install it or go to the library to “get” it.
So if we run:
x = stats::runif(10)
We are telling the function to create 10 random numbers between 0 and 1 and to assign those numbers (called a vector) to the variable x.
base indicates this is a base R function, not part of any package
# calculate mean
base::mean(x)
#calculate median
stats::median(x)
3.1.2 Describing the spread.
rnorm()
Now we will randomly generate a vector of numbers using rnorm()
?rnorm
rnorm() creates a vector of numbers with a normal distribution (see 3.1.2.1)
Once again the documentation to the right -> provides information about the arguments the function will take. n = how many numbers do we want in our distribution. mean = what do we want the mean of our distribution to be?
sd = what doe we want the standard distribution to be?
#assign a vector 20 of numbers that are a normal distribution with a mean of 6 and a standard deviation of 0.7
x = stats::rnorm(20, mean = 6, sd = 0.7)
#note the following are exactly the same command
#remove white space (entirely preference, I find white space makes code more readable)
#x=rnorm(20,mean=6,sd=0.7)
#mean and sd indicated by their positions in the function, no names needed
#x = rnorm(20, 6, 0.7)
#calculate and output the variance of 'x'
stats::var(x)
#calculate and output the standard deviation of 'x'
stats::sd(x)
To evaluate the influence of outliers in our data:
#calcuate the interquartile range
stats::IQR(x)
#calculate the quantiles of the data
stats::quantile(x)
These data are very useful but can be difficult to understand without a visual. To visualize the data we can create a boxplot. graphics is a default package distribution with R so no need to install it and go get it from the library.
Note that many R users shorten TRUE to T and FALSE to F. While this is common practice it reduces code readability and potentially produces unintended results. What if a variable has been assigned to ‘T’ or ‘F’?
#plot the vector 'x' as a boxplot horizontally rather than vertically (vertical is the default)
graphics::boxplot(x, horizontal = TRUE)
Try the same plot without horizontal = TRUE. What happens?
Look back at or rerun stats::quantile. How do these numbers map to the box plot?
Calculate the mean and median for x to determine what the solid line on the plot represents.
Now we will be using a package that doesn’t come standard with R.
We need to install the package called mosaic.
install.packages("mosaic")
Then we go to our library where packages are stored to ‘checkout’ the package. I think of it like going to the library to check out a book.
library(mosaic)
set.seed()
We use a function called set.seed() to make our analysis repeatable. We will use randomly generated numbers for the analysis. When we ask R to ‘go get’ a set of randomly generated numbers it needs to know where to start in its list of random numbers. The starting point is the ‘seed.’ If we don’t set the seed then R uses the session starting time (it’s clock) to determine where to start in the list. This makes the analysis impossible to repeat. Therefore, we set a seed.
#set the seed to 21
base::set.seed(21)
#simulate a sample with 50 numbers, a mean of 20, and a standard deviation of 5. Assign it to the variable 'sample1'
sample1 = stats::rnorm(50, 20, 5)
Now bootstrap resample (bootstraping): resample with replacement to estimate the precision of population parameter estimates.
# do bootstrap resampling, sampling with replacement. Assign the results to the variable boot.means
boot.means = mosaic::do(1000) * mosaic::mean(mosaic::resample(sample1))
The above will create a data frame in your environment that you can see on the top right ->
Note that there are 1000 observations, this is because we told the do() function to repeat 1000 times.
Get percentiles and plot.
boot.means[,1] tells the function to calculate using all of the values in the first column. The synatax is [row,column].
p = is setting our percentiles to 2.5% and 97.5% (outside of these percentiles is outside of 95%)
to learn about c use ?c
q = mosaic::quantile(boot.means[,1], p = base::c(0.025, 0.975))
Plot the histogram. ?hist to view the arguments
graphics::hist(boot.means[,1], col = "cornflowerblue", border = "white", xlab = "sample means")
graphics::abline(v = c(q[1], q[2]), col = "red")
graphics::text(x = q[1], y = 200, round(q[1],3), adj = base::c(1,0))
graphics::text(x = q[2], y = 200, round(q[2],3), adj = base::c(0,0))
Using qnorm to calculate Z-scores
alpha = 0.05
sd = 5
n = 50
base::mean(sample1) + stats::qnorm(c(alpha/2, 1-alpha/2)) * sd/sqrt(n)
for explanations of set.seed() and rnorm() see above (hint: ctrl + F will bring up a search bar to search the document for these functions)
Below we are creating variables called ‘gene1’, ‘gene2’, ‘org.diff’, and ‘gene.df’.
‘gene1’ & ‘gene2’ are representing data collected under different conditions.
to learn about data.frame and rep use ?data.frame and ?rep
#set the seed to 100
base::set.seed(100)
#simulate gene expression measurements obtained under different conditions
gene1 = stats::rnorm(30, mean=4, sd=2)
gene2 = stats::rnorm(30, mean=2, sd=2)
#calculate differences in means
org.diff = base::mean(gene1) - base::mean(gene2)
#create a data frame with values in column 1 and the labels 'test' and 'control' in column 2 corresponding to gene1 (the gene we are calling 'test') and gene2 (the gene we are calling 'control')
gene.df = base::data.frame(exp = base::c(gene1, gene2),
group = c(base::rep("test", 30), base::rep("control", 30)))
to produce exp.null we use the gene.df data frame but ‘shuffle’ which values are paired with which group and replicates 1000 times.
#
exp.null <- mosaic::do(1000) * base::diff(mosaic::mean(exp ~ mosaic::shuffle(group), data = gene.df))
Create a histogram of the randomly distributed values (if the null hypothesis is correct) using the first column of the data frame ‘exp.null’ (where the values are stored).
use ?hist to explore the arguments below
hist(exp.null[,1], xlab="null distribution | no difference in samples", main = expression(paste(H[0], ":no difference in means")), xlim = c(-2, 2), col="cornflowerblue", border="white")
#add a red line to indicate the location of the 95% percentile
graphics::abline(v = mosaic::quantile(exp.null[, 1], 0.95), col = "red" )
#add a blue line to indicate the original difference in means between sample groups
graphics::abline(v = org.diff, col = "blue" )
#add a red label '0.05' to the red line at the level of 200 on the y-axis (corresponds to a p-value of 0.05)
graphics::text(x = mosaic::quantile(exp.null[, 1], 0.95), y = 200,"0.05", adj = c(1, 0), col="red")
#add a blue label 'org. diff.' to the blue line at the level of 200 on the y-axis
graphics::text(x = org.diff, y = 200,"org. diff.", adj = c(1, 0), col = "blue")
Calculate the p-value of the difference between the randomly produced null data set and the original data set.
p.val = base::sum(exp.null[, 1] > org.diff) / base::length(exp.null[, 1])
p.val
No R explanations required in this section
Below we will use a new package, qvalue. This package is a bioconductor package so we install it differently.
if (!require("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("Biobase")
In the console below, you may be asked: Update all/some/none? [a/s/n]:
type ‘a’, then ‘enter’
Now install the package ‘qvalue’
BiocManager::install("qvalue")
And go to the library to get it for use in this session
library(qvalue)
Load the data from a breast cancer gene expression study, stored in the package qvalue, for use
References for these data: Hedenfalk I et al. (2001). Gene expression profiles in hereditary breast cancer. New England Journal of Medicine, 344: 539-548. Storey JD and Tibshirani R. (2003). Statistical significance for genome-wide studies. Proceedings of the National Academy of Sciences, 100: 9440-9445. http://www.pnas.org/content/100/16/9440.full
The hedenfalk data set was imported with the qvalue package. We load it into our environment with the data command. To learn more about any of these ?what_you_want_to_learn_about in the console.
data(hedenfalk)
use the qvalue function to calculate the qvalues from the vecor of pvalues p provided in the dataset hedenfalk then extract only qvalues from the list that the qvalue function produces.
The book instructions look like this qvalues <- qvalue(hedenfalk$p)$q The $ usually means we are subsetting something.
On the right -> under the Environment tab/Data click on the dataset hedenfalk. There are three parts to this list ‘p’ ‘stat’ ‘stat0’. We are only interested in ‘p’.
The way R stores these data are with the $. To tell R we only want the information in ‘p’ we use: hedenfalk$p
The hedenfalk dataset doesn’t have a ‘q’. Where does the $q come from?
Tye qvalue(hedenfalk$p) into the console. Scroll through the output.
Now you are seeing these data how R ‘sees’ it. You can see that there is no $q. There is $qvalue.
Play with what results you get from the following commands in the console:
qvalue(p = hedenfalk$p)$p qvalue(p = hedenfalk$p)$l qvalue(p = hedenfalk$p)$la qvalue(p = hedenfalk$p)$q
Why do some return data but others return ‘null’?
How does the data returned by qvalue(p = hedenfalk$p)$q compare to qvalues produced below?
qvalues <- qvalue(hedenfalk$p)$q
The book likely used ‘q’ to avoid confusion with the command qvalue or the new object the instructions ask you to created in the code block above. Ultimately, I find this confusing because it makes figuring out what each component of a command do and where they come from. I would advise instead something like:
qresult <- qvalue(hedenfalk$p)$qvalue
Now we test the correction methods
Note that the book uses the = assignment operator here but the <- assignment operator elsewhere. I find this confusing so have converted all to <- (read more about operators here)
bonf.pval <- p.adjust(hedenfalk$p,method ="bonferroni")
fdr.adj.pval <- p.adjust(hedenfalk$p,method ="fdr")
plot(hedenfalk$p, qresult, pch=19, ylim=c(0,1), xlab="raw P-values", ylab="adjusted P-values")
points(hedenfalk$p, bonf.pval, pch=19, col="red")
points(hedenfalk$p, fdr.adj.pval, pch=19, col="blue")
legend("bottomright", legend=c("q-value","FDR (BH)" ,"Bonferroni"), fill=c("black","blue","red"))
LS0tCnRpdGxlOiAiR2Vub21pY3Mgd2l0aCBSIDMuMSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpUaGlzIGRvY3VtZW50IGlzIGFuIGV4dGVuc2lvbiBhbmQgZXhwbGFuYXRpb24gb2YgdGhlICpSIGV4ZXJjaXNlcyogaW4gdGhlIGJvb2sgW0dlbm9taWNzIHdpdGggUl0oaHR0cHM6Ly9jb21wZ2Vub21yLmdpdGh1Yi5pby9ib29rLykgZm9yIERyLiBXaWdnaW5zJyBJbnRyb2R1Y3RvcnkgR2Vub21pY3MgY291cnNlIGF0IE9rbGFob21hIFN0YXRlIFVuaXZlcnNpdHkuCgpUaGUgW0dlbm9taWNzIHdpdGggUl0oaHR0cHM6Ly9jb21wZ2Vub21yLmdpdGh1Yi5pby9ib29rLykgYm9vayBjb250YWlucyBjcml0aWNhbCBpbmZvcm1hdGlvbiwgZXhwbGFpbnMgdGhlIHN0YXRpc3RpY3MgdXNlZCwgYW5kIHByb3ZpZGVzIHRoZSBleGVyY2lzZXMuCgpUaGlzIGRvY3VtZW50IGlzIGludGVuZGVkIGFzIGEgY29tcGFuaW9uIHRvIHRoZSBbR2Vub21pY3Mgd2l0aCBSXShodHRwczovL2NvbXBnZW5vbXIuZ2l0aHViLmlvL2Jvb2svKSBib29rIGZvciBzdHVkZW50cyB3aG8gYXJlIFIgYmVnaW5uZXJzLiAKClRoZXNlIGluc3RydWN0aW9ucyBhcmUgZm9yIFJTdHVkaW8uIApJbiB0aGlzIGNvdXJzZSB3ZSBhcmUgcnVubmluZyBSU3R1ZGlvIHRocm91Z2ggdGhlIEN5dmVyc2UgcGxhdGZvcm0KClRoaXMgc2VjdGlvbiBiZWdpbnMgd2l0aCBbMy4xXShodHRwczovL2NvbXBnZW5vbXIuZ2l0aHViLmlvL2Jvb2svaG93LXRvLXN1bW1hcml6ZS1jb2xsZWN0aW9uLW9mLWRhdGEtcG9pbnRzLXRoZS1pZGVhLWJlaGluZC1zdGF0aXN0aWNhbC1kaXN0cmlidXRpb25zLmh0bWwpCgojIyMjIEhlcmUgSSB1c2UgdGhlIGBwYWNrYWdlOjpmdW5jdGlvbigpYCBzdHJ1Y3R1cmUuICAKVGhpcyBwcm92aWRlcyB5b3Ugd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB3aGljaCBwYWNrYWdlIGVhY2ggZnVuY3Rpb24gY29tZXMgZnJvbS4gSXQgbWFrZXMgY29kZSBtdWNoIG1vcmUgdW5kZXJzdGFuZGFibGUgYW5kIHJlcGVhdGFibGUuCgojIyMgWzMuMS4xOiBEZXNjcmliaW5nIHRoZSBjZW50cmFsIHRlbmRlbmN5XShodHRwczovL2NvbXBnZW5vbXIuZ2l0aHViLmlvL2Jvb2svaG93LXRvLXN1bW1hcml6ZS1jb2xsZWN0aW9uLW9mLWRhdGEtcG9pbnRzLXRoZS1pZGVhLWJlaGluZC1zdGF0aXN0aWNhbC1kaXN0cmlidXRpb25zLmh0bWwjZGVzY3JpYmluZy10aGUtY2VudHJhbC10ZW5kZW5jeS1tZWFuLWFuZC1tZWRpYW4pCl9fX19fX19fX19fX19fX19fX19fXwojIyMgcnVuaWYoKQpGaXJzdCB3ZSBhc3NpZ24gdGhlIHZhcmlhYmxlICd4JyB0byB0aGUgb3V0cHV0IG9mIHJ1bmlmKDEwKQoKQW55dGltZSB5b3Ugd2FudCB0byBrbm93IG1vcmUgYWJvdXQgYSBmdW5jdGlvbiB1c2UgdGhlICc/JyBiZWZvcmUgdGhlIG5hbWUgb2YgdGhlIGZ1bmN0aW9uIGFzIGJlbG93OgpgYGB7cn0KP3J1bmlmCmBgYAoKSW4gdGhlIHBhbmVsIHRvIHRoZSByaWdodCAtPiB5b3Ugd2lsbCBzZWUgaW5mb3JtYXRpb24gYWJvdXQgdGhpcyBmdW5jdGlvbiBhcHBlYXIuCgpfX19fX19fX19fX18KIyMjIyBSZWFkaW5nIERvY3VtZW50YXRpb24KRWFjaCBmdW5jdGlvbiBoZWxwIHBhZ2UgZm9sbG93cyB0aGUgc2FtZSBmb3JtYXQuIAoKVGhleSBzdGFydCB3aXRoIGEgYnJpZWYgREVTQ1JJUFRJT04gb2Ygd2hhdCB0aGUgcGFja2FnZSBkb2VzLgoKVVNBR0Ugc2hvd3MgdGhlIHZhcmlvdXMgYXJndW1lbnRzIHlvdSBuZWVkIHRvIHNwZWNpZnkgd2hlbiB1c2luZyB0aGUgZnVuY3Rpb24gLSBzb21lIHdpbGwgYmUgY29tcHVsc29yeSwgc29tZSB3aWxsIGJlIG9wdGlvbmFsLgoKQVJHVU1FTlRTIC0gdmVyeSBicmllZmx5IGRlc2NyaWJlcyBlYWNoIG9mIHRoZSBhcmd1bWVudHMgdGhhdCB5b3UgbmVlZCB0byBzcGVjaWZ5LgoKREVUQUlMUyAtIHZlcnkgdmFyaWFibGUgIC0gdGhlIGF1dGhvciBvZiB0aGUgZnVuY3Rpb24gb2Z0ZW4gZ2l2ZXMgbW9yZSBkZXRhaWxzIGFib3V0IHRoZSBmdW5jdGlvbi4KClZBTFVFIC0gdGhpcyB0ZWxscyB5b3Ugd2hhdCB0aGUgb3V0cHV0cyBvZiB0aGUgZnVuY3Rpb24gd2lsbCBiZS4KCkFVVEhPUlMsIFJFRkVSRU5DRVMgLSBzZWxmIGV4cGxhbmF0b3J5CgpTRUUgQUxTTyAtIHBvaW50cyB5b3UgaW4gdGhlIGRpcmVjdGlvbiBvZiByZWxhdGVkIGZ1bmN0aW9ucwoKRVhBTVBMRVMgLSBnaXZlcyB5b3Ugc29tZSBjb2RlIHNuaXBwZXRzIHRvIHNob3cgeW91IHRoZSBmdW5jdGlvbiBiZWluZyB1c2VkIGluIHByYWN0aWNlLiAqSSBmaW5kIHRoZXNlIHRvIGJlIHRoZSBtb3N0IGhlbHBmdWwgYW5kIG9mdGVuIHNjcm9sbCBoZXJlIGZpcnN0KgoKU29tZSBwYWNrYWdlcyB3aWxsIGFsc28gaGF2ZSAidmlnbmV0dGVzIiwgd2hpY2ggZ2l2ZSBtb3JlIGRldGFpbGVkIGRlc2NyaXB0aW9ucyBvZiBob3cgdGhlIGZ1bmN0aW9ucyB3aXRoaW4gYSBwYWNrYWdlIHNob3VsZCBiZSB1c2VkLgpfX19fX19fXwoKVGhpcyBkb2N1bWVudGF0aW9uIHRha2VzIHNvbWUgZ2V0dGluZyB1c2VkIHRvLiBJdCBpcyBvZnRlbiBoZWxwZnVsIHRvIGdvb2dsZSAndHV0b3JpYWxzIGZ1bmN0aW9uX25hbWUnIGlmIHlvdSBhcmUgc3RydWdnbGluZy4gVGhlcmUgd2lsbCBiZSBhIGxvdCBvZiBleGFtcGxlcyBvZiBob3cgdG8gdXNlIGl0LiBQbGF5IHdpdGggdGhlIGZ1bmN0aW9uIGFuZCBzZWUgd2hhdCBoYXBwZW5zLiBUaGlzIHdpbGwgYmUgeW91ciBiZXN0IHRlYWNoZXIuIAoKX19fX19fX18KCkluIHRoZSBgcnVuaWYoKWAgZG9jdW1lbnRhdGlvbiB1bmRlciB1c2FnZTogIApgcnVuaWYobiwgbWluID0gMCwgbWF4ID0gMSlgLiAKbiA9IGhvdyBtYW55IHJhbmRvbSBudW1iZXJzIHlvdSB3YW50LiAKbWluID0gMDogdGhlIGRlZmF1bHQgbG93ZXN0IG51bWJlciBpcyAwLiAKbWF4ID0gMTogdGhlIGRlZmF1bHQgaGlnaGVzdCBudW1iZXIgaXMgMS4gCgpUaGlzIGlzIHVzZWZ1bCBmb3Igc2ltdWxhdGluZyBkYXRhLiAKCiAKClRoZSBgc3RhdHNgIHBhY2thZ2UgY29tZXMgc3RhbmRhcmQgd2l0aCBSIGRpc3RyaWJ1dGlvbnMgc28gd2UgZG8gbm90IG5lZWQgdG8gaW5zdGFsbCBpdCBvciBnbyB0byB0aGUgbGlicmFyeSB0byAiZ2V0IiBpdC4gCgpTbyBpZiB3ZSBydW46CmBgYHtyfQp4ID0gc3RhdHM6OnJ1bmlmKDEwKQpgYGAKV2UgYXJlIHRlbGxpbmcgdGhlIGZ1bmN0aW9uIHRvIGNyZWF0ZSAxMCByYW5kb20gbnVtYmVycyBiZXR3ZWVuIDAgYW5kIDEgYW5kIHRvIGFzc2lnbiB0aG9zZSBudW1iZXJzIChjYWxsZWQgYSB2ZWN0b3IpIHRvIHRoZSB2YXJpYWJsZSBgeGAuCgpgYmFzZWAgaW5kaWNhdGVzIHRoaXMgaXMgYSBiYXNlIFIgZnVuY3Rpb24sIG5vdCBwYXJ0IG9mIGFueSBwYWNrYWdlCmBgYHtyfQojIGNhbGN1bGF0ZSBtZWFuCmJhc2U6Om1lYW4oeCkKCiNjYWxjdWxhdGUgbWVkaWFuCnN0YXRzOjptZWRpYW4oeCkKYGBgCl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fXwojIyMgMy4xLjIgRGVzY3JpYmluZyB0aGUgc3ByZWFkLiAKCiMjIyBybm9ybSgpCgpOb3cgd2Ugd2lsbCByYW5kb21seSBnZW5lcmF0ZSBhIHZlY3RvciBvZiBudW1iZXJzIHVzaW5nIGBybm9ybSgpYApgYGB7cn0KP3Jub3JtCmBgYAoKYHJub3JtKClgIGNyZWF0ZXMgYSB2ZWN0b3Igb2YgbnVtYmVycyB3aXRoIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiAoc2VlIFszLjEuMi4xXShodHRwczovL3Rpbnl1cmwuY29tLzRjeHh3eWFwKSkKCk9uY2UgYWdhaW4gdGhlIGRvY3VtZW50YXRpb24gdG8gdGhlIHJpZ2h0IC0+IHByb3ZpZGVzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBhcmd1bWVudHMgdGhlIGZ1bmN0aW9uIHdpbGwgdGFrZS4gCm4gPSBob3cgbWFueSBudW1iZXJzIGRvIHdlIHdhbnQgaW4gb3VyIGRpc3RyaWJ1dGlvbi4gCm1lYW4gPSB3aGF0IGRvIHdlIHdhbnQgdGhlIG1lYW4gb2Ygb3VyIGRpc3RyaWJ1dGlvbiB0byBiZT8gIApzZCA9IHdoYXQgZG9lIHdlIHdhbnQgdGhlIHN0YW5kYXJkIGRpc3RyaWJ1dGlvbiB0byBiZT8gIAoKYGBge3J9CiNhc3NpZ24gYSB2ZWN0b3IgMjAgb2YgbnVtYmVycyB0aGF0IGFyZSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBhIG1lYW4gb2YgNiBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMC43CnggPSBzdGF0czo6cm5vcm0oMjAsIG1lYW4gPSA2LCBzZCA9IDAuNykKCiNub3RlIHRoZSBmb2xsb3dpbmcgYXJlIGV4YWN0bHkgdGhlIHNhbWUgY29tbWFuZAojcmVtb3ZlIHdoaXRlIHNwYWNlIChlbnRpcmVseSBwcmVmZXJlbmNlLCBJIGZpbmQgd2hpdGUgc3BhY2UgbWFrZXMgY29kZSBtb3JlIHJlYWRhYmxlKQojeD1ybm9ybSgyMCxtZWFuPTYsc2Q9MC43KQoKI21lYW4gYW5kIHNkIGluZGljYXRlZCBieSB0aGVpciBwb3NpdGlvbnMgaW4gdGhlIGZ1bmN0aW9uLCBubyBuYW1lcyBuZWVkZWQKI3ggPSBybm9ybSgyMCwgNiwgMC43KQoKI2NhbGN1bGF0ZSBhbmQgb3V0cHV0IHRoZSB2YXJpYW5jZSBvZiAneCcKc3RhdHM6OnZhcih4KQoKI2NhbGN1bGF0ZSBhbmQgb3V0cHV0IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgJ3gnCnN0YXRzOjpzZCh4KQpgYGAKClRvIGV2YWx1YXRlIHRoZSBpbmZsdWVuY2Ugb2YgW291dGxpZXJzXShodHRwczovL3Rpbnl1cmwuY29tL21yeDgyc3pmKSBpbiBvdXIgZGF0YToKYGBge3J9CiNjYWxjdWF0ZSB0aGUgaW50ZXJxdWFydGlsZSByYW5nZQpzdGF0czo6SVFSKHgpCgojY2FsY3VsYXRlIHRoZSBxdWFudGlsZXMgb2YgdGhlIGRhdGEKc3RhdHM6OnF1YW50aWxlKHgpCmBgYAoKVGhlc2UgZGF0YSBhcmUgdmVyeSB1c2VmdWwgYnV0IGNhbiBiZSBkaWZmaWN1bHQgdG8gdW5kZXJzdGFuZCB3aXRob3V0IGEgdmlzdWFsLiBUbyB2aXN1YWxpemUgdGhlIGRhdGEgd2UgY2FuIGNyZWF0ZSBhIGJveHBsb3QuIGBncmFwaGljc2AgaXMgYSBkZWZhdWx0IHBhY2thZ2UgZGlzdHJpYnV0aW9uIHdpdGggUiBzbyBubyBuZWVkIHRvIGluc3RhbGwgaXQgYW5kIGdvIGdldCBpdCBmcm9tIHRoZSBsaWJyYXJ5LiAKCk5vdGUgdGhhdCBtYW55IFIgdXNlcnMgc2hvcnRlbiBUUlVFIHRvIFQgYW5kIEZBTFNFIHRvIEYuIFdoaWxlIHRoaXMgaXMgY29tbW9uIHByYWN0aWNlIGl0IHJlZHVjZXMgY29kZSByZWFkYWJpbGl0eSBhbmQgcG90ZW50aWFsbHkgcHJvZHVjZXMgdW5pbnRlbmRlZCByZXN1bHRzLiBXaGF0IGlmIGEgdmFyaWFibGUgaGFzIGJlZW4gYXNzaWduZWQgdG8gJ1QnIG9yICdGJz8KYGBge3J9CiNwbG90IHRoZSB2ZWN0b3IgJ3gnIGFzIGEgYm94cGxvdCBob3Jpem9udGFsbHkgcmF0aGVyIHRoYW4gdmVydGljYWxseSAodmVydGljYWwgaXMgdGhlIGRlZmF1bHQpCmdyYXBoaWNzOjpib3hwbG90KHgsIGhvcml6b250YWwgPSBUUlVFKQpgYGAKClRyeSB0aGUgc2FtZSBwbG90IHdpdGhvdXQgYGhvcml6b250YWwgPSBUUlVFLmAgV2hhdCBoYXBwZW5zPwoKTG9vayBiYWNrIGF0IG9yIHJlcnVuIGBzdGF0czo6cXVhbnRpbGUuYCBIb3cgZG8gdGhlc2UgbnVtYmVycyBtYXAgdG8gdGhlIGJveCBwbG90PwoKQ2FsY3VsYXRlIHRoZSBtZWFuIGFuZCBtZWRpYW4gZm9yIGB4YCB0byBkZXRlcm1pbmUgd2hhdCB0aGUgc29saWQgbGluZSBvbiB0aGUgcGxvdCByZXByZXNlbnRzLgoKCiMjIyBbMy4xLjIgUHJlY2lzaW9uIG9mIGVzdGltYXRlczogQ29uZmlkZW5jZSBpbnRlcnZhbHNdKGh0dHBzOi8vdGlueXVybC5jb20vMnA5ZjZ0c2opCgpOb3cgd2Ugd2lsbCBiZSB1c2luZyBhIHBhY2thZ2UgdGhhdCBkb2Vzbid0IGNvbWUgc3RhbmRhcmQgd2l0aCBSLgoKV2UgbmVlZCB0byBpbnN0YWxsIHRoZSBwYWNrYWdlIGNhbGxlZCBgbW9zYWljYC4KYGBge3J9Cmluc3RhbGwucGFja2FnZXMoIm1vc2FpYyIpCmBgYAoKVGhlbiB3ZSBnbyB0byBvdXIgbGlicmFyeSB3aGVyZSBwYWNrYWdlcyBhcmUgc3RvcmVkIHRvICdjaGVja291dCcgdGhlIHBhY2thZ2UuIEkgdGhpbmsgb2YgaXQgbGlrZSBnb2luZyB0byB0aGUgbGlicmFyeSB0byBjaGVjayBvdXQgYSBib29rLiAKYGBge3J9CmxpYnJhcnkobW9zYWljKQpgYGAKIyMjIyBzZXQuc2VlZCgpIApXZSB1c2UgYSBmdW5jdGlvbiBjYWxsZWQgYHNldC5zZWVkKClgIHRvIG1ha2Ugb3VyIGFuYWx5c2lzIHJlcGVhdGFibGUuIFdlIHdpbGwgdXNlIHJhbmRvbWx5IGdlbmVyYXRlZCBudW1iZXJzIGZvciB0aGUgYW5hbHlzaXMuIFdoZW4gd2UgYXNrIFIgdG8gJ2dvIGdldCcgYSBzZXQgb2YgcmFuZG9tbHkgZ2VuZXJhdGVkIG51bWJlcnMgaXQgbmVlZHMgdG8ga25vdyB3aGVyZSB0byBzdGFydCBpbiBpdHMgbGlzdCBvZiByYW5kb20gbnVtYmVycy4gVGhlIHN0YXJ0aW5nIHBvaW50IGlzIHRoZSAnc2VlZC4nIElmIHdlIGRvbid0IHNldCB0aGUgc2VlZCB0aGVuIFIgdXNlcyB0aGUgc2Vzc2lvbiBzdGFydGluZyB0aW1lIChpdCdzIGNsb2NrKSB0byBkZXRlcm1pbmUgd2hlcmUgdG8gc3RhcnQgaW4gdGhlIGxpc3QuIFRoaXMgbWFrZXMgdGhlIGFuYWx5c2lzIGltcG9zc2libGUgdG8gcmVwZWF0LiBUaGVyZWZvcmUsIHdlIHNldCBhIHNlZWQuIAoKYGBge3J9CiNzZXQgdGhlIHNlZWQgdG8gMjEKYmFzZTo6c2V0LnNlZWQoMjEpCgojc2ltdWxhdGUgYSBzYW1wbGUgd2l0aCA1MCBudW1iZXJzLCBhIG1lYW4gb2YgMjAsIGFuZCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiA1LiBBc3NpZ24gaXQgdG8gdGhlIHZhcmlhYmxlICdzYW1wbGUxJwpzYW1wbGUxID0gc3RhdHM6OnJub3JtKDUwLCAyMCwgNSkKYGBgCgoKTm93IGJvb3RzdHJhcCByZXNhbXBsZSAoYm9vdHN0cmFwaW5nKTogcmVzYW1wbGUgd2l0aCByZXBsYWNlbWVudCB0byBlc3RpbWF0ZSB0aGUgcHJlY2lzaW9uIG9mIHBvcHVsYXRpb24gcGFyYW1ldGVyIGVzdGltYXRlcy4KCmBgYHtyfQojIGRvIGJvb3RzdHJhcCByZXNhbXBsaW5nLCBzYW1wbGluZyB3aXRoIHJlcGxhY2VtZW50LiBBc3NpZ24gdGhlIHJlc3VsdHMgdG8gdGhlIHZhcmlhYmxlIGJvb3QubWVhbnMKYm9vdC5tZWFucyA9IG1vc2FpYzo6ZG8oMTAwMCkgKiBtb3NhaWM6Om1lYW4obW9zYWljOjpyZXNhbXBsZShzYW1wbGUxKSkKYGBgCgpUaGUgYWJvdmUgd2lsbCBjcmVhdGUgYSBkYXRhIGZyYW1lIGluIHlvdXIgZW52aXJvbm1lbnQgdGhhdCB5b3UgY2FuIHNlZSBvbiB0aGUgdG9wIHJpZ2h0IC0+CgpOb3RlIHRoYXQgdGhlcmUgYXJlIDEwMDAgb2JzZXJ2YXRpb25zLCB0aGlzIGlzIGJlY2F1c2Ugd2UgdG9sZCB0aGUgYGRvKClgIGZ1bmN0aW9uIHRvIHJlcGVhdCAxMDAwIHRpbWVzLiAgICAKR2V0IHBlcmNlbnRpbGVzIGFuZCBwbG90LiAgCgpib290Lm1lYW5zWywxXSB0ZWxscyB0aGUgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHVzaW5nIGFsbCBvZiB0aGUgdmFsdWVzIGluIHRoZSBmaXJzdCBjb2x1bW4uIFRoZSBzeW5hdGF4IGlzIFtyb3csY29sdW1uXS4gIAoKcCA9IGlzIHNldHRpbmcgb3VyIHBlcmNlbnRpbGVzIHRvIDIuNSUgYW5kIDk3LjUlIChvdXRzaWRlIG9mIHRoZXNlIHBlcmNlbnRpbGVzIGlzIG91dHNpZGUgb2YgOTUlKQoKdG8gbGVhcm4gYWJvdXQgYGNgIHVzZSA/YwpgYGB7cn0KcSA9IG1vc2FpYzo6cXVhbnRpbGUoYm9vdC5tZWFuc1ssMV0sIHAgPSBiYXNlOjpjKDAuMDI1LCAwLjk3NSkpCmBgYAoKUGxvdCB0aGUgaGlzdG9ncmFtLiAKP2hpc3QgdG8gdmlldyB0aGUgYXJndW1lbnRzCmBgYHtyfQpncmFwaGljczo6aGlzdChib290Lm1lYW5zWywxXSwgY29sID0gImNvcm5mbG93ZXJibHVlIiwgYm9yZGVyID0gIndoaXRlIiwgeGxhYiA9ICJzYW1wbGUgbWVhbnMiKQpncmFwaGljczo6YWJsaW5lKHYgPSBjKHFbMV0sIHFbMl0pLCBjb2wgPSAicmVkIikKZ3JhcGhpY3M6OnRleHQoeCA9IHFbMV0sIHkgPSAyMDAsIHJvdW5kKHFbMV0sMyksIGFkaiA9IGJhc2U6OmMoMSwwKSkKZ3JhcGhpY3M6OnRleHQoeCA9IHFbMl0sIHkgPSAyMDAsIHJvdW5kKHFbMl0sMyksIGFkaiA9IGJhc2U6OmMoMCwwKSkKYGBgCiMjIyBVc2luZyBgcW5vcm1gIHRvIGNhbGN1bGF0ZSBaLXNjb3JlcwoKYGBge3J9CmFscGhhID0gMC4wNQpzZCA9IDUKbiA9IDUwCmJhc2U6Om1lYW4oc2FtcGxlMSkgKyBzdGF0czo6cW5vcm0oYyhhbHBoYS8yLCAxLWFscGhhLzIpKSAqIHNkL3NxcnQobikKYGBgCgojIyMgWzMuMi4xIFJhbmRvbWl6YXRpb24tYmFzZWQgdGVzdGluZyBmb3IgZGlmZmVyZW5jZSBvZiB0aGUgbWVhbnNdKGh0dHBzOi8vY29tcGdlbm9tci5naXRodWIuaW8vYm9vay9ob3ctdG8tdGVzdC1mb3ItZGlmZmVyZW5jZXMtYmV0d2Vlbi1zYW1wbGVzLmh0bWwjcmFuZG9taXphdGlvbi1iYXNlZC10ZXN0aW5nLWZvci1kaWZmZXJlbmNlLW9mLXRoZS1tZWFucykKCmZvciBleHBsYW5hdGlvbnMgb2YgYHNldC5zZWVkKClgIGFuZCBgcm5vcm0oKWAgc2VlIGFib3ZlIChoaW50OiBjdHJsICsgRiB3aWxsIGJyaW5nIHVwIGEgc2VhcmNoIGJhciB0byBzZWFyY2ggdGhlIGRvY3VtZW50IGZvciB0aGVzZSBmdW5jdGlvbnMpCgpCZWxvdyB3ZSBhcmUgY3JlYXRpbmcgdmFyaWFibGVzIGNhbGxlZCAnZ2VuZTEnLCAnZ2VuZTInLCAnb3JnLmRpZmYnLCBhbmQgJ2dlbmUuZGYnLiAgCidnZW5lMScgJiAnZ2VuZTInIGFyZSByZXByZXNlbnRpbmcgZGF0YSBjb2xsZWN0ZWQgdW5kZXIgZGlmZmVyZW50IGNvbmRpdGlvbnMuICAgCgp0byBsZWFybiBhYm91dCBgZGF0YS5mcmFtZWAgYW5kIGByZXBgIHVzZSA/ZGF0YS5mcmFtZSBhbmQgP3JlcApgYGB7cn0KI3NldCB0aGUgc2VlZCB0byAxMDAKYmFzZTo6c2V0LnNlZWQoMTAwKQoKI3NpbXVsYXRlIGdlbmUgZXhwcmVzc2lvbiBtZWFzdXJlbWVudHMgb2J0YWluZWQgdW5kZXIgZGlmZmVyZW50IGNvbmRpdGlvbnMKZ2VuZTEgPSBzdGF0czo6cm5vcm0oMzAsIG1lYW49NCwgc2Q9MikKZ2VuZTIgPSBzdGF0czo6cm5vcm0oMzAsIG1lYW49Miwgc2Q9MikKCiNjYWxjdWxhdGUgZGlmZmVyZW5jZXMgaW4gbWVhbnMKb3JnLmRpZmYgPSBiYXNlOjptZWFuKGdlbmUxKSAtIGJhc2U6Om1lYW4oZ2VuZTIpCgojY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHZhbHVlcyBpbiBjb2x1bW4gMSBhbmQgdGhlIGxhYmVscyAndGVzdCcgYW5kICdjb250cm9sJyBpbiBjb2x1bW4gMiBjb3JyZXNwb25kaW5nIHRvIGdlbmUxICh0aGUgZ2VuZSB3ZSBhcmUgY2FsbGluZyAndGVzdCcpIGFuZCBnZW5lMiAodGhlIGdlbmUgd2UgYXJlIGNhbGxpbmcgJ2NvbnRyb2wnKQpnZW5lLmRmID0gYmFzZTo6ZGF0YS5mcmFtZShleHAgPSBiYXNlOjpjKGdlbmUxLCBnZW5lMiksCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gYyhiYXNlOjpyZXAoInRlc3QiLCAzMCksIGJhc2U6OnJlcCgiY29udHJvbCIsIDMwKSkpCmBgYAoKdG8gcHJvZHVjZSBleHAubnVsbCB3ZSB1c2UgdGhlIGdlbmUuZGYgZGF0YSBmcmFtZSBidXQgJ3NodWZmbGUnIHdoaWNoIHZhbHVlcyBhcmUgcGFpcmVkIHdpdGggd2hpY2ggZ3JvdXAgYW5kIHJlcGxpY2F0ZXMgMTAwMCB0aW1lcy4KYGBge3J9CiMKZXhwLm51bGwgPC0gbW9zYWljOjpkbygxMDAwKSAqIGJhc2U6OmRpZmYobW9zYWljOjptZWFuKGV4cCB+IG1vc2FpYzo6c2h1ZmZsZShncm91cCksIGRhdGEgPSBnZW5lLmRmKSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApgYGAKQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSByYW5kb21seSBkaXN0cmlidXRlZCB2YWx1ZXMgKGlmIHRoZSBudWxsIGh5cG90aGVzaXMgaXMgY29ycmVjdCkgdXNpbmcgdGhlIGZpcnN0IGNvbHVtbiBvZiB0aGUgZGF0YSBmcmFtZSAnZXhwLm51bGwnICh3aGVyZSB0aGUgdmFsdWVzIGFyZSBzdG9yZWQpLiAgCnVzZSA/aGlzdCB0byBleHBsb3JlIHRoZSBhcmd1bWVudHMgYmVsb3cKYGBge3J9Cmhpc3QoZXhwLm51bGxbLDFdLCB4bGFiPSJudWxsIGRpc3RyaWJ1dGlvbiB8IG5vIGRpZmZlcmVuY2UgaW4gc2FtcGxlcyIsIG1haW4gPSBleHByZXNzaW9uKHBhc3RlKEhbMF0sICI6bm8gZGlmZmVyZW5jZSBpbiBtZWFucyIpKSwgeGxpbSA9IGMoLTIsIDIpLCBjb2w9ImNvcm5mbG93ZXJibHVlIiwgYm9yZGVyPSJ3aGl0ZSIpCgojYWRkIGEgcmVkIGxpbmUgdG8gaW5kaWNhdGUgdGhlIGxvY2F0aW9uIG9mIHRoZSA5NSUgcGVyY2VudGlsZQpncmFwaGljczo6YWJsaW5lKHYgPSBtb3NhaWM6OnF1YW50aWxlKGV4cC5udWxsWywgMV0sIDAuOTUpLCBjb2wgPSAicmVkIiApCgojYWRkIGEgYmx1ZSBsaW5lIHRvIGluZGljYXRlIHRoZSBvcmlnaW5hbCBkaWZmZXJlbmNlIGluIG1lYW5zIGJldHdlZW4gc2FtcGxlIGdyb3VwcwpncmFwaGljczo6YWJsaW5lKHYgPSBvcmcuZGlmZiwgY29sID0gImJsdWUiICkKCiNhZGQgYSByZWQgbGFiZWwgJzAuMDUnIHRvIHRoZSByZWQgbGluZSBhdCB0aGUgbGV2ZWwgb2YgMjAwIG9uIHRoZSB5LWF4aXMgKGNvcnJlc3BvbmRzIHRvIGEgcC12YWx1ZSBvZiAwLjA1KQpncmFwaGljczo6dGV4dCh4ID0gbW9zYWljOjpxdWFudGlsZShleHAubnVsbFssIDFdLCAwLjk1KSwgeSA9IDIwMCwiMC4wNSIsIGFkaiA9IGMoMSwgMCksIGNvbD0icmVkIikKCiNhZGQgYSBibHVlIGxhYmVsICdvcmcuIGRpZmYuJyB0byB0aGUgYmx1ZSBsaW5lIGF0IHRoZSBsZXZlbCBvZiAyMDAgb24gdGhlIHktYXhpcwpncmFwaGljczo6dGV4dCh4ID0gb3JnLmRpZmYsIHkgPSAyMDAsIm9yZy4gZGlmZi4iLCBhZGogPSBjKDEsIDApLCBjb2wgPSAiYmx1ZSIpCmBgYAoKQ2FsY3VsYXRlIHRoZSBwLXZhbHVlIG9mIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHJhbmRvbWx5IHByb2R1Y2VkIG51bGwgZGF0YSBzZXQgYW5kIHRoZSBvcmlnaW5hbCBkYXRhIHNldC4KYGBge3J9CnAudmFsID0gYmFzZTo6c3VtKGV4cC5udWxsWywgMV0gPiBvcmcuZGlmZikgLyBiYXNlOjpsZW5ndGgoZXhwLm51bGxbLCAxXSkKcC52YWwgCmBgYAoKIyMjIFszLjIuMiBVc2luZyB0LXRlc3QgZm9yIGRpZmZlcmVuY2Ugb2YgdGhlIG1lYW5zIGJldHdlZW4gdHdvIHNhbXBsZXNdKGh0dHBzOi8vY29tcGdlbm9tci5naXRodWIuaW8vYm9vay9ob3ctdG8tdGVzdC1mb3ItZGlmZmVyZW5jZXMtYmV0d2Vlbi1zYW1wbGVzLmh0bWwjdXNpbmctdC10ZXN0LWZvci1kaWZmZXJlbmNlLW9mLXRoZS1tZWFucy1iZXR3ZWVuLXR3by1zYW1wbGVzKQpObyBSIGV4cGxhbmF0aW9ucyByZXF1aXJlZCBpbiB0aGlzIHNlY3Rpb24KCiMjIyBbMy4yLjMgTXVsdGlwbGUgdGVzdGluZyBjb3JyZWN0aW9uXShodHRwczovL2NvbXBnZW5vbXIuZ2l0aHViLmlvL2Jvb2svaG93LXRvLXRlc3QtZm9yLWRpZmZlcmVuY2VzLWJldHdlZW4tc2FtcGxlcy5odG1sI211bHRpcGxlLXRlc3RpbmctY29ycmVjdGlvbikKCkJlbG93IHdlIHdpbGwgdXNlIGEgbmV3IHBhY2thZ2UsIHF2YWx1ZS4gVGhpcyBwYWNrYWdlIGlzIGEgYmlvY29uZHVjdG9yIHBhY2thZ2Ugc28gd2UgaW5zdGFsbCBpdCBkaWZmZXJlbnRseS4gCmBgYHtyfQppZiAoIXJlcXVpcmUoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKQmlvY01hbmFnZXI6Omluc3RhbGwoIkJpb2Jhc2UiKQpgYGAKSW4gdGhlIGNvbnNvbGUgYmVsb3csIHlvdSBtYXkgYmUgYXNrZWQ6IApVcGRhdGUgYWxsL3NvbWUvbm9uZT8gW2Evcy9uXTogCgp0eXBlICdhJywgdGhlbiAnZW50ZXInCgpOb3cgaW5zdGFsbCB0aGUgcGFja2FnZSAncXZhbHVlJwpgYGB7cn0KQmlvY01hbmFnZXI6Omluc3RhbGwoInF2YWx1ZSIpCmBgYAoKQW5kIGdvIHRvIHRoZSBsaWJyYXJ5IHRvIGdldCBpdCBmb3IgdXNlIGluIHRoaXMgc2Vzc2lvbgpgYGB7cn0KbGlicmFyeShxdmFsdWUpCmBgYAoKTG9hZCB0aGUgZGF0YSBmcm9tIGEgYnJlYXN0IGNhbmNlciBnZW5lIGV4cHJlc3Npb24gc3R1ZHksIHN0b3JlZCBpbiB0aGUgcGFja2FnZSBxdmFsdWUsIGZvciB1c2UKClJlZmVyZW5jZXMgZm9yIHRoZXNlIGRhdGE6CkhlZGVuZmFsayBJIGV0IGFsLiAoMjAwMSkuIEdlbmUgZXhwcmVzc2lvbiBwcm9maWxlcyBpbiBoZXJlZGl0YXJ5IGJyZWFzdCBjYW5jZXIuIE5ldyBFbmdsYW5kIEpvdXJuYWwgb2YgTWVkaWNpbmUsIDM0NDogNTM5LTU0OC4KU3RvcmV5IEpEIGFuZCBUaWJzaGlyYW5pIFIuICgyMDAzKS4gU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGZvciBnZW5vbWUtd2lkZSBzdHVkaWVzLiBQcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcywgMTAwOiA5NDQwLTk0NDUuIGh0dHA6Ly93d3cucG5hcy5vcmcvY29udGVudC8xMDAvMTYvOTQ0MC5mdWxsCgpUaGUgYGhlZGVuZmFsa2AgZGF0YSBzZXQgd2FzIGltcG9ydGVkIHdpdGggdGhlIGBxdmFsdWVgIHBhY2thZ2UuIFdlIGxvYWQgaXQgaW50byBvdXIgZW52aXJvbm1lbnQgd2l0aCB0aGUgYGRhdGFgIGNvbW1hbmQuIFRvIGxlYXJuIG1vcmUgYWJvdXQgYW55IG9mIHRoZXNlIGA/d2hhdF95b3Vfd2FudF90b19sZWFybl9hYm91dGAgaW4gdGhlIGNvbnNvbGUuIApgYGB7cn0KZGF0YShoZWRlbmZhbGspCmBgYAoKdXNlIHRoZSBgcXZhbHVlYCBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIHF2YWx1ZXMgZnJvbSB0aGUgdmVjb3Igb2YgcHZhbHVlcyBgcGAgcHJvdmlkZWQgaW4gdGhlIGRhdGFzZXQgYGhlZGVuZmFsa2AgdGhlbiBleHRyYWN0IG9ubHkgcXZhbHVlcyBmcm9tIHRoZSBsaXN0IHRoYXQgdGhlIGBxdmFsdWVgIGZ1bmN0aW9uIHByb2R1Y2VzLiAKClRoZSBib29rIGluc3RydWN0aW9ucyBsb29rIGxpa2UgdGhpcyBgcXZhbHVlcyA8LSBxdmFsdWUoaGVkZW5mYWxrJHApJHFgClRoZSBgJGAgdXN1YWxseSBtZWFucyB3ZSBhcmUgc3Vic2V0dGluZyBzb21ldGhpbmcuIAoKT24gdGhlIHJpZ2h0IC0+IHVuZGVyIHRoZSBFbnZpcm9ubWVudCB0YWIvRGF0YSBjbGljayBvbiB0aGUgZGF0YXNldCBgaGVkZW5mYWxrYC4gVGhlcmUgYXJlIHRocmVlIHBhcnRzIHRvIHRoaXMgbGlzdCAncCcgJ3N0YXQnICdzdGF0MCcuIFdlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gJ3AnLgoKVGhlIHdheSBSIHN0b3JlcyB0aGVzZSBkYXRhIGFyZSB3aXRoIHRoZSBgJGAuIFRvIHRlbGwgUiB3ZSBvbmx5IHdhbnQgdGhlIGluZm9ybWF0aW9uIGluICdwJyB3ZSB1c2U6IGBoZWRlbmZhbGskcGAKClRoZSBgaGVkZW5mYWxrYCBkYXRhc2V0IGRvZXNuJ3QgaGF2ZSBhICdxJy4gV2hlcmUgZG9lcyB0aGUgJHEgY29tZSBmcm9tPyAKClR5ZSBgcXZhbHVlKGhlZGVuZmFsayRwKWAgaW50byB0aGUgY29uc29sZS4gU2Nyb2xsIHRocm91Z2ggdGhlIG91dHB1dC4KCk5vdyB5b3UgYXJlIHNlZWluZyB0aGVzZSBkYXRhIGhvdyBSICdzZWVzJyBpdC4gWW91IGNhbiBzZWUgdGhhdCB0aGVyZSBpcyBubyBgJHFgLiBUaGVyZSBpcyBgJHF2YWx1ZWAuCgpQbGF5IHdpdGggd2hhdCByZXN1bHRzIHlvdSBnZXQgZnJvbSB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIGluIHRoZSBjb25zb2xlOgoKYHF2YWx1ZShwID0gaGVkZW5mYWxrJHApJHBgCmBxdmFsdWUocCA9IGhlZGVuZmFsayRwKSRsYApgcXZhbHVlKHAgPSBoZWRlbmZhbGskcCkkbGFgCmBxdmFsdWUocCA9IGhlZGVuZmFsayRwKSRxYAoKV2h5IGRvIHNvbWUgcmV0dXJuIGRhdGEgYnV0IG90aGVycyByZXR1cm4gJ251bGwnPwoKSG93IGRvZXMgdGhlIGRhdGEgcmV0dXJuZWQgYnkgYHF2YWx1ZShwID0gaGVkZW5mYWxrJHApJHFgIGNvbXBhcmUgdG8gYHF2YWx1ZXNgIHByb2R1Y2VkIGJlbG93PyAKYGBge3J9CnF2YWx1ZXMgPC0gcXZhbHVlKGhlZGVuZmFsayRwKSRxCmBgYAoKVGhlIGJvb2sgbGlrZWx5IHVzZWQgJ3EnIHRvIGF2b2lkIGNvbmZ1c2lvbiB3aXRoIHRoZSBjb21tYW5kIGBxdmFsdWVgIG9yIHRoZSBuZXcgb2JqZWN0IHRoZSBpbnN0cnVjdGlvbnMgYXNrIHlvdSB0byBjcmVhdGVkIGluIHRoZSBjb2RlIGJsb2NrIGFib3ZlLiBVbHRpbWF0ZWx5LCBJIGZpbmQgdGhpcyBjb25mdXNpbmcgYmVjYXVzZSBpdCBtYWtlcyBmaWd1cmluZyBvdXQgd2hhdCBlYWNoIGNvbXBvbmVudCBvZiBhIGNvbW1hbmQgZG8gYW5kIHdoZXJlIHRoZXkgY29tZSBmcm9tLiBJIHdvdWxkIGFkdmlzZSBpbnN0ZWFkIHNvbWV0aGluZyBsaWtlOgoKYGBge3J9CnFyZXN1bHQgPC0gcXZhbHVlKGhlZGVuZmFsayRwKSRxdmFsdWUKYGBgCgpOb3cgd2UgdGVzdCB0aGUgY29ycmVjdGlvbiBtZXRob2RzCgpOb3RlIHRoYXQgdGhlIGJvb2sgdXNlcyB0aGUgYD1gIGFzc2lnbm1lbnQgb3BlcmF0b3IgaGVyZSBidXQgdGhlIGA8LWAgYXNzaWdubWVudCBvcGVyYXRvciBlbHNld2hlcmUuIEkgZmluZCB0aGlzIGNvbmZ1c2luZyBzbyBoYXZlIGNvbnZlcnRlZCBhbGwgdG8gYDwtYCAocmVhZCBtb3JlIGFib3V0IG9wZXJhdG9ycyBbaGVyZV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL1N5bnRheCkpCmBgYHtyfQpib25mLnB2YWwgPC0gcC5hZGp1c3QoaGVkZW5mYWxrJHAsbWV0aG9kID0iYm9uZmVycm9uaSIpCmZkci5hZGoucHZhbCA8LSBwLmFkanVzdChoZWRlbmZhbGskcCxtZXRob2QgPSJmZHIiKQpgYGAKCmBgYHtyfQpwbG90KGhlZGVuZmFsayRwLCBxcmVzdWx0LCBwY2g9MTksIHlsaW09YygwLDEpLCB4bGFiPSJyYXcgUC12YWx1ZXMiLCB5bGFiPSJhZGp1c3RlZCBQLXZhbHVlcyIpCnBvaW50cyhoZWRlbmZhbGskcCwgYm9uZi5wdmFsLCBwY2g9MTksIGNvbD0icmVkIikKcG9pbnRzKGhlZGVuZmFsayRwLCBmZHIuYWRqLnB2YWwsIHBjaD0xOSwgY29sPSJibHVlIikKbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZD1jKCJxLXZhbHVlIiwiRkRSIChCSCkiICwiQm9uZmVycm9uaSIpLCBmaWxsPWMoImJsYWNrIiwiYmx1ZSIsInJlZCIpKQpgYGAKCgo=