Preamble

If you havenā€™t done so already, you can check out our R crash course for an overview of why R is great and some of the basic syntax. Here are some great examples of people that have used R to do cool stuff with data

Rā€™s role in promoting reproducible research cannot be overstated.

Two Biostatiscians (later termed ā€˜Forensic Bioinformaticiansā€™) from M.D.Ā Anderson used R extensively during their re-analysis and investigation of a Clinical Prognostication paper from Duke. The subsequent scandal put Reproducible Research at the forefront of everyoneā€™s mind.

Keith Baggerlyā€™s talk on the subject is highly-recommended.

About the practicals for this workshop

  • The traditional way to enter R commands is via the Terminal, or using the console in RStudio (bottom-left panel when RStudio opens for first time).
  • However, for this course we will use a relatively new feature called R-notebooks.
  • An R-notebook mixes plain text with R code
    • we use the terms notebook and markdown interchangeably; they are pretty much the same thing

Markdown is a very simple way of writing a template to produce a pdf, HTML or word document. The compiled version of this document is available online and is more convenient to browse here.

  • ā€œchunksā€ of R code can be added using the insert option from the toolbar, or the CTRL + ALT + I shortcut
  • Each line of R can be executed by clicking on the line and pressing CTRL and ENTER
  • Or you can press the green triangle on the right-hand side to run everything in the chunk
  • Try this now!
  • The code might have different options which dictate how the output is displayed in the compiled document (e.g.Ā HTML)
    • e.g.Ā you might see EVAL = FALSE or echo = FALSE
    • you donā€™t have to worry about this if stepping through the markdown interactively
print("Hello World")

This will be displayed in italic

This will be displayed in bold

  • this
  • is
  • a
  • list
    • this is a sub-list

You can also add hyperlinks, images and tables. More help is available through RStudio Help -> Markdown Quick Reference

To create markdown files for your own analysis; File -> New File -> R Markdownā€¦

About the Bioconductor project

Established in 2001 as a means of distributing tools for the analysis and comprehension of high-throughput genomic data in R. Initially focused on microarrays, but now has many tools for NGS data

  • primary processing of NGS data nearly-always done in other languages
    • R is extensively-used for visualisation and interpretation once data are in manageable form

On the Bioconductor website, you will find

For this session, we will introduce the Bioconductor project as a means of analysing high-throughput data

Installing a package

All Bioconductor software packages are listed under

  • bioconductor.org -> Install -> Packages -> Analysis software packages
  • e.g. edgeR landing page
  • installation instructions are given, which involves running the biocLite command
  • this will install any additional dependancies
  • you only need to run this procedure once for each version of R
## You don't need to run this, edgeR should already be installed for the course
source("http://www.bioconductor.org/biocLite.R")
biocLite("edgeR")

Once installed, a Bioconductor package can be loaded in the usual way with the library function. All packages are required to have a vignette which gives detailed instructions on how to use the package and the workflow of commands. Some packages such as edgeR have very comprehensive user guides with lots of use-cases.

library(edgeR)
vignette("edgeR")
edgeRUsersGuide()

Package documentation can also be accessed via the Help tab in RStudio.

Structures for data analysis

Complex data structures are used in Bioconductor to represent high-throughput data, but we often have simple functions that we can use to access the data. We will use some example data available through Bioconductor to demonstrate how high-throughput data can be represented, and also to review some basic concepts in data manipulation in R.

  • the data are from a microarray experiment. We will be concentrating on more-modern technologies in this class, but most of the R techniques required will be similar
  • experimental data packages are available through Bioconductor, and can be installed in the way we just described
  • the package should already be installed on your computer, so you wonā€™t need to run this.
## No need to run this - for reference only!
##

biocLite("breastCancerVDX")

To make the dataset accessible in R, we first need to load the package. If we navigate to the documentation for breastCancerVDX in RStudio, we find that it provides an object called vdx which we load into Rā€™s memory using the data function.

library(breastCancerVDX)
Error in library(breastCancerVDX) : 
  there is no package called ā€˜breastCancerVDXā€™

The object vdx is a representation of breast cancer dataset that has been converted for use with standard Bioconductor tools. The package authors donā€™t envisage that we will want to view the entire dataset at once, so have provided a number of ways to interact with the data

  • typing the name of the object provides a summary
    • how many genes in the dataset, how many samples etc
vdx
ExpressionSet (storageMode: lockedEnvironment)
assayData: 22283 features, 344 samples 
  element names: exprs 
protocolData: none
phenoData
  sampleNames: VDX_3 VDX_5 ... VDX_2038 (344 total)
  varLabels: samplename dataset ... e.os (21 total)
  varMetadata: labelDescription
featureData
  featureNames: 1007_s_at 1053_at ... AFFX-TrpnX-M_at (22283 total)
  fvarLabels: probe Gene.title ... GO.Component.1 (22 total)
  fvarMetadata: labelDescription
experimentData: use 'experimentData(object)'
  pubMedIds: 17420468 
Annotation: hgu133a 

Accessing expression values

The expression values can be obtained by the exprs function:-

  • remember, <- is used for assignment to create a new variable
  • the data are stored in a matrix in R
    • it is a good idea to check the dimensions using dim, ncol, nrow etc
nrow(eValues)
[1] 22283
  • the rownames are the manufacturer-assigned ID for a particular probe
  • the column names are the identifiers for each patient in the study
  • each entry is a normalised log\(_2\) intensity value for a particular gene in a given sample
    • we wonā€™t talk about normalisation here, but basicallly the data have been processed to eliminate technical variation
  • subsetting a matrix is done using the [row, column] notation
    • the function c is used to make a one-dimensional vector
    • the shortcut : can used to stand for a sequence of consecutive numbers
eValues[c(1,2,3),c(1,2,3,4)]
              VDX_3     VDX_5     VDX_6     VDX_7
1007_s_at 11.965135 11.798593 11.777625 11.538577
1053_at    7.895424  7.885696  7.949535  7.481396
117_at     8.259272  7.052025  8.225930  8.382408
eValues[1:3,1:4]
              VDX_3     VDX_5     VDX_6     VDX_7
1007_s_at 11.965135 11.798593 11.777625 11.538577
1053_at    7.895424  7.885696  7.949535  7.481396
117_at     8.259272  7.052025  8.225930  8.382408
  • we can also omit certain rows or columns from the output by prefixing the indices with a -
eValues[1:3,-(1:4)]

Simple visualisations

The most basic plotting function in R is plot

  • using the plot function with a vector will plot the values of that vector against the index
    • what do you think is displayed in the plot below?
plot(eValues[1,])

## Various aspects of the plot can be controlled by specifying additional arguments
  • one possible use is to compare the values in a vector with respect to a given factor
  • but we donā€™t know the clinical variables in our dataset yet (to come later)
  • a boxplot can also accept a matrix or data frame as an argument
  • what do you think the following plot shows?
boxplot(eValues,outline=FALSE)

Accessing patient data

The metadata, or phenotypic data, for the samples is retrieved using the pData function.

metadata <- pData(vdx)
metadata
  • indivdual columns can be accessed using the $ notation.
  • auto-complete is available in RStudio; once you type the $it should give you a list of possible options.
  • the data are returned as a vector, which can be fed-into other standard plotting and analysis functions
metadata$samplename



Exercise

  • what type of R object is used to store the metadata
  • why is this different to the object used for the expression values?
  • use the square bracket notation [] to print
    • the first 10 rows of the metadata object, first five columns
    • last 10 rows of the metadata object, columns 7 to 9
  • visualise the distribution of the patient ages using a histogram
  • calculate the average age of patients in the study with the mean function
    • what do you notice about the result?
    • can you change the arguments to mean to get a more meaningful result



So far we have been able to print out a subset of our data by specifying a set of numeric indices (e.g.Ā first 10 rows etc). Lets say weā€™re interested in high-grade tumours, in which case we might not know in advance which rows these correspond to

  • == >, <, != can used to make a logical comparison
  • result is a TRUE or FALSE indicating whether each entry satisfies the test condition, or not.
    • however, if a particular entry in the vector is NA the resulting logical vector will have NA at the same positions
metadata$grade == 3
metadata$age > 50

Such a logical vector can then be used for subsetting

  • which can be used to make sure there are no NA values in the logical vectors
    • it gives the numeric indices that correspond to TRUE
  • here, we donā€™t specify any column indices inside the []
    • R will print all the columns
    • however, donā€™t forget the ,!
    • if you do, R will still try and do something. It almost certainly be what you expected
metadata[which(metadata$grade == 3),]

Can use same expression to subset the columns of the expression matrix

  • why can we do this? Because the columns of the expression matrix are in the same order as the rows of the metadata
    • donā€™t believe me? see belowā€¦
  • this isnā€™t a coincidence. the data have been carefully curated to ensure that this is the case
  • data stored in online repositories are often organised in this way
colnames(eValues)
rownames(metadata)
colnames(eValues) == rownames(metadata)
all(colnames(eValues) == rownames(metadata))
  • we can subset the expression data according to our clinical data, and save the result to a file
highGradeExpression <- eValues[,which(metadata$grade==3)]
write.csv(highGradeExpression, file="highGradeTumours.csv")
  • in fact, we can subset the entire vdx object by sample subsets if we wish

vdx[,metadata$grade==3]

Previously, we used a boxplot to visualise the expression levels of all genes in a given sample to look for trends across the dataset. Another use for a boxplot is to visualise the expression level of a particular gene with respect to the sample metadata

  • we can extract the column of interest with a $ and use the formula syntax
    • table in this case will tell us how many observations of each category are present
  • R will be clever and convert the factor into a factor type if required
fac <- metadata$er
table(fac)
fac
  0   1 
135 209 
boxplot(eValues[1,] ~ fac,
        xlab="ER Status",
        ylab="Expression Level",
        col=c("steelblue","goldenrod"))

Performing a test to assess significance follows similar syntax

  • t.test is the generic function to perform a t-test, and can be adapted to different circumstances
    • e.g.Ā if our data are paired, or not
    • see the help for t.test for more details
  • for now, we will gloss over testing assumptions on the data such as requiring a normal (Gaussian) distribtion and multiple testing correction
t.test(eValues[1,]~fac)

    Welch Two Sample t-test

data:  eValues[1, ] by fac
t = -1.7476, df = 244.15, p-value = 0.08179
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.2101311  0.0125555
sample estimates:
mean in group 0 mean in group 1 
       11.72515        11.82394 

Accessing feature (gene) information

We could be lucky, and the first row in the expression matrix could be our favourite gene of interest! However, this is unlikely to be the case and we will have to figure out which row we want to plot

  • we can use another aspect of the nki object; the feature data
  • there is a handy fData function to access these data
  • again, this gives us a data frame
  • this is a pre-built table supplied with the dataset
    • later in the course we will look at using online services and databases for annotation and converting between identifiers
features <- fData(vdx)
class(features)
[1] "data.frame"
features[1:10,]

As we know, gene symbols (more-common gene names) can be accessed using the $ syntax; returning a vector

features$Gene.symbol

We could inspect the data frame manually (e.g.Ā using View(features)) and identfy the row number corresponding to our gene of interest. However, as aspiring R programmers, there is a better way

  • == to test for exact matching
  • match will return the index of the first match
  • grep can be used for partial matches
  • each of the above will give an vector that can be used to subset the expression values
which(features$Gene.symbol == "BRCA1")
[1]  4058 11245
match("BRCA1",features$Gene.symbol)
[1] 4058
grep("BRCA1",features$Gene.symbol)
[1]  4058 11245
grep("BRCA",features$Gene.symbol)
[1]  4058  7869 11245 14103



Exercise

  • Verify that the rows of the feature matrix and the expression values are in the same order
  • Find the row corresponding to your favourite gene
    • if you donā€™t have one, try ESR1
    • if you find multiple matches, pick the first one that occurs
  • Does the expression level of this gene appear to be associated with the ER status?



Testing all genes for significance

Later in the course we will see how to execute a differential expression analysis for RNA-seq data, and discuss some of the issues surrounding this. For now we will perform a simple two-sample t-test for all genes in our study, and derive a results table

  • firstly, we load the genefilter package which has a very convenient function for performing many t tests in parallel
  • rowttests will run a t-test for each row in a given matrix and produce an output table
    • statistic; test statistic
    • dm; difference in means
    • p.value; the p-value
  • rowttests expects a factor as the second argument, so we have to use as.factor
    • as usual, we can get help by doing ?rowttests
library(genefilter)
tstats <- rowttests(eValues, as.factor(metadata$er))
which(is.na(eValues[1,]))
named integer(0)
head(tstats)
hist(tstats$statistic)

?rowttests

The rows are ordered in the same way as the input matrix

  • to change this to increasing significance we can use the order function
  • when given a vector, order will return a vector of the same length giving the permutation that rearranges that vector into ascending or descending order
    • how can we find out what the default way of ordering is?
x <- c(9, 3, 4, 2, 1, 6,5, 10,8,7)
x
 [1]  9  3  4  2  1  6  5 10  8  7
order(x)
 [1]  5  4  2  3  7  6 10  9  1  8
x[order(x)]
 [1]  1  2  3  4  5  6  7  8  9 10
  • so if we want to order by p-value we first use order on the p-value vector
  • this can then be used to re-arrange the rows of the table
tstats[order(tstats$p.value,decreasing = FALSE),]
  • note that we could save the result of order as a variable and then use in the subsetting
    • either is fine, itā€™s a matter of personal preference
p.order <- order(tstats$p.value,decreasing = FALSE)
tstats[p.order,]
NA

However, the table we get isnā€™t immediately useful unless we can recognise the manufacturer probe IDs

  • to provide extra annotation to the table, we can column bind (cbind) the columns of test statistic with those from the feature matrix
  • be careful though, we can only do this in cases where the rows are in direct correspondence

Now when we order by p-value, the extra columns that we just added allow us to interpret the results more easily

We can also query this table to look for our favourite genes of interest

  • %in% is a simplified way to perform matches to multiple items in a vector
tstats.ordered[grep("ESR1",tstats.ordered$Gene.symbol),]
tstats.ordered[tstats.ordered$Gene.symbol %in% c("ESR1","GATA3","FOXA1"),]



Exercise

  • From the annotated table above, select all genes with p-values less than 0.05
  • Write this data frame as a csv file
  • (OPTIONAL) Use the p.adjust to produce a vector of p-values that are adjusted. Add this as an extra column to your table of results and write as a file



More advanced visualisation

Clustering

  • Clustering leads to readily interpretable figures and can be helpful for identifying patterns in time or space.
  • it is known as an unsupervised
  • classes unknown, want to discover them from the data
  • although we can compare to the classes we know about afterwards
  • We can cluster samples (columns)
    • e.g.Ā identification of new / unknown tumor classes using gene expression profiles
  • We can cluster genes (rows)
    • e.g.Ā using large numbers of yeast experiments to identify groups of co-regulated genes
  • we can cluster genes to reduce redundancy (i.e.Ā variable selection) in predictive models
  • it has been used to good effect for many years

The procedure is

  • Preprocess the data
  • Choose a dissimilarity measure
  • Calculate a distance matrix
  • Choose a cluster algorithm
  • Cluster the samples
  • Visualise

Fortunately, there are easy to use functions for calculating the distance matrix (dist) and performing the clustering (hclust). The tricky part is choosing the correct subset of the data and interpreting the results

  • if you look at the help for dist (?dist) you will see that it takes a numeric matrix as its argument
  • how you define this matrix is up to youā€¦
  • also notice that it computes distances between the rows of the data matrix
  • the most common application of clustering is to discover relationships between samples in our dataset
  • which are the matrix columns in our case
  • so we need to transpose our expression values before using this function
?dist
distMat <- dist(t(eValues))

We can see which samples are grouped together by cutting the tree at a particular height, or to give a pre-determined number of groups

cutree(hclust(distMat), h=250)
cutree(hclust(distMat), k=2)



Exercise

  • How many groups are formed if we cut the tree at a height of 250?
  • How many samples are in each group
  • Create a data frame containing the meta data for the samples belonging to the largest group



There are two very important points to consider when performing clustering

  • it will always do something; it can never not work
  • distance between samples is given on the y axis, not the x-axis
  • just because two samples are next to each other on the x-axis does not mean they are similar
  • think of a mobile in a childā€™s nurseryā€¦

Heatmaps

Perhaps a more common way of displaying a heatmap is as part of a heatmap which shows the relationship between both genes and samples

But first we need to restrict the genes

  • we canā€™t display all 30,000 genes in one picture
  • to do this we pick the genes with the the lowest p-value from our previous analysis
library(genefilter)
geneVar <- rowSds(eValues)
topVar <- order(geneVar,decreasing=TRUE)[1:30]

Letā€™s just go ahead and run the function heatmap

Cool! There seems to be some kind of structure to the data. However, the plot is a little ugly. Fortunately there are plenty of options in the function that we can use to tweak the plot (?heatmap)

  • we can specify a block of colour to go underneath each sample to indicate if belong to the ā€œER Positiveā€ or ā€œER Negativeā€ group
  • this has to be a vector with the same length as the number of samples
  • we can build such a vector in two stages
    • first repeating one colour (e.g. blue) for each samples
    • then replacing entries corresponding to a particular sample type with a different colour
sampColours <- rep("blue",ncol(eValues))
sampColours[metadata$er == 0 ] <- "yellow"
heatmap(eValues[mostDE,],
        ColSideColors = sampColours)

The colour scheme for the cells in the heatmap can also be changed. The RColorBrewer package has a number of pre-defined palettes, several of which are suitable for those with colour-blindness (the commonly used red-green scale for heatmaps is clearly a bad choice).

A good choice for a heatmap is a palette which diverges from one extreme to another. If we use the RdBu palette, samples with lower gene expression will be shown in red

  • the function brewer.pal is used to create the palette with a certain number of colours
library(RColorBrewer)
rbPal <- brewer.pal(10, "RdBu")
heatmap(eValues[mostDE,],
        ColSideColors = sampColours,
        col=rbPal)




Exercise

  • Modify the labels on the side of the heatmap so the gene names are displayed rather than probe names
  • You will need to check the help page of heatmap to see which argument needs to be specified to change the labels
  • (Optional) Can you remove the samples names on the bottom of the heatmap?




Of course we should not be surprised that our heatmap showed good separation; we used genes that we previously found to be statistically different between samples.

  • the heatmap function requires a numeric matrix as its argument
    • it doesnā€™t really care how this matrix was generated
  • so itā€™s your choice about what genes / samples are present in the matrix
my.genes <- which(features$Chromosome.location=="8p12")
my.genes
 [1]   375   902   903  1388  2491  5763  5869  7735  7736  7737  7745 11087 18488 18508 19261 20811 20902
heatmap(eValues[my.genes,],
        ColSideColors = sampColours,
        col=rbPal,
        labRow = features$Gene.symbol[my.genes],
        labCol="")

Or give a pre-defined list of genes

(Optional) Downloading data from GEO

For those that might be interested, here is an overview of the commands one might use to download data from the Gene Expression Omnibus. The GEOquery package is used and you have to know the accession number of the dataset that you want to analyse. Typically this would be stated in a publication in the form GSEā€¦..

## If not installed already, install GEOquery with
## source("http://www.bioconductor.org/biocLite.R")
## biocLite("GEOquery")


library(GEOquery)
mydata <- getGEO("GSE1729")

The mydata object that is created is a list in R. This is used because some studies might involve data generated on different platforms, which have separate annotations.

length(mydata)
mydata <- mydata[[1]]
mydata
boxplot(exprs(mydata),outline=FALSE)

For more detailed information on microarrays, see our course

LS0tCnRpdGxlOiAiUiBhbmQgQmlvY29uZHVjdG9yIEludHJvZHVjdGlvbiIKYXV0aG9yOiAiTWFyayBEdW5uaW5nIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiMgUHJlYW1ibGUKCklmIHlvdSBoYXZlbid0IGRvbmUgc28gYWxyZWFkeSwgeW91IGNhbiBjaGVjayBvdXQgb3VyIFtSIGNyYXNoIGNvdXJzZV0oaHR0cHM6Ly9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy5naXRodWIuaW8vci1jcmFzaC1jb3Vyc2UvKSBmb3IgYW4gb3ZlcnZpZXcgb2Ygd2h5IFIgaXMgZ3JlYXQgYW5kIHNvbWUgb2YgdGhlIGJhc2ljIHN5bnRheC4gSGVyZSBhcmUgc29tZSBncmVhdCBleGFtcGxlcyBvZiBwZW9wbGUgdGhhdCBoYXZlIHVzZWQgUiB0byBkbyBjb29sIHN0dWZmIHdpdGggZGF0YQoKLSBbRmFjZWJvb2tdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTAvMTIvYW5hbHlzaXMtb2YtZmFjZWJvb2stc3RhdHVzLXVwZGF0ZXMuaHRtbCksCi0gW2dvb2dsZV0oaHR0cDovL2Jsb2cucmV2b2x1dGlvbmFuYWx5dGljcy5jb20vMjAwOS8wNS9nb29nbGUtdXNpbmctci10by1hbmFseXplLWVmZmVjdGl2ZW5lc3Mtb2YtdHYtYWRzLmh0bWwpLAotIFtNaWNyb3NvZnRdKGh0dHA6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTQvMDUvbWljcm9zb2Z0LXVzZXMtci1mb3IteGJveC1tYXRjaG1ha2luZy5odG1sKSAod2hvIHJlY2VudGx5IFtpbnZlc3RlZF0oaHR0cDovL2Jsb2dzLm1pY3Jvc29mdC5jb20vYmxvZy8yMDE1LzAxLzIzL21pY3Jvc29mdC1hY3F1aXJlLXJldm9sdXRpb24tYW5hbHl0aWNzLWhlbHAtY3VzdG9tZXJzLWZpbmQtYmlnLWRhdGEtdmFsdWUtYWR2YW5jZWQtc3RhdGlzdGljYWwtYW5hbHlzaXMvKSBpbiBhIGNvbW1lcmljYWwgcHJvdmlkZXIgb2YgUikKLSBUaGUgW05ldyBZb3JrIFRpbWVzXShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDExLzAzL2hvdy10aGUtbmV3LXlvcmstdGltZXMtdXNlcy1yLWZvci1kYXRhLXZpc3VhbGl6YXRpb24uaHRtbCkuCi0gW0J1enpmZWVkXShodHRwOi8vYmxvZy5yZXZvbHV0aW9uYW5hbHl0aWNzLmNvbS8yMDE1LzEyL2J1enpmZWVkLXVzZXMtci1mb3ItZGF0YS1qb3VybmFsaXNtLmh0bWwpIHVzZSBSIGZvciBzb21lIG9mIHRoZWlyIHNlcmlvdXMgYXJ0aWNsZXMgYW5kIGhhdmUgbWFkZSB0aGUgY29kZSBbcHVibGljYWxseSBhdmFpbGFibGVdKGh0dHBzOi8vYnV6emZlZWRuZXdzLmdpdGh1Yi5pby8yMDE2LTA0LWZlZGVyYWwtc3VydmVpbGxhbmNlLXBsYW5lcy9hbmFseXNpcy5odG1sKQotIFRoZSBbTmV3IFplYWxhbmQgVG91cmlzdCBCb2FyZF0oaHR0cHM6Ly9tYmllbnouc2hpbnlhcHBzLmlvL3RvdXJpc21fZGFzaGJvYXJkX3Byb2QvKSBoYXZlIFIgcnVubmluZyBpbiB0aGUgYmFja2dyb3VuZCBvZiB0aGVpciB3ZWJzaXRlCi0gW0FpcmJuYl0oaHR0cHM6Ly9tZWRpdW0uY29tL2FpcmJuYi1lbmdpbmVlcmluZy91c2luZy1yLXBhY2thZ2VzLWFuZC1lZHVjYXRpb24tdG8tc2NhbGUtZGF0YS1zY2llbmNlLWF0LWFpcmJuYi05MDZmYWE1OGUxMmQpCi0gW1RoZSBCQkNdKGh0dHBzOi8vZ2l0aHViLmNvbS9CQkMtRGF0YS1Vbml0L211c2ljLWZlc3RpdmFscykgbWFrZXMgY29kZSBhdmFpbGFibGUgZm9yIHNvbWUgb2YgdGhlaXIgc3RvcmllcyAoZS5nLiBnZW5kZXIgYmlhcyBpbiBtdXNpYyBmZXN0aXZhbHMpCgpSJ3Mgcm9sZSBpbiBwcm9tb3RpbmcgcmVwcm9kdWNpYmxlIHJlc2VhcmNoIGNhbm5vdCBiZSBvdmVyc3RhdGVkLgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy9jcnVrLWF1dHVtbi1zY2hvb2wtMjAxNy9tYXN0ZXIvSW50cm9kdWN0aW9uL2ltYWdlcy9yZXAtcmVzZWFyY2gtbnl0LnBuZykKClR3byBCaW9zdGF0aXNjaWFucyAobGF0ZXIgdGVybWVkICcqRm9yZW5zaWMgQmlvaW5mb3JtYXRpY2lhbnMqJykgZnJvbSBNLkQuIEFuZGVyc29uIHVzZWQgUiBleHRlbnNpdmVseSBkdXJpbmcgdGhlaXIgcmUtYW5hbHlzaXMgYW5kIGludmVzdGlnYXRpb24gb2YgYSBDbGluaWNhbCBQcm9nbm9zdGljYXRpb24gcGFwZXIgZnJvbSBEdWtlLiBUaGUgc3Vic2VxdWVudCBbc2NhbmRhbF0oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1XNXNaVE5QTVFSTSkgcHV0IFJlcHJvZHVjaWJsZSBSZXNlYXJjaCBhdCB0aGUgZm9yZWZyb250IG9mIGV2ZXJ5b25lJ3MgbWluZC4KCktlaXRoIEJhZ2dlcmx5J3MgdGFsayBvbiB0aGUgc3ViamVjdCBpcyBoaWdobHktcmVjb21tZW5kZWQuCgo8aWZyYW1lIHdpZHRoPSI0MjAiIGhlaWdodD0iMzE1IiBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkLzdnWUlzN3VZYk1vIiBmcmFtZWJvcmRlcj0iMCIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPgoKIyMgQWJvdXQgdGhlIHByYWN0aWNhbHMgZm9yIHRoaXMgd29ya3Nob3AKCi0gVGhlIHRyYWRpdGlvbmFsIHdheSB0byBlbnRlciBSIGNvbW1hbmRzIGlzIHZpYSB0aGUgVGVybWluYWwsIG9yIHVzaW5nIHRoZSBjb25zb2xlIGluIFJTdHVkaW8gKGJvdHRvbS1sZWZ0IHBhbmVsIHdoZW4gUlN0dWRpbyBvcGVucyBmb3IgZmlyc3QgdGltZSkuCi0gSG93ZXZlciwgZm9yIHRoaXMgY291cnNlIHdlIHdpbGwgdXNlIGEgcmVsYXRpdmVseSBuZXcgZmVhdHVyZSBjYWxsZWQgKlItbm90ZWJvb2tzKi4KLSBBbiBSLW5vdGVib29rIG1peGVzIHBsYWluIHRleHQgd2l0aCBSIGNvZGUKICAgICsgd2UgdXNlIHRoZSB0ZXJtcyBub3RlYm9vayBhbmQgbWFya2Rvd24gaW50ZXJjaGFuZ2VhYmx5OyB0aGV5IGFyZSBwcmV0dHkgbXVjaCB0aGUgc2FtZSB0aGluZwoKKk1hcmtkb3duKiBpcyBhIHZlcnkgc2ltcGxlIHdheSBvZiB3cml0aW5nIGEgdGVtcGxhdGUgdG8gcHJvZHVjZSBhIHBkZiwgSFRNTCBvciB3b3JkIGRvY3VtZW50LiBUaGUgY29tcGlsZWQgdmVyc2lvbiBvZiB0aGlzIGRvY3VtZW50IGlzIGF2YWlsYWJsZSBvbmxpbmUgYW5kIGlzIG1vcmUgY29udmVuaWVudCB0byBicm93c2UgW2hlcmVdKGh0dHBzOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL2NydWstYXV0dW1uLXNjaG9vbC0yMDE3L0RheTEvYmlvYy1pbnRyby5uYi5odG1sKS4KCi0gImNodW5rcyIgb2YgUiBjb2RlIGNhbiBiZSBhZGRlZCB1c2luZyB0aGUgKmluc2VydCogb3B0aW9uIGZyb20gdGhlIHRvb2xiYXIsIG9yIHRoZSBDVFJMICsgQUxUICsgSSBzaG9ydGN1dAotIEVhY2ggbGluZSBvZiBSIGNhbiBiZSBleGVjdXRlZCBieSBjbGlja2luZyBvbiB0aGUgbGluZSBhbmQgcHJlc3NpbmcgQ1RSTCBhbmQgRU5URVIKLSBPciB5b3UgY2FuIHByZXNzIHRoZSBncmVlbiB0cmlhbmdsZSBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlIHRvIHJ1biBldmVyeXRoaW5nIGluIHRoZSBjaHVuawotIFRyeSB0aGlzIG5vdyEKLSBUaGUgY29kZSBtaWdodCBoYXZlIGRpZmZlcmVudCBvcHRpb25zIHdoaWNoIGRpY3RhdGUgaG93IHRoZSBvdXRwdXQgaXMgZGlzcGxheWVkIGluIHRoZSBjb21waWxlZCBkb2N1bWVudCAoZS5nLiBIVE1MKQogICAgKyBlLmcuIHlvdSBtaWdodCBzZWUgYEVWQUwgPSBGQUxTRWAgb3IgYGVjaG8gPSBGQUxTRWAKICAgICsgeW91IGRvbid0IGhhdmUgdG8gd29ycnkgYWJvdXQgdGhpcyBpZiBzdGVwcGluZyB0aHJvdWdoIHRoZSBtYXJrZG93biBpbnRlcmFjdGl2ZWx5CgpgYGB7cn0KcHJpbnQoIkhlbGxvIFdvcmxkIikKCmBgYAoKKlRoaXMgd2lsbCBiZSBkaXNwbGF5ZWQgaW4gaXRhbGljKgoKKipUaGlzIHdpbGwgYmUgZGlzcGxheWVkIGluIGJvbGQqKgoKCi0gdGhpcyAKLSBpcyAKLSBhIAotIGxpc3QKICAgICsgdGhpcyBpcyBhICpzdWItbGlzdCoKCllvdSBjYW4gYWxzbyBhZGQgaHlwZXJsaW5rcywgaW1hZ2VzIGFuZCB0YWJsZXMuIE1vcmUgaGVscCBpcyBhdmFpbGFibGUgdGhyb3VnaCBSU3R1ZGlvICoqSGVscCAtPiBNYXJrZG93biBRdWljayBSZWZlcmVuY2UqKgoKVG8gY3JlYXRlIG1hcmtkb3duIGZpbGVzIGZvciB5b3VyIG93biBhbmFseXNpczsgRmlsZSAtPiBOZXcgRmlsZSAtPiBSIE1hcmtkb3duLi4uCgoKIyBBYm91dCB0aGUgQmlvY29uZHVjdG9yIHByb2plY3QKCiFbXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9pbWFnZXMvbG9nb19iaW9jb25kdWN0b3IuZ2lmKQoKCkVzdGFibGlzaGVkIGluIDIwMDEgYXMgYSBtZWFucyBvZiBkaXN0cmlidXRpbmcgdG9vbHMgZm9yIHRoZSBhbmFseXNpcyBhbmQgY29tcHJlaGVuc2lvbiBvZiBoaWdoLXRocm91Z2hwdXQgZ2Vub21pYyBkYXRhIGluIFIuIEluaXRpYWxseSBmb2N1c2VkIG9uIG1pY3JvYXJyYXlzLCBidXQgbm93IGhhcyBtYW55IHRvb2xzIGZvciBOR1MgZGF0YQoKLSBwcmltYXJ5IHByb2Nlc3Npbmcgb2YgTkdTIGRhdGEgbmVhcmx5LWFsd2F5cyBkb25lIGluIG90aGVyIGxhbmd1YWdlcwogICAgKyBSIGlzIGV4dGVuc2l2ZWx5LXVzZWQgZm9yIHZpc3VhbGlzYXRpb24gYW5kIGludGVycHJldGF0aW9uIG9uY2UgZGF0YSBhcmUgaW4gbWFuYWdlYWJsZSBmb3JtCgpPbiB0aGUgW0Jpb2NvbmR1Y3RvciB3ZWJzaXRlXSh3d3cuYmlvY29uZHVjdG9yLm9yZyksIHlvdSB3aWxsIGZpbmQKCi0gSW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucwotIFtDb3Vyc2UgTWF0ZXJpYWxzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9oZWxwL2NvdXJzZS1tYXRlcmlhbHMvKQotIFtTdXBwb3J0IGZvcnVtXShodHRwczovL3N1cHBvcnQuYmlvY29uZHVjdG9yLm9yZy8pCiAgICArIGEgbWVhbnMgb2YgY29tbXVuaWNhdGluZyB3aXRoIGRldmVsb3BlcnMgYW5kIHBvd2VyLXVzZXJzCi0gW0V4YW1wbGUgZGF0YXNldHNdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvQmlvY1ZpZXdzLmh0bWwjX19fRXhwZXJpbWVudERhdGEpCi0gW0Fubm90YXRpb24gUmVzb3VyY2VzXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX0Fubm90YXRpb25EYXRhKQotIENvbmZlcmVuY2VzCiAgICArIHVwY29taW5nIGNvbmZlcmVuY2UgaW4gQ2FtYnJpZGdlIC0gW0RlY2VtYmVyIDIwMTddKGh0dHBzOi8vYmlvY29uZHVjdG9yLmdpdGh1Yi5pby9FdXJvQmlvYzIwMTcvKQogICAgCgpGb3IgdGhpcyBzZXNzaW9uLCB3ZSB3aWxsIGludHJvZHVjZSB0aGUgQmlvY29uZHVjdG9yIHByb2plY3QgYXMgYSBtZWFucyBvZiBhbmFseXNpbmcgaGlnaC10aHJvdWdocHV0IGRhdGEKCiMjIEluc3RhbGxpbmcgYSBwYWNrYWdlCgpBbGwgQmlvY29uZHVjdG9yIHNvZnR3YXJlIHBhY2thZ2VzIGFyZSBsaXN0ZWQgdW5kZXIKCi0gYmlvY29uZHVjdG9yLm9yZyAtPiBJbnN0YWxsIC0+IFBhY2thZ2VzIC0+IEFuYWx5c2lzICpzb2Z0d2FyZSogcGFja2FnZXMKLSBlLmcuIFtlZGdlUiBsYW5kaW5nIHBhZ2VdKGh0dHA6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2VkZ2VSLmh0bWwpCi0gaW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyBhcmUgZ2l2ZW4sIHdoaWNoIGludm9sdmVzIHJ1bm5pbmcgdGhlIGBiaW9jTGl0ZWAgY29tbWFuZAogICsgdGhpcyB3aWxsIGluc3RhbGwgYW55IGFkZGl0aW9uYWwgZGVwZW5kYW5jaWVzCi0geW91IG9ubHkgbmVlZCB0byBydW4gdGhpcyBwcm9jZWR1cmUgb25jZSBmb3IgZWFjaCB2ZXJzaW9uIG9mIFIKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIFlvdSBkb24ndCBuZWVkIHRvIHJ1biB0aGlzLCBlZGdlUiBzaG91bGQgYWxyZWFkeSBiZSBpbnN0YWxsZWQgZm9yIHRoZSBjb3Vyc2UKc291cmNlKCJodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCmJpb2NMaXRlKCJlZGdlUiIpCmBgYAoKT25jZSBpbnN0YWxsZWQsIGEgQmlvY29uZHVjdG9yIHBhY2thZ2UgY2FuIGJlIGxvYWRlZCBpbiB0aGUgdXN1YWwgd2F5IHdpdGggdGhlIGBsaWJyYXJ5YCBmdW5jdGlvbi4gQWxsIHBhY2thZ2VzIGFyZSByZXF1aXJlZCB0byBoYXZlIGEgKnZpZ25ldHRlKiB3aGljaCBnaXZlcyBkZXRhaWxlZCBpbnN0cnVjdGlvbnMgb24gaG93IHRvIHVzZSB0aGUgcGFja2FnZSBhbmQgdGhlIHdvcmtmbG93IG9mIGNvbW1hbmRzLiBTb21lIHBhY2thZ2VzIHN1Y2ggYXMgYGVkZ2VSYCBoYXZlIHZlcnkgY29tcHJlaGVuc2l2ZSAqdXNlciBndWlkZXMqIHdpdGggbG90cyBvZiB1c2UtY2FzZXMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLGV2YWw9RkFMU0V9CmxpYnJhcnkoZWRnZVIpCnZpZ25ldHRlKCJlZGdlUiIpCmVkZ2VSVXNlcnNHdWlkZSgpCmBgYAoKUGFja2FnZSBkb2N1bWVudGF0aW9uIGNhbiBhbHNvIGJlIGFjY2Vzc2VkIHZpYSB0aGUgKkhlbHAqIHRhYiBpbiBSU3R1ZGlvLgoKIyMgU3RydWN0dXJlcyBmb3IgZGF0YSBhbmFseXNpcwoKQ29tcGxleCBkYXRhIHN0cnVjdHVyZXMgYXJlIHVzZWQgaW4gQmlvY29uZHVjdG9yIHRvIHJlcHJlc2VudCBoaWdoLXRocm91Z2hwdXQgZGF0YSwgYnV0IHdlIG9mdGVuIGhhdmUgc2ltcGxlIGZ1bmN0aW9ucyB0aGF0IHdlIGNhbiB1c2UgdG8gYWNjZXNzIHRoZSBkYXRhLiBXZSB3aWxsIHVzZSBzb21lIGV4YW1wbGUgZGF0YSBhdmFpbGFibGUgdGhyb3VnaCBCaW9jb25kdWN0b3IgdG8gZGVtb25zdHJhdGUgaG93IGhpZ2gtdGhyb3VnaHB1dCBkYXRhIGNhbiBiZSByZXByZXNlbnRlZCwgYW5kIGFsc28gdG8gcmV2aWV3IHNvbWUgYmFzaWMgY29uY2VwdHMgaW4gZGF0YSBtYW5pcHVsYXRpb24gaW4gUi4KCi0gdGhlIGRhdGEgYXJlIGZyb20gYSAqbWljcm9hcnJheSogZXhwZXJpbWVudC4gV2Ugd2lsbCBiZSBjb25jZW50cmF0aW5nIG9uIG1vcmUtbW9kZXJuIHRlY2hub2xvZ2llcyBpbiB0aGlzIGNsYXNzLCBidXQgbW9zdCBvZiB0aGUgUiB0ZWNobmlxdWVzIHJlcXVpcmVkIHdpbGwgYmUgc2ltaWxhcgotIFtleHBlcmltZW50YWwgZGF0YV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9CaW9jVmlld3MuaHRtbCNfX19FeHBlcmltZW50RGF0YSkgcGFja2FnZXMgYXJlIGF2YWlsYWJsZSB0aHJvdWdoIEJpb2NvbmR1Y3RvciwgYW5kIGNhbiBiZSBpbnN0YWxsZWQgaW4gdGhlIHdheSB3ZSBqdXN0IGRlc2NyaWJlZAogICsgdGhlIHBhY2thZ2Ugc2hvdWxkIGFscmVhZHkgYmUgaW5zdGFsbGVkIG9uIHlvdXIgY29tcHV0ZXIsIHNvIHlvdSB3b24ndCBuZWVkIHRvIHJ1biB0aGlzLgoKYGBge3IgZXZhbD1GQUxTRX0KIyMgTm8gbmVlZCB0byBydW4gdGhpcyAtIGZvciByZWZlcmVuY2Ugb25seSEKIyMKCmJpb2NMaXRlKCJicmVhc3RDYW5jZXJWRFgiKQpgYGAKClRvIG1ha2UgdGhlIGRhdGFzZXQgYWNjZXNzaWJsZSBpbiBSLCB3ZSBmaXJzdCBuZWVkIHRvIGxvYWQgdGhlIHBhY2thZ2UuIElmIHdlIG5hdmlnYXRlIHRvIHRoZSBkb2N1bWVudGF0aW9uIGZvciBgYnJlYXN0Q2FuY2VyVkRYYCBpbiBSU3R1ZGlvLCB3ZSBmaW5kIHRoYXQgaXQgcHJvdmlkZXMgYW4gb2JqZWN0IGNhbGxlZCBgdmR4YCB3aGljaCB3ZSBsb2FkIGludG8gUidzIG1lbW9yeSB1c2luZyB0aGUgYGRhdGFgIGZ1bmN0aW9uLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShicmVhc3RDYW5jZXJWRFgpCmRhdGEodmR4KQpgYGAKClRoZSBvYmplY3QgYHZkeGAgaXMgYSByZXByZXNlbnRhdGlvbiBvZiBicmVhc3QgY2FuY2VyIGRhdGFzZXQgdGhhdCBoYXMgYmVlbiBjb252ZXJ0ZWQgZm9yIHVzZSB3aXRoIHN0YW5kYXJkIEJpb2NvbmR1Y3RvciB0b29scy4gVGhlIHBhY2thZ2UgYXV0aG9ycyBkb24ndCBlbnZpc2FnZSB0aGF0IHdlIHdpbGwgd2FudCB0byB2aWV3IHRoZSBlbnRpcmUgZGF0YXNldCBhdCBvbmNlLCBzbyBoYXZlIHByb3ZpZGVkIGEgbnVtYmVyIG9mIHdheXMgdG8gaW50ZXJhY3Qgd2l0aCB0aGUgZGF0YQoKLSB0eXBpbmcgdGhlIG5hbWUgb2YgdGhlIG9iamVjdCBwcm92aWRlcyBhIHN1bW1hcnkKICAgICsgaG93IG1hbnkgZ2VuZXMgaW4gdGhlIGRhdGFzZXQsIGhvdyBtYW55IHNhbXBsZXMgZXRjCiAgICAKYGBge3J9CnZkeApgYGAKCgojIyBBY2Nlc3NpbmcgZXhwcmVzc2lvbiB2YWx1ZXMKClRoZSBleHByZXNzaW9uIHZhbHVlcyBjYW4gYmUgb2J0YWluZWQgYnkgdGhlIGBleHByc2AgZnVuY3Rpb246LQoKLSByZW1lbWJlciwgYDwtYCBpcyB1c2VkIGZvciBhc3NpZ25tZW50IHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZQotIHRoZSBkYXRhIGFyZSBzdG9yZWQgaW4gYSBgbWF0cml4YCBpbiBSCiAgICArIGl0IGlzIGEgZ29vZCBpZGVhIHRvIGNoZWNrIHRoZSBkaW1lbnNpb25zIHVzaW5nIGBkaW1gLCBgbmNvbGAsIGBucm93YCBldGMKICAgIApgYGB7cn0KZVZhbHVlcyA8LSBleHBycyh2ZHgpCmNsYXNzKGVWYWx1ZXMpCmRpbShlVmFsdWVzKQpuY29sKGVWYWx1ZXMpCm5yb3coZVZhbHVlcykKCgpgYGAKCgotIHRoZSByb3duYW1lcyBhcmUgdGhlIG1hbnVmYWN0dXJlci1hc3NpZ25lZCBJRCBmb3IgYSBwYXJ0aWN1bGFyIHByb2JlCi0gdGhlIGNvbHVtbiBuYW1lcyBhcmUgdGhlIGlkZW50aWZpZXJzIGZvciBlYWNoIHBhdGllbnQgaW4gdGhlIHN0dWR5Ci0gZWFjaCBlbnRyeSBpcyBhICpub3JtYWxpc2VkKiBsb2ckXzIkIGludGVuc2l0eSB2YWx1ZSBmb3IgYSBwYXJ0aWN1bGFyIGdlbmUgaW4gYSBnaXZlbiBzYW1wbGUKICAgICsgd2Ugd29uJ3QgdGFsayBhYm91dCBub3JtYWxpc2F0aW9uIGhlcmUsIGJ1dCBiYXNpY2FsbGx5IHRoZSBkYXRhIGhhdmUgYmVlbiBwcm9jZXNzZWQgdG8gZWxpbWluYXRlIHRlY2huaWNhbCB2YXJpYXRpb24KLSBzdWJzZXR0aW5nIGEgbWF0cml4IGlzIGRvbmUgdXNpbmcgdGhlIGBbcm93LCBjb2x1bW5dYCBub3RhdGlvbgogICAgKyB0aGUgZnVuY3Rpb24gYGNgIGlzIHVzZWQgdG8gbWFrZSBhIG9uZS1kaW1lbnNpb25hbCAqdmVjdG9yKgogICAgKyB0aGUgc2hvcnRjdXQgYDpgIGNhbiB1c2VkIHRvIHN0YW5kIGZvciBhIHNlcXVlbmNlIG9mIGNvbnNlY3V0aXZlIG51bWJlcnMKICAKYGBge3J9CmVWYWx1ZXNbYygxLDIsMyksYygxLDIsMyw0KV0KZVZhbHVlc1sxOjMsMTo0XQoKCmBgYAoKLSB3ZSBjYW4gYWxzbyBvbWl0IGNlcnRhaW4gcm93cyBvciBjb2x1bW5zIGZyb20gdGhlIG91dHB1dCBieSBwcmVmaXhpbmcgdGhlIGluZGljZXMgd2l0aCBhIGAtYAoKYGBge3IgZXZhbD1GQUxTRX0KZVZhbHVlc1sxOjMsLSgxOjQpXQoKYGBgCgojIyBTaW1wbGUgdmlzdWFsaXNhdGlvbnMKClRoZSBtb3N0IGJhc2ljIHBsb3R0aW5nIGZ1bmN0aW9uIGluIFIgaXMgYHBsb3RgCgotIHVzaW5nIHRoZSBgcGxvdGAgZnVuY3Rpb24gd2l0aCBhIHZlY3RvciB3aWxsIHBsb3QgdGhlIHZhbHVlcyBvZiB0aGF0IHZlY3RvciBhZ2FpbnN0IHRoZSBpbmRleAogICAgKyB3aGF0IGRvIHlvdSB0aGluayBpcyBkaXNwbGF5ZWQgaW4gdGhlIHBsb3QgYmVsb3c/CgpgYGB7cn0KcGxvdChlVmFsdWVzWzEsXSkKCiMjIFZhcmlvdXMgYXNwZWN0cyBvZiB0aGUgcGxvdCBjYW4gYmUgY29udHJvbGxlZCBieSBzcGVjaWZ5aW5nIGFkZGl0aW9uYWwgYXJndW1lbnRzCgoKYGBgCgoKCi0gb25lIHBvc3NpYmxlIHVzZSBpcyB0byBjb21wYXJlIHRoZSB2YWx1ZXMgaW4gYSB2ZWN0b3Igd2l0aCByZXNwZWN0IHRvIGEgZ2l2ZW4gZmFjdG9yCi0gYnV0IHdlIGRvbid0IGtub3cgdGhlIGNsaW5pY2FsIHZhcmlhYmxlcyBpbiBvdXIgZGF0YXNldCB5ZXQgKHRvIGNvbWUgbGF0ZXIpCi0gYSBib3hwbG90IGNhbiBhbHNvIGFjY2VwdCBhIG1hdHJpeCBvciBkYXRhIGZyYW1lIGFzIGFuIGFyZ3VtZW50Ci0gd2hhdCBkbyB5b3UgdGhpbmsgdGhlIGZvbGxvd2luZyBwbG90IHNob3dzPwoKYGBge3IgZmlnLndpZHRoPTEyfQpib3hwbG90KGVWYWx1ZXMsb3V0bGluZT1GQUxTRSkKCgoKYGBgCgoKIyMgQWNjZXNzaW5nIHBhdGllbnQgZGF0YQoKVGhlICptZXRhZGF0YSosIG9yIHBoZW5vdHlwaWMgZGF0YSwgZm9yIHRoZSBzYW1wbGVzIGlzIHJldHJpZXZlZCB1c2luZyB0aGUgYHBEYXRhYCBmdW5jdGlvbi4KCmBgYHtyfQptZXRhZGF0YSA8LSBwRGF0YSh2ZHgpCm1ldGFkYXRhCmBgYAoKCi0gaW5kaXZkdWFsIGNvbHVtbnMgY2FuIGJlIGFjY2Vzc2VkIHVzaW5nIHRoZSBgJGAgbm90YXRpb24uIAotICphdXRvLWNvbXBsZXRlKiBpcyBhdmFpbGFibGUgaW4gUlN0dWRpbzsgb25jZSB5b3UgdHlwZSB0aGUgYCRgaXQgc2hvdWxkIGdpdmUgeW91IGEgbGlzdCBvZiBwb3NzaWJsZSBvcHRpb25zLgotIHRoZSBkYXRhIGFyZSByZXR1cm5lZCBhcyBhICp2ZWN0b3IqLCB3aGljaCBjYW4gYmUgZmVkLWludG8gb3RoZXIgc3RhbmRhcmQgcGxvdHRpbmcgYW5kIGFuYWx5c2lzIGZ1bmN0aW9ucyAKCmBgYHtyIGV2YWw9RkFMU0V9Cm1ldGFkYXRhJHNhbXBsZW5hbWUKCgpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKCiMjIEV4ZXJjaXNlCgotIHdoYXQgdHlwZSBvZiBSIG9iamVjdCBpcyB1c2VkIHRvIHN0b3JlIHRoZSBtZXRhZGF0YQotIHdoeSBpcyB0aGlzIGRpZmZlcmVudCB0byB0aGUgb2JqZWN0IHVzZWQgZm9yIHRoZSBleHByZXNzaW9uIHZhbHVlcz8KLSB1c2UgdGhlIHNxdWFyZSBicmFja2V0IG5vdGF0aW9uIGBbXWAgdG8gcHJpbnQKICAgICsgdGhlIGZpcnN0IDEwIHJvd3Mgb2YgdGhlIG1ldGFkYXRhIG9iamVjdCwgZmlyc3QgZml2ZSBjb2x1bW5zCiAgICArIGxhc3QgMTAgcm93cyBvZiB0aGUgbWV0YWRhdGEgb2JqZWN0LCBjb2x1bW5zIDcgdG8gOQotIHZpc3VhbGlzZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBwYXRpZW50IGFnZXMgdXNpbmcgYSBoaXN0b2dyYW0KLSBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgYWdlIG9mIHBhdGllbnRzIGluIHRoZSBzdHVkeSB3aXRoIHRoZSBgbWVhbmAgZnVuY3Rpb24KICAgICsgd2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHRoZSByZXN1bHQ/CiAgICArIGNhbiB5b3UgY2hhbmdlIHRoZSBhcmd1bWVudHMgdG8gbWVhbiB0byBnZXQgYSBtb3JlIG1lYW5pbmdmdWwgcmVzdWx0CgoqKioqKioKKioqKioqCioqKioqKgoKCmBgYHtyfQoKCmBgYAoKCgoKU28gZmFyIHdlIGhhdmUgYmVlbiBhYmxlIHRvIHByaW50IG91dCBhIHN1YnNldCBvZiBvdXIgZGF0YSBieSBzcGVjaWZ5aW5nIGEgc2V0IG9mIG51bWVyaWMgaW5kaWNlcyAoZS5nLiBmaXJzdCAxMCByb3dzIGV0YykuIExldHMgc2F5IHdlJ3JlIGludGVyZXN0ZWQgaW4gaGlnaC1ncmFkZSB0dW1vdXJzLCBpbiB3aGljaCBjYXNlIHdlIG1pZ2h0IG5vdCBrbm93IGluIGFkdmFuY2Ugd2hpY2ggcm93cyB0aGVzZSBjb3JyZXNwb25kIHRvCgotIGA9PWAgYD5gLCBgPGAsIGAhPWAgY2FuIHVzZWQgdG8gbWFrZSBhICpsb2dpY2FsIGNvbXBhcmlzb24qCi0gcmVzdWx0IGlzIGEgYFRSVUVgIG9yIGBGQUxTRWAgaW5kaWNhdGluZyB3aGV0aGVyIGVhY2ggZW50cnkgc2F0aXNmaWVzIHRoZSB0ZXN0IGNvbmRpdGlvbiwgb3Igbm90LgogICAgKyBob3dldmVyLCBpZiBhIHBhcnRpY3VsYXIgZW50cnkgaW4gdGhlIHZlY3RvciBpcyBgTkFgIHRoZSByZXN1bHRpbmcgbG9naWNhbCB2ZWN0b3Igd2lsbCBoYXZlIGBOQWAgYXQgdGhlIHNhbWUgcG9zaXRpb25zCgpgYGB7ciBldmFsPUZBTFNFfQptZXRhZGF0YSRncmFkZSA9PSAzCm1ldGFkYXRhJGFnZSA+IDUwCgpgYGAKClN1Y2ggYSBsb2dpY2FsIHZlY3RvciBjYW4gdGhlbiBiZSB1c2VkIGZvciBzdWJzZXR0aW5nCgotIGB3aGljaGAgY2FuIGJlIHVzZWQgdG8gbWFrZSBzdXJlIHRoZXJlIGFyZSBubyBgTkFgIHZhbHVlcyBpbiB0aGUgbG9naWNhbCB2ZWN0b3JzCiAgICArIGl0IGdpdmVzIHRoZSBudW1lcmljIGluZGljZXMgdGhhdCBjb3JyZXNwb25kIHRvIGBUUlVFYAotIGhlcmUsIHdlIGRvbid0IHNwZWNpZnkgYW55IGNvbHVtbiBpbmRpY2VzIGluc2lkZSB0aGUgYFtdYAogICAgKyBSIHdpbGwgcHJpbnQgYWxsIHRoZSBjb2x1bW5zCiAgICArIGhvd2V2ZXIsIGRvbid0IGZvcmdldCB0aGUgLCEKICAgICsgaWYgeW91IGRvLCBSIHdpbGwgc3RpbGwgdHJ5IGFuZCBkbyBzb21ldGhpbmcuIEl0IGFsbW9zdCBjZXJ0YWlubHkgYmUgd2hhdCB5b3UgZXhwZWN0ZWQKCmBgYHtyfQptZXRhZGF0YVt3aGljaChtZXRhZGF0YSRncmFkZSA9PSAzKSxdCgoKYGBgCgpDYW4gdXNlIHNhbWUgZXhwcmVzc2lvbiB0byBzdWJzZXQgdGhlIGNvbHVtbnMgb2YgdGhlIGV4cHJlc3Npb24gbWF0cml4CgotIHdoeSBjYW4gd2UgZG8gdGhpcz8gQmVjYXVzZSB0aGUgKmNvbHVtbnMqIG9mIHRoZSBleHByZXNzaW9uIG1hdHJpeCBhcmUgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlICpyb3dzKiBvZiB0aGUgbWV0YWRhdGEKICAgICsgZG9uJ3QgYmVsaWV2ZSBtZT8gc2VlIGJlbG93Li4uCi0gdGhpcyBpc24ndCBhIGNvaW5jaWRlbmNlLiB0aGUgZGF0YSBoYXZlIGJlZW4gY2FyZWZ1bGx5IGN1cmF0ZWQgdG8gZW5zdXJlIHRoYXQgdGhpcyBpcyB0aGUgY2FzZQotIGRhdGEgc3RvcmVkIGluIG9ubGluZSByZXBvc2l0b3JpZXMgYXJlIG9mdGVuIG9yZ2FuaXNlZCBpbiB0aGlzIHdheQoKYGBge3IgZXZhbD1GQUxTRX0KY29sbmFtZXMoZVZhbHVlcykKcm93bmFtZXMobWV0YWRhdGEpCmNvbG5hbWVzKGVWYWx1ZXMpID09IHJvd25hbWVzKG1ldGFkYXRhKQphbGwoY29sbmFtZXMoZVZhbHVlcykgPT0gcm93bmFtZXMobWV0YWRhdGEpKQpgYGAKCi0gd2UgY2FuIHN1YnNldCB0aGUgZXhwcmVzc2lvbiBkYXRhIGFjY29yZGluZyB0byBvdXIgY2xpbmljYWwgZGF0YSwgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBhIGZpbGUKCmBgYHtyIGV2YWw9RkFMU0V9CmhpZ2hHcmFkZUV4cHJlc3Npb24gPC0gZVZhbHVlc1ssd2hpY2gobWV0YWRhdGEkZ3JhZGU9PTMpXQp3cml0ZS5jc3YoaGlnaEdyYWRlRXhwcmVzc2lvbiwgZmlsZT0iaGlnaEdyYWRlVHVtb3Vycy5jc3YiKQoKYGBgCgotIGluIGZhY3QsIHdlIGNhbiBzdWJzZXQgdGhlIGVudGlyZSBgdmR4YCBvYmplY3QgYnkgc2FtcGxlIHN1YnNldHMgaWYgd2Ugd2lzaAoKYGBge3J9Cgp2ZHhbLG1ldGFkYXRhJGdyYWRlPT0zXQoKYGBgCgoKUHJldmlvdXNseSwgd2UgdXNlZCBhIGJveHBsb3QgdG8gdmlzdWFsaXNlIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiBhbGwgZ2VuZXMgaW4gYSBnaXZlbiBzYW1wbGUgdG8gbG9vayBmb3IgdHJlbmRzIGFjcm9zcyB0aGUgZGF0YXNldC4gQW5vdGhlciB1c2UgZm9yIGEgYm94cGxvdCBpcyB0byB2aXN1YWxpc2UgdGhlIGV4cHJlc3Npb24gbGV2ZWwgb2YgYSBwYXJ0aWN1bGFyIGdlbmUgd2l0aCByZXNwZWN0IHRvIHRoZSBzYW1wbGUgbWV0YWRhdGEKCi0gd2UgY2FuIGV4dHJhY3QgdGhlIGNvbHVtbiBvZiBpbnRlcmVzdCB3aXRoIGEgYCRgIGFuZCB1c2UgdGhlICpmb3JtdWxhKiBzeW50YXgKICAgICsgYHRhYmxlYCBpbiB0aGlzIGNhc2Ugd2lsbCB0ZWxsIHVzIGhvdyBtYW55IG9ic2VydmF0aW9ucyBvZiBlYWNoIGNhdGVnb3J5IGFyZSBwcmVzZW50Ci0gUiB3aWxsIGJlIGNsZXZlciBhbmQgY29udmVydCB0aGUgZmFjdG9yIGludG8gYSBgZmFjdG9yYCB0eXBlIGlmIHJlcXVpcmVkCgpgYGB7cn0KCmZhYyA8LSBtZXRhZGF0YSRlcgp0YWJsZShmYWMpCmJveHBsb3QoZVZhbHVlc1sxLF0gfiBmYWMsCiAgICAgICAgeGxhYj0iRVIgU3RhdHVzIiwKICAgICAgICB5bGFiPSJFeHByZXNzaW9uIExldmVsIiwKICAgICAgICBjb2w9Yygic3RlZWxibHVlIiwiZ29sZGVucm9kIikpCmBgYAoKUGVyZm9ybWluZyBhIHRlc3QgdG8gYXNzZXNzIHNpZ25pZmljYW5jZSBmb2xsb3dzIHNpbWlsYXIgc3ludGF4CgotIGB0LnRlc3RgIGlzIHRoZSBnZW5lcmljIGZ1bmN0aW9uIHRvIHBlcmZvcm0gYSB0LXRlc3QsIGFuZCBjYW4gYmUgYWRhcHRlZCB0byBkaWZmZXJlbnQgY2lyY3Vtc3RhbmNlcwogICAgKyBlLmcuIGlmIG91ciBkYXRhIGFyZSBwYWlyZWQsIG9yIG5vdAogICAgKyBzZWUgdGhlIGhlbHAgZm9yIGB0LnRlc3RgIGZvciBtb3JlIGRldGFpbHMKLSBmb3Igbm93LCB3ZSB3aWxsIGdsb3NzIG92ZXIgdGVzdGluZyBhc3N1bXB0aW9ucyBvbiB0aGUgZGF0YSBzdWNoIGFzIHJlcXVpcmluZyBhICpub3JtYWwqIChHYXVzc2lhbikgZGlzdHJpYnRpb24gYW5kIG11bHRpcGxlIHRlc3RpbmcgY29ycmVjdGlvbgogICAgCgpgYGB7cn0KdC50ZXN0KGVWYWx1ZXNbMSxdfmZhYykKYGBgCgojIyBBY2Nlc3NpbmcgZmVhdHVyZSAoZ2VuZSkgaW5mb3JtYXRpb24gCgpXZSBjb3VsZCBiZSBsdWNreSwgYW5kIHRoZSBmaXJzdCByb3cgaW4gdGhlIGV4cHJlc3Npb24gbWF0cml4IGNvdWxkIGJlIG91ciBmYXZvdXJpdGUgZ2VuZSBvZiBpbnRlcmVzdCEgSG93ZXZlciwgdGhpcyBpcyB1bmxpa2VseSB0byBiZSB0aGUgY2FzZSBhbmQgd2Ugd2lsbCBoYXZlIHRvIGZpZ3VyZSBvdXQgd2hpY2ggcm93IHdlIHdhbnQgdG8gcGxvdAoKLSB3ZSBjYW4gdXNlIGFub3RoZXIgYXNwZWN0IG9mIHRoZSBgbmtpYCBvYmplY3Q7IHRoZSAqZmVhdHVyZSBkYXRhKgotIHRoZXJlIGlzIGEgaGFuZHkgYGZEYXRhYCBmdW5jdGlvbiB0byBhY2Nlc3MgdGhlc2UgZGF0YQotIGFnYWluLCB0aGlzIGdpdmVzIHVzIGEgKmRhdGEgZnJhbWUqCi0gdGhpcyBpcyBhIHByZS1idWlsdCB0YWJsZSBzdXBwbGllZCB3aXRoIHRoZSBkYXRhc2V0CiAgICArIGxhdGVyIGluIHRoZSBjb3Vyc2Ugd2Ugd2lsbCBsb29rIGF0IHVzaW5nIG9ubGluZSBzZXJ2aWNlcyBhbmQgZGF0YWJhc2VzIGZvciBhbm5vdGF0aW9uIGFuZCBjb252ZXJ0aW5nIGJldHdlZW4gaWRlbnRpZmllcnMKCmBgYHtyfQpmZWF0dXJlcyA8LSBmRGF0YSh2ZHgpCmNsYXNzKGZlYXR1cmVzKQpmZWF0dXJlc1sxOjEwLF0KCmBgYAoKQXMgd2Uga25vdywgZ2VuZSBzeW1ib2xzIChtb3JlLWNvbW1vbiBnZW5lIG5hbWVzKSBjYW4gYmUgYWNjZXNzZWQgdXNpbmcgdGhlIGAkYCBzeW50YXg7IHJldHVybmluZyBhIHZlY3RvcgoKYGBge3IgZXZhbD1GQUxTRX0KZmVhdHVyZXMkR2VuZS5zeW1ib2wKYGBgCgpXZSBjb3VsZCBpbnNwZWN0IHRoZSBkYXRhIGZyYW1lIG1hbnVhbGx5IChlLmcuIHVzaW5nIGBWaWV3KGZlYXR1cmVzKWApIGFuZCBpZGVudGZ5IHRoZSByb3cgbnVtYmVyIGNvcnJlc3BvbmRpbmcgdG8gb3VyIGdlbmUgb2YgaW50ZXJlc3QuIEhvd2V2ZXIsIGFzIGFzcGlyaW5nIFIgcHJvZ3JhbW1lcnMsIHRoZXJlIGlzIGEgYmV0dGVyIHdheQoKLSBgPT1gIHRvIHRlc3QgZm9yIGV4YWN0IG1hdGNoaW5nCi0gYG1hdGNoYCB3aWxsIHJldHVybiB0aGUgKmluZGV4KiBvZiB0aGUgZmlyc3QgbWF0Y2gKLSBgZ3JlcGAgY2FuIGJlIHVzZWQgZm9yIHBhcnRpYWwgbWF0Y2hlcwotIGVhY2ggb2YgdGhlIGFib3ZlIHdpbGwgZ2l2ZSBhbiAqdmVjdG9yKiB0aGF0IGNhbiBiZSB1c2VkIHRvIHN1YnNldCB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMKCmBgYHtyfQp3aGljaChmZWF0dXJlcyRHZW5lLnN5bWJvbCA9PSAiQlJDQTEiKQptYXRjaCgiQlJDQTEiLGZlYXR1cmVzJEdlbmUuc3ltYm9sKQpncmVwKCJCUkNBMSIsZmVhdHVyZXMkR2VuZS5zeW1ib2wpCmdyZXAoIkJSQ0EiLGZlYXR1cmVzJEdlbmUuc3ltYm9sKQoKCmBgYAoKCioqKioqKgoqKioqKioKKioqKioqCgoKIyMgRXhlcmNpc2UKCi0gVmVyaWZ5IHRoYXQgdGhlIHJvd3Mgb2YgdGhlIGZlYXR1cmUgbWF0cml4IGFuZCB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMgYXJlIGluIHRoZSBzYW1lIG9yZGVyCi0gRmluZCB0aGUgcm93IGNvcnJlc3BvbmRpbmcgdG8geW91ciBmYXZvdXJpdGUgZ2VuZQogICAgKyBpZiB5b3UgZG9uJ3QgaGF2ZSBvbmUsIHRyeSBgRVNSMWAKICAgICsgaWYgeW91IGZpbmQgbXVsdGlwbGUgbWF0Y2hlcywgcGljayB0aGUgZmlyc3Qgb25lIHRoYXQgb2NjdXJzCi0gRG9lcyB0aGUgZXhwcmVzc2lvbiBsZXZlbCBvZiB0aGlzIGdlbmUgYXBwZWFyIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCB0aGUgRVIgc3RhdHVzPwoKKioqKioqCioqKioqKgoqKioqKioKCmBgYHtyfQoKCgpgYGAKCgojIyBUZXN0aW5nIGFsbCBnZW5lcyBmb3Igc2lnbmlmaWNhbmNlCgpMYXRlciBpbiB0aGUgY291cnNlIHdlIHdpbGwgc2VlIGhvdyB0byBleGVjdXRlIGEgKmRpZmZlcmVudGlhbCBleHByZXNzaW9uKiBhbmFseXNpcyBmb3IgUk5BLXNlcSBkYXRhLCBhbmQgZGlzY3VzcyBzb21lIG9mIHRoZSBpc3N1ZXMgc3Vycm91bmRpbmcgdGhpcy4gRm9yIG5vdyB3ZSB3aWxsIHBlcmZvcm0gYSBzaW1wbGUgdHdvLXNhbXBsZSB0LXRlc3QgZm9yIGFsbCBnZW5lcyBpbiBvdXIgc3R1ZHksIGFuZCBkZXJpdmUgYSByZXN1bHRzIHRhYmxlCgotIGZpcnN0bHksIHdlIGxvYWQgdGhlIGBnZW5lZmlsdGVyYCBwYWNrYWdlIHdoaWNoIGhhcyBhIHZlcnkgY29udmVuaWVudCBmdW5jdGlvbiBmb3IgcGVyZm9ybWluZyBtYW55IHQgdGVzdHMgaW4gcGFyYWxsZWwKLSBgcm93dHRlc3RzYCB3aWxsIHJ1biBhIHQtdGVzdCBmb3IgZWFjaCByb3cgaW4gYSBnaXZlbiBtYXRyaXggYW5kIHByb2R1Y2UgYW4gb3V0cHV0IHRhYmxlCiAgICArIGBzdGF0aXN0aWNgOyB0ZXN0IHN0YXRpc3RpYwogICAgKyBgZG1gOyBkaWZmZXJlbmNlIGluIG1lYW5zCiAgICArIGBwLnZhbHVlYDsgdGhlIHAtdmFsdWUKLSBgcm93dHRlc3RzYCBleHBlY3RzIGEgKmZhY3RvciogYXMgdGhlIHNlY29uZCBhcmd1bWVudCwgc28gd2UgaGF2ZSB0byB1c2UgYGFzLmZhY3RvcmAKICAgICsgYXMgdXN1YWwsIHdlIGNhbiBnZXQgaGVscCBieSBkb2luZyBgP3Jvd3R0ZXN0c2AKICAgIApgYGB7cn0KbGlicmFyeShnZW5lZmlsdGVyKQp0c3RhdHMgPC0gcm93dHRlc3RzKGVWYWx1ZXMsIGFzLmZhY3RvcihtZXRhZGF0YSRlcikpCndoaWNoKGlzLm5hKGVWYWx1ZXNbMSxdKSkKaGVhZCh0c3RhdHMpCmhpc3QodHN0YXRzJHN0YXRpc3RpYykKCmBgYAoKVGhlIHJvd3MgYXJlIG9yZGVyZWQgaW4gdGhlIHNhbWUgd2F5IGFzIHRoZSBpbnB1dCBtYXRyaXgKCi0gdG8gY2hhbmdlIHRoaXMgdG8gaW5jcmVhc2luZyBzaWduaWZpY2FuY2Ugd2UgY2FuIHVzZSB0aGUgYG9yZGVyYCBmdW5jdGlvbgotIHdoZW4gZ2l2ZW4gYSB2ZWN0b3IsIGBvcmRlcmAgd2lsbCByZXR1cm4gYSB2ZWN0b3Igb2YgdGhlIHNhbWUgbGVuZ3RoIGdpdmluZyB0aGUgcGVybXV0YXRpb24gdGhhdCByZWFycmFuZ2VzIHRoYXQgdmVjdG9yIGludG8gYXNjZW5kaW5nIG9yIGRlc2NlbmRpbmcgb3JkZXIKICAgICsgaG93IGNhbiB3ZSBmaW5kIG91dCB3aGF0IHRoZSBkZWZhdWx0IHdheSBvZiBvcmRlcmluZyBpcz8KCmBgYHtyfQoKeCA8LSBjKDksIDMsIDQsIDIsIDEsIDYsNSwgMTAsOCw3KQp4Cm9yZGVyKHgpCnhbb3JkZXIoeCldCmBgYAoKLSBzbyBpZiB3ZSB3YW50IHRvIG9yZGVyIGJ5IHAtdmFsdWUgd2UgZmlyc3QgdXNlIG9yZGVyIG9uIHRoZSBwLXZhbHVlIHZlY3RvcgotIHRoaXMgY2FuIHRoZW4gYmUgdXNlZCB0byByZS1hcnJhbmdlIHRoZSByb3dzIG9mIHRoZSB0YWJsZQoKYGBge3J9CnRzdGF0c1tvcmRlcih0c3RhdHMkcC52YWx1ZSxkZWNyZWFzaW5nID0gRkFMU0UpLF0KCmBgYAoKLSBub3RlIHRoYXQgd2UgY291bGQgc2F2ZSB0aGUgcmVzdWx0IG9mIGBvcmRlcmAgYXMgYSB2YXJpYWJsZSBhbmQgdGhlbiB1c2UgaW4gdGhlIHN1YnNldHRpbmcKICAgICsgZWl0aGVyIGlzIGZpbmUsIGl0J3MgYSBtYXR0ZXIgb2YgcGVyc29uYWwgcHJlZmVyZW5jZQoKYGBge3J9CnAub3JkZXIgPC0gb3JkZXIodHN0YXRzJHAudmFsdWUsZGVjcmVhc2luZyA9IEZBTFNFKQp0c3RhdHNbcC5vcmRlcixdCgogIApgYGAKCgpIb3dldmVyLCB0aGUgdGFibGUgd2UgZ2V0IGlzbid0IGltbWVkaWF0ZWx5IHVzZWZ1bCB1bmxlc3Mgd2UgY2FuIHJlY29nbmlzZSB0aGUgbWFudWZhY3R1cmVyIHByb2JlIElEcwoKLSB0byBwcm92aWRlIGV4dHJhIGFubm90YXRpb24gdG8gdGhlIHRhYmxlLCB3ZSBjYW4gKmNvbHVtbiBiaW5kKiAoYGNiaW5kYCkgdGhlIGNvbHVtbnMgb2YgdGVzdCBzdGF0aXN0aWMgd2l0aCB0aG9zZSBmcm9tIHRoZSBmZWF0dXJlIG1hdHJpeAotIGJlIGNhcmVmdWwgdGhvdWdoLCB3ZSBjYW4gb25seSBkbyB0aGlzIGluIGNhc2VzIHdoZXJlIHRoZSByb3dzIGFyZSBpbiBkaXJlY3QgY29ycmVzcG9uZGVuY2UKCgpgYGB7cn0KYWxsKHJvd25hbWVzKHRzdGF0cykgPT0gcm93bmFtZXMoZmVhdHVyZXMpKQoKdHN0YXRzLmFubm90YXRlZCA8LSBjYmluZCh0c3RhdHMsIGZlYXR1cmVzWyxjKCJHZW5lLnN5bWJvbCIsIkVudHJlekdlbmUuSUQiLCJDaHJvbW9zb21lLmxvY2F0aW9uIildKQp0c3RhdHMuYW5ub3RhdGVkICAgICAgICAgICAgICAgICAgICAgICAgCmBgYAoKTm93IHdoZW4gd2Ugb3JkZXIgYnkgcC12YWx1ZSwgdGhlIGV4dHJhIGNvbHVtbnMgdGhhdCB3ZSBqdXN0IGFkZGVkIGFsbG93IHVzIHRvIGludGVycHJldCB0aGUgcmVzdWx0cyBtb3JlIGVhc2lseQoKYGBge3J9CnRzdGF0cy5vcmRlcmVkIDwtIHRzdGF0cy5hbm5vdGF0ZWRbb3JkZXIodHN0YXRzJHAudmFsdWUsZGVjcmVhc2luZyA9IEZBTFNFKSxdCnRzdGF0cy5vcmRlcmVkCgpgYGAKCldlIGNhbiBhbHNvIHF1ZXJ5IHRoaXMgdGFibGUgdG8gbG9vayBmb3Igb3VyIGZhdm91cml0ZSBnZW5lcyBvZiBpbnRlcmVzdAoKLSBgJWluJWAgaXMgYSBzaW1wbGlmaWVkIHdheSB0byBwZXJmb3JtIG1hdGNoZXMgdG8gbXVsdGlwbGUgaXRlbXMgaW4gYSB2ZWN0b3IKCmBgYHtyfQoKdHN0YXRzLm9yZGVyZWRbZ3JlcCgiRVNSMSIsdHN0YXRzLm9yZGVyZWQkR2VuZS5zeW1ib2wpLF0KdHN0YXRzLm9yZGVyZWRbdHN0YXRzLm9yZGVyZWQkR2VuZS5zeW1ib2wgJWluJSBjKCJFU1IxIiwiR0FUQTMiLCJGT1hBMSIpLF0KCgpgYGAKCgoKKioqKioqCioqKioqKgoqKioqKioKCgojIyBFeGVyY2lzZQoKLSBGcm9tIHRoZSBhbm5vdGF0ZWQgdGFibGUgYWJvdmUsIHNlbGVjdCBhbGwgZ2VuZXMgd2l0aCBwLXZhbHVlcyBsZXNzIHRoYW4gMC4wNQotIFdyaXRlIHRoaXMgZGF0YSBmcmFtZSBhcyBhIGBjc3ZgIGZpbGUKLSAoT1BUSU9OQUwpIFVzZSB0aGUgYHAuYWRqdXN0YCB0byBwcm9kdWNlIGEgdmVjdG9yIG9mIHAtdmFsdWVzIHRoYXQgYXJlIGFkanVzdGVkLiBBZGQgdGhpcyBhcyBhbiBleHRyYSBjb2x1bW4gdG8geW91ciB0YWJsZSBvZiByZXN1bHRzIGFuZCB3cml0ZSBhcyBhIGZpbGUKCioqKioqKgoqKioqKioKKioqKioqCgpgYGB7cn0KCgpgYGAKCgojIE1vcmUgYWR2YW5jZWQgdmlzdWFsaXNhdGlvbgoKIyMgQ2x1c3RlcmluZwoKCgotIENsdXN0ZXJpbmcgbGVhZHMgdG8gcmVhZGlseSBpbnRlcnByZXRhYmxlIGZpZ3VyZXMgYW5kIGNhbiBiZSBoZWxwZnVsIGZvciBpZGVudGlmeWluZyBwYXR0ZXJucyBpbiB0aW1lIG9yIHNwYWNlLgotIGl0IGlzIGtub3duIGFzIGFuICp1bnN1cGVydmlzZWQqCiAgKyBjbGFzc2VzIHVua25vd24sIHdhbnQgdG8gZGlzY292ZXIgdGhlbSBmcm9tIHRoZSBkYXRhCiAgKyBhbHRob3VnaCB3ZSBjYW4gY29tcGFyZSB0byB0aGUgY2xhc3NlcyB3ZSBrbm93IGFib3V0IGFmdGVyd2FyZHMKLSBXZSBjYW4gY2x1c3RlciBzYW1wbGVzIChjb2x1bW5zKQogICAgKyBlLmcuIGlkZW50aWZpY2F0aW9uIG9mIG5ldyAvIHVua25vd24gdHVtb3IgY2xhc3NlcyB1c2luZyBnZW5lIGV4cHJlc3Npb24gcHJvZmlsZXMKLSBXZSBjYW4gY2x1c3RlciBnZW5lcyAocm93cykKICAgICsgZS5nLiB1c2luZyBsYXJnZSBudW1iZXJzIG9mIHllYXN0IGV4cGVyaW1lbnRzIHRvIGlkZW50aWZ5IGdyb3VwcyBvZiBjby1yZWd1bGF0ZWQgZ2VuZXMKLSB3ZSBjYW4gY2x1c3RlciBnZW5lcyB0byByZWR1Y2UgcmVkdW5kYW5jeSAoaS5lLiB2YXJpYWJsZSBzZWxlY3Rpb24pIGluIHByZWRpY3RpdmUgbW9kZWxzCi0gaXQgaGFzIGJlZW4gdXNlZCB0byBnb29kIGVmZmVjdCBmb3IgbWFueSB5ZWFycyAKCiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcvbWljcm9hcnJheS1hbmFseXNpcy9tYXN0ZXIvaW1hZ2VzL3Blcm91LnBuZykKClRoZSBwcm9jZWR1cmUgaXMgCgotIFByZXByb2Nlc3MgdGhlIGRhdGEKLSBDaG9vc2UgYSBkaXNzaW1pbGFyaXR5IG1lYXN1cmUKLSBDYWxjdWxhdGUgYSBkaXN0YW5jZSBtYXRyaXgKLSBDaG9vc2UgYSBjbHVzdGVyIGFsZ29yaXRobQotIENsdXN0ZXIgdGhlIHNhbXBsZXMKLSBWaXN1YWxpc2UKCkZvcnR1bmF0ZWx5LCB0aGVyZSBhcmUgZWFzeSB0byB1c2UgZnVuY3Rpb25zIGZvciBjYWxjdWxhdGluZyB0aGUgZGlzdGFuY2UgbWF0cml4IChgZGlzdGApIGFuZCBwZXJmb3JtaW5nIHRoZSBjbHVzdGVyaW5nIChgaGNsdXN0YCkuIFRoZSB0cmlja3kgcGFydCBpcyBjaG9vc2luZyB0aGUgY29ycmVjdCBzdWJzZXQgb2YgdGhlIGRhdGEgYW5kIGludGVycHJldGluZyB0aGUgcmVzdWx0cwoKLSBpZiB5b3UgbG9vayBhdCB0aGUgaGVscCBmb3IgYGRpc3RgIChgP2Rpc3RgKSB5b3Ugd2lsbCBzZWUgdGhhdCBpdCB0YWtlcyBhIG51bWVyaWMgbWF0cml4IGFzIGl0cyBhcmd1bWVudAotIGhvdyB5b3UgZGVmaW5lIHRoaXMgbWF0cml4IGlzIHVwIHRvIHlvdS4uLgotIGFsc28gbm90aWNlIHRoYXQgaXQgY29tcHV0ZXMgZGlzdGFuY2VzIGJldHdlZW4gdGhlICoqKnJvd3MqKiogb2YgdGhlIGRhdGEgbWF0cml4Ci0gdGhlIG1vc3QgY29tbW9uIGFwcGxpY2F0aW9uIG9mIGNsdXN0ZXJpbmcgaXMgdG8gZGlzY292ZXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuICpzYW1wbGVzKiBpbiBvdXIgZGF0YXNldAotIHdoaWNoIGFyZSB0aGUgbWF0cml4ICpjb2x1bW5zKiBpbiBvdXIgY2FzZQotIHNvIHdlIG5lZWQgdG8gKnRyYW5zcG9zZSogb3VyIGV4cHJlc3Npb24gdmFsdWVzIGJlZm9yZSB1c2luZyB0aGlzIGZ1bmN0aW9uCiAgICAKYGBge3J9Cj9kaXN0CmRpc3RNYXQgPC0gZGlzdCh0KGVWYWx1ZXMpKQoKCmBgYAoKCgoKYGBge3J9CmNsdXN0IDwtIGhjbHVzdChkaXN0TWF0KQpwbG90KGNsdXN0KQpgYGAKCldlIGNhbiBzZWUgd2hpY2ggc2FtcGxlcyBhcmUgZ3JvdXBlZCB0b2dldGhlciBieSBjdXR0aW5nIHRoZSB0cmVlIGF0IGEgcGFydGljdWxhciBoZWlnaHQsIG9yIHRvIGdpdmUgYSBwcmUtZGV0ZXJtaW5lZCBudW1iZXIgb2YgZ3JvdXBzCgpgYGB7ciBlY2hvPUZBTFNFfQpoY2EgPC0gaGNsdXN0KGRpc3RNYXQpCnBsb3QoaGNhKQpyZWN0LmhjbHVzdChoY2EsaD0yNTApCmBgYAoKCgpgYGB7cn0KY3V0cmVlKGhjbHVzdChkaXN0TWF0KSwgaD0yNTApCmN1dHJlZShoY2x1c3QoZGlzdE1hdCksIGs9MikKYGBgCgoqKioqKioKKioqKioqCioqKioqKgoKCiMjIEV4ZXJjaXNlCgotIEhvdyBtYW55IGdyb3VwcyBhcmUgZm9ybWVkIGlmIHdlIGN1dCB0aGUgdHJlZSBhdCBhIGhlaWdodCBvZiAyNTA/Ci0gSG93IG1hbnkgc2FtcGxlcyBhcmUgaW4gZWFjaCBncm91cAotIENyZWF0ZSBhIGRhdGEgZnJhbWUgY29udGFpbmluZyB0aGUgbWV0YSBkYXRhIGZvciB0aGUgc2FtcGxlcyBiZWxvbmdpbmcgdG8gdGhlIGxhcmdlc3QgZ3JvdXAKCmBgYHtyIGVjaG89RkFMU0V9CgpgYGAKCgoKKioqKioqCioqKioqKgoqKioqKioKCgoKClRoZXJlIGFyZSB0d28gdmVyeSBpbXBvcnRhbnQgcG9pbnRzIHRvIGNvbnNpZGVyIHdoZW4gcGVyZm9ybWluZyBjbHVzdGVyaW5nCgotIGl0IHdpbGwgYWx3YXlzIGRvICpzb21ldGhpbmcqOyBpdCBjYW4gbmV2ZXIgbm90IHdvcmsKLSBkaXN0YW5jZSBiZXR3ZWVuIHNhbXBsZXMgaXMgZ2l2ZW4gb24gdGhlIHkgYXhpcywgbm90IHRoZSB4LWF4aXMKICArIGp1c3QgYmVjYXVzZSB0d28gc2FtcGxlcyBhcmUgbmV4dCB0byBlYWNoIG90aGVyIG9uIHRoZSB4LWF4aXMgZG9lcyBub3QgbWVhbiB0aGV5IGFyZSBzaW1pbGFyCiAgKyB0aGluayBvZiBhIG1vYmlsZSBpbiBhIGNoaWxkJ3MgbnVyc2VyeS4uLgoKIVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9iaW9pbmZvcm1hdGljcy1jb3JlLXNoYXJlZC10cmFpbmluZy9taWNyb2FycmF5LWFuYWx5c2lzL21hc3Rlci9pbWFnZXMvc3RvY2staWxsdXN0cmF0aW9uLTY0Nzc5MzUxLWhhbmdpbmctYmFieS1tb2JpbGUtc2N1bHB0dXJlLXN0YXJzLWFuZC1tb29uLWRyYXdpbmcuanBnKQoKIyMgSGVhdG1hcHMKClBlcmhhcHMgYSBtb3JlIGNvbW1vbiB3YXkgb2YgZGlzcGxheWluZyBhIGhlYXRtYXAgaXMgYXMgcGFydCBvZiBhIGhlYXRtYXAgd2hpY2ggc2hvd3MgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGJvdGggZ2VuZXMgYW5kIHNhbXBsZXMKCkJ1dCBmaXJzdCB3ZSBuZWVkIHRvIHJlc3RyaWN0IHRoZSBnZW5lcwoKLSB3ZSBjYW4ndCBkaXNwbGF5IGFsbCAzMCwwMDAgZ2VuZXMgaW4gb25lIHBpY3R1cmUKLSB0byBkbyB0aGlzIHdlIHBpY2sgdGhlIGdlbmVzIHdpdGggdGhlIHRoZSBsb3dlc3QgcC12YWx1ZSBmcm9tIG91ciBwcmV2aW91cyBhbmFseXNpcwoKYGBge3J9Cm1vc3RERSA8LSBvcmRlcih0c3RhdHMkcC52YWx1ZSxkZWNyZWFzaW5nID0gRkFMU0UpWzE6MzBdCmBgYAoKTGV0J3MganVzdCBnbyBhaGVhZCBhbmQgcnVuIHRoZSBmdW5jdGlvbiBgaGVhdG1hcGAKCmBgYHtyfQpoZWF0bWFwKGVWYWx1ZXNbbW9zdERFLF0pCmBgYAoKQ29vbCEgVGhlcmUgc2VlbXMgdG8gYmUgc29tZSBraW5kIG9mIHN0cnVjdHVyZSB0byB0aGUgZGF0YS4gSG93ZXZlciwgdGhlIHBsb3QgaXMgYSBsaXR0bGUgdWdseS4gRm9ydHVuYXRlbHkgdGhlcmUgYXJlIHBsZW50eSBvZiBvcHRpb25zIGluIHRoZSBmdW5jdGlvbiB0aGF0IHdlIGNhbiB1c2UgdG8gdHdlYWsgdGhlIHBsb3QgKGA/aGVhdG1hcGApCgotIHdlIGNhbiBzcGVjaWZ5IGEgYmxvY2sgb2YgY29sb3VyIHRvIGdvIHVuZGVybmVhdGggZWFjaCBzYW1wbGUgdG8gaW5kaWNhdGUgaWYgYmVsb25nIHRvIHRoZSAiKkVSIFBvc2l0aXZlKiIgb3IgIipFUiBOZWdhdGl2ZSoiIGdyb3VwCi0gdGhpcyBoYXMgdG8gYmUgYSB2ZWN0b3Igd2l0aCB0aGUgc2FtZSBsZW5ndGggYXMgdGhlIG51bWJlciBvZiBzYW1wbGVzCi0gd2UgY2FuIGJ1aWxkIHN1Y2ggYSB2ZWN0b3IgaW4gdHdvIHN0YWdlcwogICAgKyBmaXJzdCByZXBlYXRpbmcgb25lIGNvbG91ciAoZS5nLiBgYmx1ZWApIGZvciBlYWNoIHNhbXBsZXMKICAgICsgdGhlbiByZXBsYWNpbmcgZW50cmllcyBjb3JyZXNwb25kaW5nIHRvIGEgcGFydGljdWxhciBzYW1wbGUgdHlwZSB3aXRoIGEgZGlmZmVyZW50IGNvbG91cgoKYGBge3J9CnNhbXBDb2xvdXJzIDwtIHJlcCgiYmx1ZSIsbmNvbChlVmFsdWVzKSkKc2FtcENvbG91cnNbbWV0YWRhdGEkZXIgPT0gMCBdIDwtICJ5ZWxsb3ciCmhlYXRtYXAoZVZhbHVlc1ttb3N0REUsXSwKICAgICAgICBDb2xTaWRlQ29sb3JzID0gc2FtcENvbG91cnMpCmBgYAoKVGhlIGNvbG91ciBzY2hlbWUgZm9yIHRoZSBjZWxscyBpbiB0aGUgaGVhdG1hcCBjYW4gYWxzbyBiZSBjaGFuZ2VkLiBUaGUgYFJDb2xvckJyZXdlcmAgcGFja2FnZSBoYXMgYSBudW1iZXIgb2YgcHJlLWRlZmluZWQgcGFsZXR0ZXMsIHNldmVyYWwgb2Ygd2hpY2ggYXJlIHN1aXRhYmxlIGZvciB0aG9zZSB3aXRoIGNvbG91ci1ibGluZG5lc3MgKHRoZSBjb21tb25seSB1c2VkIHJlZC1ncmVlbiBzY2FsZSBmb3IgaGVhdG1hcHMgaXMgY2xlYXJseSBhIGJhZCBjaG9pY2UpLgoKYGBge3J9CmRpc3BsYXkuYnJld2VyLmFsbChjb2xvcmJsaW5kRnJpZW5kbHkgPSBUUlVFKQpgYGAKCkEgZ29vZCBjaG9pY2UgZm9yIGEgaGVhdG1hcCBpcyBhIHBhbGV0dGUgd2hpY2ggZGl2ZXJnZXMgZnJvbSBvbmUgZXh0cmVtZSB0byBhbm90aGVyLiBJZiB3ZSB1c2UgdGhlIGBSZEJ1YCBwYWxldHRlLCBzYW1wbGVzIHdpdGggbG93ZXIgZ2VuZSBleHByZXNzaW9uIHdpbGwgYmUgc2hvd24gaW4gKnJlZCoKCi0gdGhlIGZ1bmN0aW9uIGBicmV3ZXIucGFsYCBpcyB1c2VkIHRvIGNyZWF0ZSB0aGUgcGFsZXR0ZSB3aXRoIGEgY2VydGFpbiBudW1iZXIgb2YgY29sb3VycwoKYGBge3J9CmxpYnJhcnkoUkNvbG9yQnJld2VyKQpyYlBhbCA8LSBicmV3ZXIucGFsKDEwLCAiUmRCdSIpCmhlYXRtYXAoZVZhbHVlc1ttb3N0REUsXSwKICAgICAgICBDb2xTaWRlQ29sb3JzID0gc2FtcENvbG91cnMsCiAgICAgICAgY29sPXJiUGFsKQpgYGAKCgoqKioqKioKKioqKioqCioqKioqKgoKCiMjIEV4ZXJjaXNlCgotIE1vZGlmeSB0aGUgbGFiZWxzIG9uIHRoZSBzaWRlIG9mIHRoZSBoZWF0bWFwIHNvIHRoZSBnZW5lIG5hbWVzIGFyZSBkaXNwbGF5ZWQgcmF0aGVyIHRoYW4gcHJvYmUgbmFtZXMKLSBZb3Ugd2lsbCBuZWVkIHRvIGNoZWNrIHRoZSBoZWxwIHBhZ2Ugb2YgYGhlYXRtYXBgIHRvIHNlZSB3aGljaCBhcmd1bWVudCBuZWVkcyB0byBiZSBzcGVjaWZpZWQgdG8gY2hhbmdlIHRoZSBsYWJlbHMKLSAoT3B0aW9uYWwpIENhbiB5b3UgcmVtb3ZlIHRoZSBzYW1wbGVzIG5hbWVzIG9uIHRoZSBib3R0b20gb2YgdGhlIGhlYXRtYXA/CgpgYGB7ciBlY2hvPUZBTFNFfQpmZWF0dXJlc1t0b3BWYXIsXQpoZWF0bWFwKGVWYWx1ZXNbbW9zdERFLF0sCiAgICAgICAgQ29sU2lkZUNvbG9ycyA9IHNhbXBDb2xvdXJzLAogICAgICAgIGNvbD1yYlBhbCwKICAgICAgICBsYWJSb3cgPSBmZWF0dXJlcyRHZW5lLnN5bWJvbFttb3N0REVdLAogICAgICAgIGxhYkNvbD0iIikKYGBgCgoKKioqKioqCioqKioqKgoqKioqKioKCk9mIGNvdXJzZSB3ZSBzaG91bGQgbm90IGJlIHN1cnByaXNlZCB0aGF0IG91ciBoZWF0bWFwIHNob3dlZCBnb29kIHNlcGFyYXRpb247IHdlIHVzZWQgZ2VuZXMgdGhhdCB3ZSBwcmV2aW91c2x5IGZvdW5kIHRvIGJlIHN0YXRpc3RpY2FsbHkgZGlmZmVyZW50IGJldHdlZW4gc2FtcGxlcy4KCi0gdGhlIGhlYXRtYXAgZnVuY3Rpb24gcmVxdWlyZXMgYSBudW1lcmljIG1hdHJpeCBhcyBpdHMgYXJndW1lbnQKICAgICsgaXQgZG9lc24ndCByZWFsbHkgY2FyZSBob3cgdGhpcyBtYXRyaXggd2FzIGdlbmVyYXRlZAotIHNvIGl0J3MgeW91ciBjaG9pY2UgYWJvdXQgd2hhdCBnZW5lcyAvIHNhbXBsZXMgYXJlIHByZXNlbnQgaW4gdGhlIG1hdHJpeAotIAoKCmBgYHtyfQpteS5nZW5lcyA8LSB3aGljaChmZWF0dXJlcyRDaHJvbW9zb21lLmxvY2F0aW9uPT0iOHAxMiIpCm15LmdlbmVzCmhlYXRtYXAoZVZhbHVlc1tteS5nZW5lcyxdLAogICAgICAgIENvbFNpZGVDb2xvcnMgPSBzYW1wQ29sb3VycywKICAgICAgICBjb2w9cmJQYWwsCiAgICAgICAgbGFiUm93ID0gZmVhdHVyZXMkR2VuZS5zeW1ib2xbbXkuZ2VuZXNdLAogICAgICAgIGxhYkNvbD0iIikKCmBgYAoKT3IgZ2l2ZSBhIHByZS1kZWZpbmVkIGxpc3Qgb2YgZ2VuZXMKCmBgYHtyfQoKbXkuZ2VuZXMgPC0gZmVhdHVyZXMkR2VuZS5zeW1ib2wgJWluJSBjKCJDTElDNiIsIlRGRjMiLCJQRFpLMSIsIlNDVUJFMiIsIkNZUDJCNiIsIkhPWEIxMyIsIk5BVDEiLCJMWTZEIiwiU0xDN0EyIikKaGVhdG1hcChlVmFsdWVzW215LmdlbmVzLF0sCiAgICAgICAgQ29sU2lkZUNvbG9ycyA9IHNhbXBDb2xvdXJzLAogICAgICAgIGNvbD1yYlBhbCwKICAgICAgICBsYWJSb3cgPSBmZWF0dXJlcyRHZW5lLnN5bWJvbFtteS5nZW5lc10sCiAgICAgICAgbGFiQ29sPSIiKQoKCmBgYAoKCgoKCgojIyMgKE9wdGlvbmFsKSBEb3dubG9hZGluZyBkYXRhIGZyb20gR0VPCgpGb3IgdGhvc2UgdGhhdCBtaWdodCBiZSBpbnRlcmVzdGVkLCBoZXJlIGlzIGFuIG92ZXJ2aWV3IG9mIHRoZSBjb21tYW5kcyBvbmUgbWlnaHQgdXNlIHRvIGRvd25sb2FkIGRhdGEgZnJvbSB0aGUgR2VuZSBFeHByZXNzaW9uIE9tbmlidXMuIFRoZSBgR0VPcXVlcnlgIHBhY2thZ2UgaXMgdXNlZCBhbmQgeW91IGhhdmUgdG8ga25vdyB0aGUgKmFjY2Vzc2lvbiogbnVtYmVyIG9mIHRoZSBkYXRhc2V0IHRoYXQgeW91IHdhbnQgdG8gYW5hbHlzZS4gVHlwaWNhbGx5IHRoaXMgd291bGQgYmUgc3RhdGVkIGluIGEgcHVibGljYXRpb24gaW4gdGhlIGZvcm0gKkdTRS4uLi4qLgoKYGBge3IgZXZhbD1GQUxTRX0KIyMgSWYgbm90IGluc3RhbGxlZCBhbHJlYWR5LCBpbnN0YWxsIEdFT3F1ZXJ5IHdpdGgKIyMgc291cmNlKCJodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiMjIGJpb2NMaXRlKCJHRU9xdWVyeSIpCgoKbGlicmFyeShHRU9xdWVyeSkKbXlkYXRhIDwtIGdldEdFTygiR1NFMTcyOSIpCgpgYGAKClRoZSBgbXlkYXRhYCBvYmplY3QgdGhhdCBpcyBjcmVhdGVkIGlzIGEgbGlzdCBpbiBSLiBUaGlzIGlzIHVzZWQgYmVjYXVzZSBzb21lIHN0dWRpZXMgbWlnaHQgaW52b2x2ZSBkYXRhIGdlbmVyYXRlZCBvbiBkaWZmZXJlbnQgcGxhdGZvcm1zLCB3aGljaCBoYXZlIHNlcGFyYXRlIGFubm90YXRpb25zLgoKYGBge3J9Cmxlbmd0aChteWRhdGEpCm15ZGF0YSA8LSBteWRhdGFbWzFdXQpteWRhdGEKYm94cGxvdChleHBycyhteWRhdGEpLG91dGxpbmU9RkFMU0UpCmBgYAoKCgpGb3IgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBvbiBtaWNyb2FycmF5cywgW3NlZSBvdXIgY291cnNlXShodHRwOi8vYmlvaW5mb3JtYXRpY3MtY29yZS1zaGFyZWQtdHJhaW5pbmcuZ2l0aHViLmlvL21pY3JvYXJyYXktYW5hbHlzaXMvKQoK