Preliminaries

The knitr R package

knitr() is the R package that generates the report from R Markdown. We can create reports as Word doc, PDF, and HTML files.

An R package bundles together code, data, documentation, and tests, and is easy to download and share with others.

About this activity

You will load and examine R dataframe objects that contain data from over 1,000 breast cancer patients from The Cancer Genome Atlas. The objects include: * clinical measurements on the patients and the patients’ tumors * gene expression data

The Cancer Genome Atlas or TCGA characterized over 20,000 cancer samples spanning 33 cancer types with genomics.

In 2012, the TCGA Network reported findings of analyses of primary breast cancers in hundreds of patients with multiple experimental platforms (exome sequencing, genomic DNA copy number arrays, DNA methylation, mRNA arrays, microRNA sequencing, and reverse-phase protein arrays.) Here, we will consider the normalized read count expression data for patient-level breast cancer (brca) data from TCGA.


Loading the data

Throughout the course, We will be loading data from R files with the extension .RData. An example is the file TCAG_brca.RData which we examine below.

.RData files are binary files (i.e., not human readable) that can store multiple R objects, such as vectors, lists, matrices, and data frames.

The R objects in .Rdata files are the results of previous R analyses, and the data is in tidy form. Tidy data sets have consistent structure and are easy to manipulate, visualize, and model.

We could spend the entire course on learning how to put data in tidy form, but our goal is the understand and analyze the data, which occurs after tidying the data.


# We simply `load()` TCGA_brca.RData
# The file.path() function tells `load()` where our data lives
# The argument "verbose = TRUE" makes `load()` tell us what R objects are being loaded

load(file.path(data_dir, "TCGA_brca.RData"),verbose=TRUE)   
Loading objects:
  brca_expr_norm_names_df
  brca_gene_df
  brca_clin_df

The objects were named to be be descriptive. * “df” stands for “dataframe” * “brca” stands for “TCGA breast cancer” * “expr” stands for “gene expression data” * “norm” means that the data is normalized to account for variability in the experiments * “names” reminds us that the data has the gene names included * “clin” is short for “clinical”

We looked at brca_clin_df last time.
Today we will also consider brca_expr_norm_names_df.

The Environment tab gives us basic information on the objects that were loaded.

We will use some of the R functions we learned to examine the data in more detail. * dim() tells us the dimensions (# row, # columns) of the objects. * head() shows us the top several rows of the objects. * indexing allows us to look at the rows and columns we choose.

dim(brca_expr_norm_names_df)  # We should get a result consistent
[1] 18351  1083
                              # with what is in the Environment tab

Let’s see what’s in the rows and columns.

brca_expr_norm_names_df[1:5,1:5]     # Inspect the first few rows of the data frame

The first column, “symbol” is the names of the genes. The remaining columns are the expression levels of the genes from different patient samples.

“TCGA-3C-AAAU” is the anonymized identifier for one patient’s sample.

To do analysis with the expression dataframe, we have to remove the “symbol” column so that the matrix contains only the numerical gene expression data. But we want to keep the information from the “symbol” column. R allows us to do this by assigning the symbols as the row names for our matrix.


brca_expr_mat <- as.matrix(brca_expr_norm_names_df[,-1])  # We remove the first column,
                                                          # use the function as.matrix().
                                                          # and assign the result to a new object.

# Now, we'll use the function row.names() to keep the gene symbols.
row.names(brca_expr_mat) <- brca_expr_norm_names_df$symbol

Let’s use indexing to check our the first 5 rows and columns of our matrix.

brca_expr_mat[1:5,1:5]
         TCGA-3C-AAAU TCGA-3C-AALI TCGA-3C-AALJ TCGA-3C-AALK
TSPAN6       188.1840     207.7220    1005.4400    1104.6800
TNMD           0.3447       1.0875      39.8912       1.2412
DPM1         524.2260     809.1350     967.3620     463.3840
SCYL3        325.1410    1558.9400     335.8300     549.2550
C1orf112     124.2940     274.6660     241.6860     218.2250
         TCGA-4H-AAAK
TSPAN6       942.5530
TNMD           4.6809
DPM1         486.3830
SCYL3        416.2850
C1orf112     179.4600

Let’s look at the average value of the expression for the first two genes, TSPAN6 and TNMD.

TSPAN6 codes for the protein Tetraspanin-6. This protein helps signal events that play a role in cell growth and motility.

TNMD codes for the protein Tenomodulin, which is important for the formation of tendons. TNMD is highly expressed in tendons, but lowly expressed in other parts of the body.


# mean() takes the average of a set of values

avg_TSPAN6_expr <- mean(brca_expr_mat[1,])   # We are taking the average of expression values
                                             # for the first row of the matrix

avg_TNMD_expr <- mean(brca_expr_mat[2,])     # We are taking the average of expression values
                                             # for the second row of the matrix


print(paste("Average expression of TSPAN6: ", round(avg_TSPAN6_expr,0)))
[1] "Average expression of TSPAN6:  996"
print(paste("Average expression of TNMD: ", round(avg_TNMD_expr,0)))
[1] "Average expression of TNMD:  24"
# The function paste() allows us to print text and numbers together.
# The function round() lets us choose how many decimal digits we want to show.

It would stink to have to do this for all 18,351 genes.

But we can use the function apply() to apply a function like mean() to all rows or columns of our matrix.

mean.expr <- apply(brca_expr_mat, 1, mean)  # We "apply" the function mean() to all rows.
                                            # The first argument is the matrix.
                                            # The second argument is 1 for "rows".
                                            # The third argument is the function name, `mean()`

as.table(round(mean.expr[1:25],0))
  TSPAN6     TNMD     DPM1    SCYL3 C1orf112      FGR      CFH 
     996       24      699      624      320      187     1246 
   FUCA2     GCLC     NFYA    STPG1   NIPAL3    LAS1L    ENPP4 
    1432      869     1326      215      985     1143      642 
  SEMA3F     CFTR   ANKIB1  CYP51A1    KRIT1    RAD52      BAD 
    2180       19     1614     1945      738      130      853 
    LAP3     CD99   HS3ST1     AOC1 
    2646     2751       91       84 

It’s nice we can calculate all the means at once, but it’s not very helpful in comparing the mean of the different genes.

Let’s try plotting the means as a histogram.

hist(mean.expr, breaks=100, main="Distribution of Gene Expression Values",
  xlab="Mean Expression")


# A histogram is a graphical display of data using bars of different heights. 
# Each bar groups numbers into ranges. Taller bars show that more data falls in that range. 
# The first argument is the data, the second is how bars we want to show, 
# the third is a title for the plot, and the fourth is the name of the x-axis.

Wow! Most of the average expression values are relatively small (less than 200), but there must be a few really large values.

Let’s see how many genes have average expression greater than 10,000.


print(paste("Total values: ", length(mean.expr)))
[1] "Total values:  18351"
print(paste("Num values > 10,000: ", sum(mean.expr > 10000)))
[1] "Num values > 10,000:  201"
print(paste("Pencentage of mean values > 10,000: ", round(sum(mean.expr>10000)/length(mean.expr)*100,1)))
[1] "Pencentage of mean values > 10,000:  1.1"

So there are a few really big numbers. The curve in the histogram has a long right tail.
Nearly all genes have low expression, and very few genes have high expression.

Unfortunately, many analysis functions in R do not work well with this kind of distribution.

Instead, it is very common to assume the data follow a normal (or bell) curve.

In practive,very little real life biological data truly follow a normal curve. It has been established that most analysis works quite well if the data are close enough to a normal curve.

We can make our data more like normal data by doing a log transform. If we take the log of all the epxression values, we put the data more on equally footing.

Expression data is typically logged using base 2.

Log base 2 of 8 is 3 because 2 * 2 * 2 = 8.

Log base 4 of 4 is 2 because 2 * 2 = 4.

Log base 2 of 2 is 1 because 2 = 2.

So the values (8, 4 , 2) are transformed to (3, 2, 1).

Let’s consider some larger numbers:

a <- c(4096, 1024, 512, 64)

b <- log(a,2)

plot(a,b,pch=19,col="red")

Notice how the scale for b is much more compressed than for a.

By taking the log2 of numbers, we make them more similar to each other.


a
[1] 4096 1024  512   64
log(a,2)
[1] 12 10  9  6

There is one caveat. The log2 of 0 is infinity!


log(0,2)
[1] -Inf

But the log2 of 1 is “well-behaved.”

log(1,2)
[1] 0

So before taking the log2 of our expression data, we will add 1 to each value to avoid the occirence of “-Inf” values.


brca_expr_mat.log <- log(brca_expr_mat+1, 2)  # We add 1 to each element, then take the log base 2.

#Let's look at some values:
brca_expr_mat.log[1:5,1:5]
         TCGA-3C-AAAU TCGA-3C-AALI TCGA-3C-AALJ TCGA-3C-AALK
TSPAN6      7.5636463     7.705439     9.975045    10.110718
TNMD        0.4272843     1.061776     5.353718     1.164271
DPM1        9.0367945     9.662019     9.919403     8.859174
SCYL3       8.3493520    10.607275     8.395877     9.103957
C1orf112    6.9691735     8.106778     7.922947     7.776269
         TCGA-4H-AAAK
TSPAN6       9.881960
TNMD         2.506120
DPM1         8.928912
SCYL3        8.704889
C1orf112     7.495535

Let’s check out what the curve is for the log-transformed expression values:

mean.expr <- apply(brca_expr_mat.log, 1, mean)  # We "apply" the function mean() to all rows.
View(our_matrix)
                                                # The first argument is the matrix.
                                                # The second argument is 1 for "rows".
                                                # The third argument is the function name, `mean()`

hist(mean.expr, breaks=100, main="Distribution of Log_2 Gene Expression Values",
  xlab="Mean Log_2 Expression")


# A histogram is a graphical display of data using bars of different heights. 
# Each bar groups numbers into ranges. Taller bars show that more data falls in that range. 
# The first argument is the data, the second is how bars we want to show, 
# the third is a title for the plot, and the fourth is the name of the x-axis.

This isn’t quite normal (especially at the lower range), but it is much closer, and importantly, close enough for some exploratory analyses.

Samples: Distributions of expression values

In addition to the genes, we can also look at the distribution of the expression values of the samples. This is commonly done using a boxplot.

I <- seq(1, ncol(brca_expr_mat.log), 10)  # We will look at every tenth sample.
boxplot(brca_expr_mat.log[,I], xaxt="n", xlab="Samples", ylab="Log_2 Gene Expression", 
  pch=19, cex=1)

Here, each of the columns is a sample, and the Y-axis shows the distribution of the expression of genes in each sample.
When we look at these plots, we hope to see a similar distribution of gene expession values across all samples.

While the expression of any individual gene will vary from sample to sample, as a whole, the distribution should be similar.

If we see differences (i.e. some samples have very different expression values), we will need to investigate why that is so, whether it is due to a difference in the biology (e.g. different tissues can exhibit different expression), or a technical artifact from the experiments (rather common, unfortunately). Here, the data looks pretty clean.

Visualizing the data with heatmaps

Let’s start by taking a look at what the data looks like using a heatmap. We won’t be able to make a heatmap of the entire data set — there are just too many genes.

Just to remind ourselves, take another look at the size of the data.

dim(brca_expr_mat.log)
[1] 18351  1082
brca_expr_mat.log[1:5,1:5]
         TCGA-3C-AAAU TCGA-3C-AALI TCGA-3C-AALJ TCGA-3C-AALK
TSPAN6      7.5636463     7.705439     9.975045    10.110718
TNMD        0.4272843     1.061776     5.353718     1.164271
DPM1        9.0367945     9.662019     9.919403     8.859174
SCYL3       8.3493520    10.607275     8.395877     9.103957
C1orf112    6.9691735     8.106778     7.922947     7.776269
         TCGA-4H-AAAK
TSPAN6       9.881960
TNMD         2.506120
DPM1         8.928912
SCYL3        8.704889
C1orf112     7.495535

To reduce the number of genes, we will try to select the most important ones for an exploratory analysis. There are many different ways to do this, and your choice here can lead to profoundly different views of the data.

One common approach to select genes in an unbiased way is to find the ones with the biggest differences across the samples, or in statistics language, the ones with the highest variance

We’ll consider the 500 genes with the highest variance.

And order out matrix accordingly, so we make a matrix for the top 500 variable genes.

NUM.GENES <- 500                                # How many genes we want to consider.
v <- apply(brca_expr_mat, 1, var)    # Here, we use the function apply
                                                # with the function var (for variance).

O <- order(v, decreasing=TRUE)   # The function order will rank the values for us.

var_genes <- v[O]                       # We look at the top ten.


brca.log.sub <- brca_expr_mat.log[O[1:NUM.GENES],]

Let’s look at it.


brca.log.sub[1:5,1:5]
       TCGA-3C-AAAU TCGA-3C-AALI TCGA-3C-AALJ TCGA-3C-AALK
CPB1       16.16135     4.243707     11.10774     6.928501
COL1A1     15.76215    17.451710     17.46421    18.729189
COL1A2     15.08131    16.591093     16.53135    17.853364
FN1        15.49303    16.703795     16.88043    17.056871
COL3A1     14.94762    16.132576     16.08866    17.581760
       TCGA-4H-AAAK
CPB1       2.707503
COL1A1    18.659219
COL1A2    18.187168
FN1       17.901097
COL3A1    17.871713

When we’re doing exploratory analysis, we’ll often work with just a subset of the data. It makes things go faster, and the plots are easier to interpret. To pull out 10% of the samples, let’s store in the I.sample vector the indexes of every 10th sample. We’ll use this subset for now, but for the final analysis, we’ll certainly want to use the whole data set.

library("RColorBrewer")             # We call a library that has nice colors

I.sample <- seq(1, ncol(brca.log.sub), 10)      # We take every tenth sample.


heatmap(brca.log.sub[,I.sample],
        Rowv=NA, Colv=NA, scale="none", labRow="",                  # These arguments are for plotting
        labCol="", col=brewer.pal(10,"RdBu"), margins = c(1, 0))    # the heatmap.

Each row is a gene, and each column is a sample. It doesn’t quite look right, though. There are a bunch of horizontal stripes. This happens because some of the genes are expressed higher than others. While this is interesting, what we really want to see are the patterns of expression across the samples. In other words, we want to know, for each gene, whether it is higher in one group of samples versus the other. So we are more interested in the relative expression of the genes, rather than the absolute expression.

To get the relative gene expression, we will first normalize each of the genes. A common way to do this is to change each gene such that the mean expression is 0, and the variance is 1. The absolute gene expression values will be changed, but the relative expression (whether it is higher or lower in a particular sample) will be preserved.

R has an easy way to do this with the function scale().


help(scale)

Notice that scale() will scale the columns of our matrix. But we want to scale the genes, which are in the rows!

This is not a problem, because we can use the transpose function t() to convert rows to columns and columns to rows.

So we scale the transposed matrix. Then we have to transpose it back!!

Here, we are using three nested functions: * t() to transpose our matrix * scale() to scale the transposed matrix * another t() to transpose back so that genes are rows again.

brca.log.sub.scale <- t(scale(t(brca.log.sub)))   

What does the heatmap look like now?

heatmap(brca.log.sub.scale[,I.sample],
   Rowv=NA, Colv=NA, scale="none", labRow="",     # The rest of the arguments are for plotting.
   labCol="", col=brewer.pal(10,"RdBu"), zlim=c(-2, 2), margins = c(1, 0))

Nice! This is much better. We can more easily see which genes are overexpressed (red) and underexpressed (blue) in a relative manner.

Our next activity will be to organize this matrix so we can see patterns among the * genes (row), and * samples (columns),

and associate the patterns with clinical informations.

Practice

  1. Save this file with another name.

  2. Work through the activity again and change values such as NUM.GENES and I.sample.

  3. How do your choices change what you see?

Clustering: Finding patterns in the data

Here, we will do hierarchical clustering.

We first calcuate the distances between the rows (genes) in our matrix. To do this, we apply the function dist() to our matrix.

Then we calculate the distances between the columns (samples) in our matrix. To do this, we apply dist() to the transpose of our matrix.


our_matrix <- brca.log.sub.scale[,I.sample]

row.dist <- dist(our_matrix)       # Use dist() on our matrix
col.dist <- dist(t(our_matrix))    # Use distr() on the transpose of our matrix

Then we cluster the rows and columns based on the distances we calculated and create the dendrograms, i.e. the distance trees, with the function hclust().


row_clusters <- hclust(row.dist)        # Apply `hclust()` to the rows  
column_clusters <- hclust(col.dist)     # Apply `hclust()` to the columns

Now, we will generate the clustered heatmaps.


heatmap(
  our_matrix,                           # The matrix of expression values
  Rowv=as.dendrogram(row_clusters),     # Create row dendrogram from `row_clusters`
  Colv=as.dendrogram(column_clusters),  # Create column dendogram from `column_clusters`
  
  scale="none", labRow="", labCol="", col=brewer.pal(10,"RdBu"),   # plotting arguments
  zlim=c(-2, 2), margins = c(1, 0)      # more plotting arguments
)

Clusters and the underlying biology

From this heatmap, we can see that the tumors split up into different groups. Is there any biological significance to these clusters? Are these groups only seen in the gene expression patterns, or is it related to any known biology?

As you previously learned, breast cancer is split up into ER+ and ER- tumors, depending on whether the tumor expresses high levels of receptors for estrogen. This is really important to know because it tells you what’s driving the disease, and also how to treat it. To see whether ER status is associated with these clusters, let’s label the samples with a "+" for ER+ breast cancers, and "." for negative.

clin <- brca_clin_df[I.sample,]      # We index the clinical data for 
                                     # our subset of samples.


x <- clin[["breast_carcinoma_estrogen_receptor_status"]]     # We find and name the
print(sort(unique(x)))                                       # different types of ER status.
[1] "[Not Evaluated]" "Negative"        "Positive"       
er <- rep("", length(x))                                     # We will label the columns of 
er[x == "Positive"] <- "+"                                   # our heat map with `er`.
er[x == "Negative"] <- "."
print(sum(er == ""))
[1] 6
heatmap(
  our_matrix,                           # The matrix of expression values
  Rowv=as.dendrogram(row_clusters),     # Create row dendrogram from `row_clusters`
  Colv=as.dendrogram(column_clusters),  # Create column dendogram from `column_clusters`
  
  scale="none", labRow="", labCol=er, col=brewer.pal(10,"RdBu"),   # NOTICE labCol=er
  zlim=c(-2, 2), margins = c(1, 0)      # more plotting arguments
)

NA
NA

In this plot, I see about four or five clusters of samples. Most of them have a lot of ER+ tumors (marked by the row of pluses on the bottom), while one is nearly all ER-.

Beautiful!!

LS0tCnRpdGxlOiAnTW9kdWxlIDE6IEZpbmRpbmcgUGF0dGVybnMgaW4gRXhwcmVzc2lvbiBEYXRhJwphdXRob3I6ICJBbGV4IExld2lzIGFuZCBEaWFuYSBNdXJyYXkiCmRhdGU6ICJKdWx5IDMwLCAyMDIwIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKIyMgUHJlbGltaW5hcmllcwoKCgojIyMgVGhlIGtuaXRyIFIgcGFja2FnZSAKCioqa25pdHIoKSoqIGlzIHRoZSBSIHBhY2thZ2UgdGhhdCBnZW5lcmF0ZXMgdGhlIHJlcG9ydCBmcm9tIFIgTWFya2Rvd24uIFdlIGNhbiBjcmVhdGUgcmVwb3J0cyBhcyBXb3JkIGRvYywgUERGLCBhbmQgSFRNTCBmaWxlcy4gCgpBbiBSIHBhY2thZ2UgYnVuZGxlcyB0b2dldGhlciBjb2RlLCBkYXRhLCBkb2N1bWVudGF0aW9uLCBhbmQgdGVzdHMsIGFuZCBpcyBlYXN5IHRvIGRvd25sb2FkIGFuZCBzaGFyZSB3aXRoIG90aGVycy4KCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCmRhdGFfZGlyIDwtICIvaG9tZS9kYXRhIiAgICAjIFRoZSBkYXRhIHdlIGFyZSB1c2luZyBpcyBsb2NhdGVkIGluIHRoaXMgZGlyZWN0b3J5IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBvbiBvdXIgY2xvdWQgY29tcHV0aW5nIHNlcnZlci4KCmBgYAoKIyMgQWJvdXQgdGhpcyBhY3Rpdml0eQoKWW91IHdpbGwgbG9hZCBhbmQgZXhhbWluZSBSIGRhdGFmcmFtZSBvYmplY3RzIHRoYXQgY29udGFpbiBkYXRhIApmcm9tIG92ZXIgMSwwMDAgYnJlYXN0IGNhbmNlciBwYXRpZW50cyBmcm9tIFRoZSBDYW5jZXIgR2Vub21lIEF0bGFzLiAKVGhlIG9iamVjdHMgaW5jbHVkZToKKiBjbGluaWNhbCBtZWFzdXJlbWVudHMgb24gdGhlIHBhdGllbnRzIGFuZCB0aGUgcGF0aWVudHMnIHR1bW9ycwoqIGdlbmUgZXhwcmVzc2lvbiBkYXRhICAKCltUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyBvciBUQ0dBXShodHRwczovL3d3dy5jYW5jZXIuZ292L2Fib3V0LW5jaS9vcmdhbml6YXRpb24vY2NnL3Jlc2VhcmNoL3N0cnVjdHVyYWwtZ2Vub21pY3MvdGNnYSkgY2hhcmFjdGVyaXplZCBvdmVyIDIwLDAwMCBjYW5jZXIgc2FtcGxlcyBzcGFubmluZyAzMyBjYW5jZXIgdHlwZXMgd2l0aCBnZW5vbWljcy4gCgpJbiAyMDEyLCB0aGUgVENHQSBOZXR3b3JrIHJlcG9ydGVkIGZpbmRpbmdzIG9mIGFuYWx5c2VzIG9mIHByaW1hcnkgYnJlYXN0IGNhbmNlcnMgaW4gaHVuZHJlZHMgb2YgcGF0aWVudHMgd2l0aCBtdWx0aXBsZSBleHBlcmltZW50YWwgcGxhdGZvcm1zIChleG9tZSBzZXF1ZW5jaW5nLCBnZW5vbWljIEROQSBjb3B5IG51bWJlciBhcnJheXMsIEROQSBtZXRoeWxhdGlvbiwgbVJOQSBhcnJheXMsIG1pY3JvUk5BIHNlcXVlbmNpbmcsIGFuZCByZXZlcnNlLXBoYXNlIHByb3RlaW4gYXJyYXlzLikgSGVyZSwgd2Ugd2lsbCBjb25zaWRlciB0aGUgbm9ybWFsaXplZCByZWFkIGNvdW50IGV4cHJlc3Npb24gZGF0YSBmb3IgcGF0aWVudC1sZXZlbCBicmVhc3QgY2FuY2VyIChicmNhKSBkYXRhIGZyb20gVENHQS4KCi0tLQoKIyMgTG9hZGluZyB0aGUgZGF0YQoKVGhyb3VnaG91dCB0aGUgY291cnNlLCBXZSB3aWxsIGJlIGxvYWRpbmcgZGF0YSBmcm9tIFIgZmlsZXMgd2l0aCB0aGUgZXh0ZW5zaW9uIGAuUkRhdGFgLiBBbiBleGFtcGxlIGlzIHRoZSBmaWxlIGBUQ0FHX2JyY2EuUkRhdGFgIHdoaWNoIHdlIGV4YW1pbmUgYmVsb3cuCgpgLlJEYXRhYCBmaWxlcyBhcmUgYmluYXJ5IGZpbGVzIChpLmUuLCBub3QgaHVtYW4gcmVhZGFibGUpIHRoYXQgY2FuIHN0b3JlIG11bHRpcGxlIFIgb2JqZWN0cywgc3VjaCBhcyB2ZWN0b3JzLCBsaXN0cywgbWF0cmljZXMsIGFuZCBkYXRhIGZyYW1lcy4gCgpUaGUgUiBvYmplY3RzIGluIGAuUmRhdGFgIGZpbGVzIGFyZSB0aGUgcmVzdWx0cyBvZiBwcmV2aW91cyBSIGFuYWx5c2VzLCBhbmQgdGhlIGRhdGEgaXMgaW4gKip0aWR5KiogZm9ybS4gClRpZHkgZGF0YSBzZXRzIGhhdmUgY29uc2lzdGVudCBzdHJ1Y3R1cmUgYW5kIGFyZSBlYXN5IHRvIG1hbmlwdWxhdGUsIHZpc3VhbGl6ZSwgYW5kIG1vZGVsLiAKCldlIGNvdWxkIHNwZW5kIHRoZSBlbnRpcmUgY291cnNlIG9uIGxlYXJuaW5nIGhvdyB0byBwdXQgZGF0YSBpbiB0aWR5IGZvcm0sIGJ1dCBvdXIgZ29hbCBpcyB0aGUgdW5kZXJzdGFuZCBhbmQgYW5hbHl6ZSB0aGUgZGF0YSwgd2hpY2ggb2NjdXJzICphZnRlciogdGlkeWluZyB0aGUgZGF0YS4KCgpgYGB7cn0KCiMgV2Ugc2ltcGx5IGBsb2FkKClgIFRDR0FfYnJjYS5SRGF0YQojIFRoZSBmaWxlLnBhdGgoKSBmdW5jdGlvbiB0ZWxscyBgbG9hZCgpYCB3aGVyZSBvdXIgZGF0YSBsaXZlcwojIFRoZSBhcmd1bWVudCAidmVyYm9zZSA9IFRSVUUiIG1ha2VzIGBsb2FkKClgIHRlbGwgdXMgd2hhdCBSIG9iamVjdHMgYXJlIGJlaW5nIGxvYWRlZAoKbG9hZChmaWxlLnBhdGgoZGF0YV9kaXIsICJUQ0dBX2JyY2EuUkRhdGEiKSx2ZXJib3NlPVRSVUUpICAgCgpgYGAKClRoZSBvYmplY3RzIHdlcmUgbmFtZWQgdG8gYmUgYmUgZGVzY3JpcHRpdmUuCiogImRmIiBzdGFuZHMgZm9yICJkYXRhZnJhbWUiCiogImJyY2EiIHN0YW5kcyBmb3IgIlRDR0EgYnJlYXN0IGNhbmNlciIKKiAiZXhwciIgc3RhbmRzIGZvciAiZ2VuZSBleHByZXNzaW9uIGRhdGEiCiogIm5vcm0iIG1lYW5zIHRoYXQgdGhlIGRhdGEgaXMgbm9ybWFsaXplZCB0byBhY2NvdW50IGZvciB2YXJpYWJpbGl0eSBpbiB0aGUgZXhwZXJpbWVudHMKKiAibmFtZXMiIHJlbWluZHMgdXMgdGhhdCB0aGUgZGF0YSBoYXMgdGhlIGdlbmUgbmFtZXMgaW5jbHVkZWQKKiAiY2xpbiIgaXMgc2hvcnQgZm9yICJjbGluaWNhbCIKCldlIGxvb2tlZCBhdCBgYnJjYV9jbGluX2RmYCBsYXN0IHRpbWUuICAKVG9kYXkgd2Ugd2lsbCBhbHNvIGNvbnNpZGVyIGBicmNhX2V4cHJfbm9ybV9uYW1lc19kZmAuCgpUaGUgKkVudmlyb25tZW50KiB0YWIgZ2l2ZXMgdXMgYmFzaWMgaW5mb3JtYXRpb24gb24gdGhlIG9iamVjdHMgdGhhdCB3ZXJlIGxvYWRlZC4KCldlIHdpbGwgdXNlIHNvbWUgb2YgdGhlIFIgZnVuY3Rpb25zIHdlIGxlYXJuZWQgdG8gZXhhbWluZSB0aGUgZGF0YSBpbiBtb3JlIGRldGFpbC4KKiBgZGltKClgIHRlbGxzIHVzIHRoZSBkaW1lbnNpb25zICgjIHJvdywgIyBjb2x1bW5zKSBvZiB0aGUgb2JqZWN0cy4KKiBgaGVhZCgpYCBzaG93cyB1cyB0aGUgdG9wIHNldmVyYWwgcm93cyBvZiB0aGUgb2JqZWN0cy4KKiBpbmRleGluZyBhbGxvd3MgdXMgdG8gbG9vayBhdCB0aGUgcm93cyBhbmQgY29sdW1ucyB3ZSBjaG9vc2UuCgoKYGBge3J9CmRpbShicmNhX2V4cHJfbm9ybV9uYW1lc19kZikgICMgV2Ugc2hvdWxkIGdldCBhIHJlc3VsdCBjb25zaXN0ZW50CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgd2l0aCB3aGF0IGlzIGluIHRoZSBFbnZpcm9ubWVudCB0YWIKYGBgCgpMZXQncyBzZWUgd2hhdCdzIGluIHRoZSByb3dzIGFuZCBjb2x1bW5zLgoKYGBge3J9CmJyY2FfZXhwcl9ub3JtX25hbWVzX2RmWzE6NSwxOjVdICAgICAjIEluc3BlY3QgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBkYXRhIGZyYW1lCmBgYAoKVGhlIGZpcnN0IGNvbHVtbiwgInN5bWJvbCIgaXMgdGhlIG5hbWVzIG9mIHRoZSBnZW5lcy4KVGhlIHJlbWFpbmluZyBjb2x1bW5zIGFyZSB0aGUgZXhwcmVzc2lvbiBsZXZlbHMgb2YgdGhlIGdlbmVzIGZyb20gZGlmZmVyZW50IHBhdGllbnQgc2FtcGxlcy4KCiJUQ0dBLTNDLUFBQVUiIGlzIHRoZSBhbm9ueW1pemVkIGlkZW50aWZpZXIgZm9yIG9uZSBwYXRpZW50J3Mgc2FtcGxlLgoKVG8gZG8gYW5hbHlzaXMgd2l0aCB0aGUgZXhwcmVzc2lvbiBkYXRhZnJhbWUsIHdlIGhhdmUgdG8gcmVtb3ZlIHRoZSAic3ltYm9sIiBjb2x1bW4Kc28gdGhhdCB0aGUgbWF0cml4IGNvbnRhaW5zIG9ubHkgdGhlIG51bWVyaWNhbCBnZW5lIGV4cHJlc3Npb24gZGF0YS4KQnV0IHdlIHdhbnQgdG8ga2VlcCB0aGUgaW5mb3JtYXRpb24gZnJvbSB0aGUgInN5bWJvbCIgY29sdW1uLgpSIGFsbG93cyB1cyB0byBkbyB0aGlzIGJ5IGFzc2lnbmluZyB0aGUgc3ltYm9scyBhcyB0aGUgcm93IG5hbWVzIGZvciBvdXIgbWF0cml4LgoKCmBgYHtyfQoKYnJjYV9leHByX21hdCA8LSBhcy5tYXRyaXgoYnJjYV9leHByX25vcm1fbmFtZXNfZGZbLC0xXSkgICMgV2UgcmVtb3ZlIHRoZSBmaXJzdCBjb2x1bW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHVzZSB0aGUgZnVuY3Rpb24gYXMubWF0cml4KCkuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGFuZCBhc3NpZ24gdGhlIHJlc3VsdCB0byBhIG5ldyBvYmplY3QuCgojIE5vdywgd2UnbGwgdXNlIHRoZSBmdW5jdGlvbiByb3cubmFtZXMoKSB0byBrZWVwIHRoZSBnZW5lIHN5bWJvbHMuCnJvdy5uYW1lcyhicmNhX2V4cHJfbWF0KSA8LSBicmNhX2V4cHJfbm9ybV9uYW1lc19kZiRzeW1ib2wKCmBgYAoKCkxldCdzIHVzZSBpbmRleGluZyB0byBjaGVjayBvdXIgdGhlIGZpcnN0IDUgcm93cyBhbmQgY29sdW1ucyBvZiBvdXIgbWF0cml4LgoKYGBge3J9CmJyY2FfZXhwcl9tYXRbMTo1LDE6NV0KYGBgCgpMZXQncyBsb29rIGF0IHRoZSBhdmVyYWdlIHZhbHVlIG9mIHRoZSBleHByZXNzaW9uIGZvciB0aGUgZmlyc3QgdHdvIGdlbmVzLCBUU1BBTjYgYW5kIFROTUQuCgpUU1BBTjYgY29kZXMgZm9yIHRoZSBwcm90ZWluIFRldHJhc3BhbmluLTYuIFRoaXMgcHJvdGVpbiBoZWxwcyBzaWduYWwgZXZlbnRzIHRoYXQgcGxheSBhIHJvbGUgaW4gY2VsbCBncm93dGggYW5kIG1vdGlsaXR5LgoKVE5NRCBjb2RlcyBmb3IgdGhlIHByb3RlaW4gVGVub21vZHVsaW4sIHdoaWNoIGlzIGltcG9ydGFudCBmb3IgdGhlIGZvcm1hdGlvbiBvZiB0ZW5kb25zLiBUTk1EIGlzIGhpZ2hseSBleHByZXNzZWQgaW4gdGVuZG9ucywgYnV0IGxvd2x5IGV4cHJlc3NlZCBpbiBvdGhlciBwYXJ0cyBvZiB0aGUgYm9keS4KCmBgYHtyfQoKIyBtZWFuKCkgdGFrZXMgdGhlIGF2ZXJhZ2Ugb2YgYSBzZXQgb2YgdmFsdWVzCgphdmdfVFNQQU42X2V4cHIgPC0gbWVhbihicmNhX2V4cHJfbWF0WzEsXSkgICAjIFdlIGFyZSB0YWtpbmcgdGhlIGF2ZXJhZ2Ugb2YgZXhwcmVzc2lvbiB2YWx1ZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdGhlIGZpcnN0IHJvdyBvZiB0aGUgbWF0cml4CgphdmdfVE5NRF9leHByIDwtIG1lYW4oYnJjYV9leHByX21hdFsyLF0pICAgICAjIFdlIGFyZSB0YWtpbmcgdGhlIGF2ZXJhZ2Ugb2YgZXhwcmVzc2lvbiB2YWx1ZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmb3IgdGhlIHNlY29uZCByb3cgb2YgdGhlIG1hdHJpeAoKCnByaW50KHBhc3RlKCJBdmVyYWdlIGV4cHJlc3Npb24gb2YgVFNQQU42OiAiLCByb3VuZChhdmdfVFNQQU42X2V4cHIsMCkpKQoKcHJpbnQocGFzdGUoIkF2ZXJhZ2UgZXhwcmVzc2lvbiBvZiBUTk1EOiAiLCByb3VuZChhdmdfVE5NRF9leHByLDApKSkKCiMgVGhlIGZ1bmN0aW9uIHBhc3RlKCkgYWxsb3dzIHVzIHRvIHByaW50IHRleHQgYW5kIG51bWJlcnMgdG9nZXRoZXIuCiMgVGhlIGZ1bmN0aW9uIHJvdW5kKCkgbGV0cyB1cyBjaG9vc2UgaG93IG1hbnkgZGVjaW1hbCBkaWdpdHMgd2Ugd2FudCB0byBzaG93LgoKYGBgCgpJdCB3b3VsZCBzdGluayB0byBoYXZlIHRvIGRvIHRoaXMgZm9yIGFsbCAxOCwzNTEgZ2VuZXMuICAKCkJ1dCB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgYXBwbHkoKWAgdG8gYXBwbHkgYSBmdW5jdGlvbiBsaWtlIGBtZWFuKClgCnRvIGFsbCByb3dzIG9yIGNvbHVtbnMgb2Ygb3VyIG1hdHJpeC4KCgpgYGB7cn0KbWVhbi5leHByIDwtIGFwcGx5KGJyY2FfZXhwcl9tYXQsIDEsIG1lYW4pICAjIFdlICJhcHBseSIgdGhlIGZ1bmN0aW9uIG1lYW4oKSB0byBhbGwgcm93cy4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRoZSBmaXJzdCBhcmd1bWVudCBpcyB0aGUgbWF0cml4LgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVGhlIHNlY29uZCBhcmd1bWVudCBpcyAxIGZvciAicm93cyIuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdGhpcmQgYXJndW1lbnQgaXMgdGhlIGZ1bmN0aW9uIG5hbWUsIGBtZWFuKClgCgphcy50YWJsZShyb3VuZChtZWFuLmV4cHJbMToyNV0sMCkpCgpgYGAKCkl0J3MgbmljZSB3ZSBjYW4gY2FsY3VsYXRlIGFsbCB0aGUgbWVhbnMgYXQgb25jZSwgYnV0IGl0J3Mgbm90IHZlcnkgaGVscGZ1bCBpbiBjb21wYXJpbmcgdGhlIG1lYW4gb2YgdGhlIGRpZmZlcmVudCBnZW5lcy4KCkxldCdzIHRyeSBwbG90dGluZyB0aGUgbWVhbnMgYXMgYSBoaXN0b2dyYW0uCgpgYGB7cn0KaGlzdChtZWFuLmV4cHIsIGJyZWFrcz0xMDAsIG1haW49IkRpc3RyaWJ1dGlvbiBvZiBHZW5lIEV4cHJlc3Npb24gVmFsdWVzIiwKICB4bGFiPSJNZWFuIEV4cHJlc3Npb24iKQoKIyBBIGhpc3RvZ3JhbSBpcyBhIGdyYXBoaWNhbCBkaXNwbGF5IG9mIGRhdGEgdXNpbmcgYmFycyBvZiBkaWZmZXJlbnQgaGVpZ2h0cy4gCiMgRWFjaCBiYXIgZ3JvdXBzIG51bWJlcnMgaW50byByYW5nZXMuIFRhbGxlciBiYXJzIHNob3cgdGhhdCBtb3JlIGRhdGEgZmFsbHMgaW4gdGhhdCByYW5nZS4gCiMgVGhlIGZpcnN0IGFyZ3VtZW50IGlzIHRoZSBkYXRhLCB0aGUgc2Vjb25kIGlzIGhvdyBiYXJzIHdlIHdhbnQgdG8gc2hvdywgCiMgdGhlIHRoaXJkIGlzIGEgdGl0bGUgZm9yIHRoZSBwbG90LCBhbmQgdGhlIGZvdXJ0aCBpcyB0aGUgbmFtZSBvZiB0aGUgeC1heGlzLgoKYGBgCgpXb3chICBNb3N0IG9mIHRoZSBhdmVyYWdlIGV4cHJlc3Npb24gdmFsdWVzIGFyZSByZWxhdGl2ZWx5IHNtYWxsIChsZXNzIHRoYW4gMjAwKSwgYnV0IHRoZXJlIG11c3QgYmUgYSBmZXcgcmVhbGx5IGxhcmdlIHZhbHVlcy4KCkxldCdzIHNlZSBob3cgbWFueSBnZW5lcyBoYXZlIGF2ZXJhZ2UgZXhwcmVzc2lvbiBncmVhdGVyIHRoYW4gMTAsMDAwLgoKCmBgYHtyfQoKcHJpbnQocGFzdGUoIlRvdGFsIHZhbHVlczogIiwgbGVuZ3RoKG1lYW4uZXhwcikpKQpwcmludChwYXN0ZSgiTnVtIHZhbHVlcyA+IDEwLDAwMDogIiwgc3VtKG1lYW4uZXhwciA+IDEwMDAwKSkpCnByaW50KHBhc3RlKCJQZW5jZW50YWdlIG9mIG1lYW4gdmFsdWVzID4gMTAsMDAwOiAiLCByb3VuZChzdW0obWVhbi5leHByPjEwMDAwKS9sZW5ndGgobWVhbi5leHByKSoxMDAsMSkpKQoKYGBgCgoKU28gdGhlcmUgYXJlIGEgZmV3IHJlYWxseSBiaWcgbnVtYmVycy4KVGhlIGN1cnZlIGluIHRoZSBoaXN0b2dyYW0gaGFzIGEgbG9uZyByaWdodCB0YWlsLiAgCk5lYXJseSBhbGwgZ2VuZXMgaGF2ZSAqbG93KiBleHByZXNzaW9uLCBhbmQgdmVyeSBmZXcgZ2VuZXMKaGF2ZSAqaGlnaCogZXhwcmVzc2lvbi4gIAoKVW5mb3J0dW5hdGVseSwgbWFueSBhbmFseXNpcyBmdW5jdGlvbnMgaW4gUiBkbyBub3QKd29yayB3ZWxsIHdpdGggdGhpcyBraW5kIG9mIGRpc3RyaWJ1dGlvbi4gIAoKSW5zdGVhZCwgaXQgaXMgdmVyeSBjb21tb24gdG8gYXNzdW1lIHRoZSBkYXRhIGZvbGxvdyBhIG5vcm1hbCAob3IgYmVsbCkgY3VydmUuCgpJbiBwcmFjdGl2ZSx2ZXJ5IGxpdHRsZSByZWFsIGxpZmUgYmlvbG9naWNhbCBkYXRhIHRydWx5IGZvbGxvdyBhIApub3JtYWwgY3VydmUuIEl0IGhhcyBiZWVuIGVzdGFibGlzaGVkIHRoYXQgbW9zdCBhbmFseXNpcyB3b3JrcyBxdWl0ZQp3ZWxsIGlmIHRoZSBkYXRhIGFyZSAqY2xvc2UgZW5vdWdoKiB0byBhIG5vcm1hbCBjdXJ2ZS4KCldlIGNhbiBtYWtlIG91ciBkYXRhIG1vcmUgbGlrZSBub3JtYWwgZGF0YSBieSBkb2luZyBhICpsb2cgdHJhbnNmb3JtKi4gCklmIHdlIHRha2UgdGhlIGxvZyBvZiBhbGwgdGhlIGVweHJlc3Npb24gdmFsdWVzLCB3ZSBwdXQgdGhlIGRhdGEgbW9yZSBvbgplcXVhbGx5IGZvb3RpbmcuIAoKRXhwcmVzc2lvbiBkYXRhIGlzIHR5cGljYWxseSBsb2dnZWQgdXNpbmcgYmFzZSAyLiAgCgpMb2cgYmFzZSAyIG9mIDggaXMgMyBiZWNhdXNlIDIgKiAyICogMiA9IDguCgpMb2cgYmFzZSA0IG9mIDQgaXMgMiBiZWNhdXNlIDIgKiAyID0gNC4KCkxvZyBiYXNlIDIgb2YgMiBpcyAxIGJlY2F1c2UgMiA9IDIuCgpTbyB0aGUgdmFsdWVzICg4LCA0ICwgMikgYXJlICp0cmFuc2Zvcm1lZCogdG8gKDMsIDIsIDEpLiAgCgpMZXQncyBjb25zaWRlciBzb21lIGxhcmdlciBudW1iZXJzOgogICAgCiAgICAKYGBge3J9CmEgPC0gYyg0MDk2LCAxMDI0LCA1MTIsIDY0KQoKYiA8LSBsb2coYSwyKQoKcGxvdChhLGIscGNoPTE5LGNvbD0icmVkIikKCmBgYAogICAgCk5vdGljZSBob3cgdGhlIHNjYWxlIGZvciBiIGlzIG11Y2ggbW9yZSBjb21wcmVzc2VkIHRoYW4gZm9yIGEuCgpCeSB0YWtpbmcgdGhlIGxvZzIgb2YgbnVtYmVycywgd2UgbWFrZSB0aGVtIG1vcmUgc2ltaWxhciB0byBlYWNoIG90aGVyLgoKCmBgYHtyfQoKYQoKbG9nKGEsMikKYGBgCgoKVGhlcmUgaXMgb25lIGNhdmVhdC4gIFRoZSBsb2cyIG9mIDAgaXMgaW5maW5pdHkhICAKCmBgYHtyfQoKbG9nKDAsMikKCmBgYAoKQnV0IHRoZSBsb2cyIG9mIDEgaXMgIndlbGwtYmVoYXZlZC4iCgpgYGB7cn0KbG9nKDEsMikKYGBgCgpTbyBiZWZvcmUgdGFraW5nIHRoZSBsb2cyIG9mIG91ciBleHByZXNzaW9uIGRhdGEsIHdlIHdpbGwgYWRkIDEgdG8gZWFjaCB2YWx1ZSB0byBhdm9pZCB0aGUgb2NjaXJlbmNlIG9mICItSW5mIiB2YWx1ZXMuCgogICAgCmBgYHtyfQoKYnJjYV9leHByX21hdC5sb2cgPC0gbG9nKGJyY2FfZXhwcl9tYXQrMSwgMikgICMgV2UgYWRkIDEgdG8gZWFjaCBlbGVtZW50LCB0aGVuIHRha2UgdGhlIGxvZyBiYXNlIDIuCgojTGV0J3MgbG9vayBhdCBzb21lIHZhbHVlczoKYnJjYV9leHByX21hdC5sb2dbMTo1LDE6NV0KCmBgYAogCiAKTGV0J3MgY2hlY2sgb3V0IHdoYXQgdGhlIGN1cnZlIGlzIGZvciB0aGUgbG9nLXRyYW5zZm9ybWVkIGV4cHJlc3Npb24gdmFsdWVzOiAKCmBgYHtyfQptZWFuLmV4cHIgPC0gYXBwbHkoYnJjYV9leHByX21hdC5sb2csIDEsIG1lYW4pICAjIFdlICJhcHBseSIgdGhlIGZ1bmN0aW9uIG1lYW4oKSB0byBhbGwgcm93cy4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIG1hdHJpeC4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgc2Vjb25kIGFyZ3VtZW50IGlzIDEgZm9yICJyb3dzIi4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgdGhpcmQgYXJndW1lbnQgaXMgdGhlIGZ1bmN0aW9uIG5hbWUsIGBtZWFuKClgCgpoaXN0KG1lYW4uZXhwciwgYnJlYWtzPTEwMCwgbWFpbj0iRGlzdHJpYnV0aW9uIG9mIExvZ18yIEdlbmUgRXhwcmVzc2lvbiBWYWx1ZXMiLAogIHhsYWI9Ik1lYW4gTG9nXzIgRXhwcmVzc2lvbiIpCgojIEEgaGlzdG9ncmFtIGlzIGEgZ3JhcGhpY2FsIGRpc3BsYXkgb2YgZGF0YSB1c2luZyBiYXJzIG9mIGRpZmZlcmVudCBoZWlnaHRzLiAKIyBFYWNoIGJhciBncm91cHMgbnVtYmVycyBpbnRvIHJhbmdlcy4gVGFsbGVyIGJhcnMgc2hvdyB0aGF0IG1vcmUgZGF0YSBmYWxscyBpbiB0aGF0IHJhbmdlLiAKIyBUaGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIGRhdGEsIHRoZSBzZWNvbmQgaXMgaG93IGJhcnMgd2Ugd2FudCB0byBzaG93LCAKIyB0aGUgdGhpcmQgaXMgYSB0aXRsZSBmb3IgdGhlIHBsb3QsIGFuZCB0aGUgZm91cnRoIGlzIHRoZSBuYW1lIG9mIHRoZSB4LWF4aXMuCgpgYGAKIAogClRoaXMgaXNuJ3QgcXVpdGUgbm9ybWFsIChlc3BlY2lhbGx5IGF0IHRoZSBsb3dlciByYW5nZSksIGJ1dCBpdCBpcwptdWNoIGNsb3NlciwgYW5kIGltcG9ydGFudGx5LCBjbG9zZSBlbm91Z2ggZm9yIHNvbWUgZXhwbG9yYXRvcnkKYW5hbHlzZXMuCgoKIyMgU2FtcGxlczogRGlzdHJpYnV0aW9ucyBvZiBleHByZXNzaW9uIHZhbHVlcwoKSW4gYWRkaXRpb24gdG8gdGhlIGdlbmVzLCB3ZSBjYW4gYWxzbyBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlCmV4cHJlc3Npb24gdmFsdWVzIG9mIHRoZSBzYW1wbGVzLiAgVGhpcyBpcyBjb21tb25seSBkb25lIHVzaW5nIGEKYm94cGxvdC4KCmBgYHtyfQpJIDwtIHNlcSgxLCBuY29sKGJyY2FfZXhwcl9tYXQubG9nKSwgMTApICAjIFdlIHdpbGwgbG9vayBhdCBldmVyeSB0ZW50aCBzYW1wbGUuCmJveHBsb3QoYnJjYV9leHByX21hdC5sb2dbLEldLCB4YXh0PSJuIiwgeGxhYj0iU2FtcGxlcyIsIHlsYWI9IkxvZ18yIEdlbmUgRXhwcmVzc2lvbiIsIAogIHBjaD0xOSwgY2V4PTEpCmBgYAogCiAKSGVyZSwgZWFjaCBvZiB0aGUgY29sdW1ucyBpcyBhIHNhbXBsZSwgYW5kIHRoZSBZLWF4aXMgc2hvd3MgdGhlCmRpc3RyaWJ1dGlvbiBvZiB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lcyBpbiBlYWNoIHNhbXBsZS4gIApXaGVuIHdlIGxvb2sgYXQgdGhlc2UgcGxvdHMsIHdlIGhvcGUgdG8gc2VlIGEgc2ltaWxhciBkaXN0cmlidXRpb24gCm9mIGdlbmUgZXhwZXNzaW9uIHZhbHVlcyBhY3Jvc3MgYWxsIHNhbXBsZXMuICAKCldoaWxlIHRoZSBleHByZXNzaW9uIG9mIGFueSBpbmRpdmlkdWFsIGdlbmUgd2lsbCB2YXJ5IGZyb20gc2FtcGxlIAp0byBzYW1wbGUsIGFzIGEgd2hvbGUsIHRoZSBkaXN0cmlidXRpb24gc2hvdWxkIGJlIHNpbWlsYXIuICAKCklmIHdlIHNlZSBkaWZmZXJlbmNlcyAoaS5lLiBzb21lIHNhbXBsZXMgaGF2ZSB2ZXJ5IGRpZmZlcmVudCAKZXhwcmVzc2lvbiB2YWx1ZXMpLCB3ZSB3aWxsIG5lZWQgdG8gaW52ZXN0aWdhdGUgd2h5IHRoYXQgaXMgc28sIAp3aGV0aGVyIGl0IGlzIGR1ZSB0byBhIGRpZmZlcmVuY2UgaW4gdGhlIGJpb2xvZ3kgKGUuZy4gZGlmZmVyZW50IAp0aXNzdWVzIGNhbiBleGhpYml0IGRpZmZlcmVudCBleHByZXNzaW9uKSwgb3IgYSB0ZWNobmljYWwgYXJ0aWZhY3QgCmZyb20gdGhlIGV4cGVyaW1lbnRzIChyYXRoZXIgY29tbW9uLCB1bmZvcnR1bmF0ZWx5KS4gIEhlcmUsIHRoZSBkYXRhCmxvb2tzIHByZXR0eSBjbGVhbi4gIAoKCiMgVmlzdWFsaXppbmcgdGhlIGRhdGEgd2l0aCBoZWF0bWFwcwoKTGV0J3Mgc3RhcnQgYnkgdGFraW5nIGEgbG9vayBhdCB3aGF0IHRoZSBkYXRhIGxvb2tzIGxpa2UgdXNpbmcgYQpoZWF0bWFwLiAgV2Ugd29uJ3QgYmUgYWJsZSB0byBtYWtlIGEgaGVhdG1hcCBvZiB0aGUgZW50aXJlIGRhdGEKc2V0IOKAlCB0aGVyZSBhcmUganVzdCB0b28gbWFueSBnZW5lcy4KCkp1c3QgdG8gcmVtaW5kIG91cnNlbHZlcywgdGFrZSBhbm90aGVyIGxvb2sgYXQgdGhlIHNpemUgb2YgdGhlIGRhdGEuCgoKYGBge3J9CmRpbShicmNhX2V4cHJfbWF0LmxvZykKCmJyY2FfZXhwcl9tYXQubG9nWzE6NSwxOjVdCgpgYGAKCgpUbyByZWR1Y2UgdGhlIG51bWJlciBvZiBnZW5lcywgd2Ugd2lsbCB0cnkgdG8gc2VsZWN0IHRoZSAqbW9zdCBpbXBvcnRhbnQqIApvbmVzIGZvciBhbiBleHBsb3JhdG9yeSBhbmFseXNpcy4gIFRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB3YXlzIHRvIGRvIHRoaXMsIAphbmQgeW91ciBjaG9pY2UgaGVyZSBjYW4gbGVhZCB0byBwcm9mb3VuZGx5IGRpZmZlcmVudCB2aWV3cyBvZiB0aGUgZGF0YS4gIAoKT25lIGNvbW1vbiBhcHByb2FjaCB0byBzZWxlY3QgZ2VuZXMgaW4gYW4gdW5iaWFzZWQgd2F5IGlzIHRvIGZpbmQgdGhlIApvbmVzIHdpdGggdGhlICpiaWdnZXN0IGRpZmZlcmVuY2VzKiBhY3Jvc3MgdGhlIHNhbXBsZXMsIG9yIGluIHN0YXRpc3RpY3MgCmxhbmd1YWdlLCB0aGUgb25lcyB3aXRoIHRoZSBoaWdoZXN0IHZhcmlhbmNlCgpXZSdsbCBjb25zaWRlciB0aGUgNTAwIGdlbmVzIHdpdGggdGhlIGhpZ2hlc3QgdmFyaWFuY2UuCgpBbmQgb3JkZXIgb3V0IG1hdHJpeCBhY2NvcmRpbmdseSwgc28gd2UgbWFrZSBhIG1hdHJpeCBmb3IgdGhlIHRvcCA1MDAgdmFyaWFibGUgZ2VuZXMuCgpgYGB7cn0KTlVNLkdFTkVTIDwtIDUwMCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBIb3cgbWFueSBnZW5lcyB3ZSB3YW50IHRvIGNvbnNpZGVyLgp2IDwtIGFwcGx5KGJyY2FfZXhwcl9tYXQsIDEsIHZhcikgICAgIyBIZXJlLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGFwcGx5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgd2l0aCB0aGUgZnVuY3Rpb24gdmFyIChmb3IgdmFyaWFuY2UpLgoKTyA8LSBvcmRlcih2LCBkZWNyZWFzaW5nPVRSVUUpICAgIyBUaGUgZnVuY3Rpb24gb3JkZXIgd2lsbCByYW5rIHRoZSB2YWx1ZXMgZm9yIHVzLgoKdmFyX2dlbmVzIDwtIHZbT10gICAgICAgICAgICAgICAgICAgICAgICMgV2UgbG9vayBhdCB0aGUgdG9wIHRlbi4KCgpicmNhLmxvZy5zdWIgPC0gYnJjYV9leHByX21hdC5sb2dbT1sxOk5VTS5HRU5FU10sXQpgYGAKCgpMZXQncyBsb29rIGF0IGl0LgoKYGBge3J9CgpicmNhLmxvZy5zdWJbMTo1LDE6NV0KCmBgYAoKV2hlbiB3ZSdyZSBkb2luZyBleHBsb3JhdG9yeSBhbmFseXNpcywgd2UnbGwgb2Z0ZW4gd29yayB3aXRoIGp1c3QKYSBzdWJzZXQgb2YgdGhlIGRhdGEuICBJdCBtYWtlcyB0aGluZ3MgZ28gZmFzdGVyLCBhbmQgdGhlIHBsb3RzIGFyZQplYXNpZXIgdG8gaW50ZXJwcmV0LiAgVG8gcHVsbCBvdXQgMTAlIG9mIHRoZSBzYW1wbGVzLCBsZXQncyBzdG9yZSBpbgp0aGUgYEkuc2FtcGxlYCB2ZWN0b3IgdGhlIGluZGV4ZXMgb2YgZXZlcnkgMTB0aCBzYW1wbGUuICBXZSdsbCB1c2UKdGhpcyBzdWJzZXQgZm9yIG5vdywgYnV0IGZvciB0aGUgZmluYWwgYW5hbHlzaXMsIHdlJ2xsIGNlcnRhaW5seSB3YW50CnRvIHVzZSB0aGUgd2hvbGUgZGF0YSBzZXQuCgoKYGBge3J9CmxpYnJhcnkoIlJDb2xvckJyZXdlciIpICAgICAgICAgICAgICMgV2UgY2FsbCBhIGxpYnJhcnkgdGhhdCBoYXMgbmljZSBjb2xvcnMKCkkuc2FtcGxlIDwtIHNlcSgxLCBuY29sKGJyY2EubG9nLnN1YiksIDEwKSAgICAgICMgV2UgdGFrZSBldmVyeSB0ZW50aCBzYW1wbGUuCgoKaGVhdG1hcChicmNhLmxvZy5zdWJbLEkuc2FtcGxlXSwKICAgICAgICBSb3d2PU5BLCBDb2x2PU5BLCBzY2FsZT0ibm9uZSIsIGxhYlJvdz0iIiwgICAgICAgICAgICAgICAgICAjIFRoZXNlIGFyZ3VtZW50cyBhcmUgZm9yIHBsb3R0aW5nCiAgICAgICAgbGFiQ29sPSIiLCBjb2w9YnJld2VyLnBhbCgxMCwiUmRCdSIpLCBtYXJnaW5zID0gYygxLCAwKSkgICAgIyB0aGUgaGVhdG1hcC4KYGBgCgoKRWFjaCByb3cgaXMgYSBnZW5lLCBhbmQgZWFjaCBjb2x1bW4gaXMgYSBzYW1wbGUuICBJdCBkb2Vzbid0IHF1aXRlCmxvb2sgcmlnaHQsIHRob3VnaC4gIFRoZXJlIGFyZSBhIGJ1bmNoIG9mIGhvcml6b250YWwgc3RyaXBlcy4gIFRoaXMKaGFwcGVucyBiZWNhdXNlIHNvbWUgb2YgdGhlIGdlbmVzIGFyZSBleHByZXNzZWQgaGlnaGVyIHRoYW4gb3RoZXJzLgpXaGlsZSB0aGlzIGlzIGludGVyZXN0aW5nLCB3aGF0IHdlIHJlYWxseSB3YW50IHRvIHNlZSBhcmUgdGhlIHBhdHRlcm5zCm9mIGV4cHJlc3Npb24gYWNyb3NzIHRoZSBzYW1wbGVzLiAgSW4gb3RoZXIgd29yZHMsIHdlIHdhbnQgdG8ga25vdywKZm9yIGVhY2ggZ2VuZSwgd2hldGhlciBpdCBpcyBoaWdoZXIgaW4gb25lIGdyb3VwIG9mIHNhbXBsZXMgdmVyc3VzIHRoZQpvdGhlci4gIFNvIHdlIGFyZSBtb3JlIGludGVyZXN0ZWQgaW4gdGhlIHJlbGF0aXZlIGV4cHJlc3Npb24gb2YgdGhlCmdlbmVzLCByYXRoZXIgdGhhbiB0aGUgYWJzb2x1dGUgZXhwcmVzc2lvbi4KClRvIGdldCB0aGUgcmVsYXRpdmUgZ2VuZSBleHByZXNzaW9uLCB3ZSB3aWxsIGZpcnN0IG5vcm1hbGl6ZSBlYWNoIG9mCnRoZSBnZW5lcy4gIEEgY29tbW9uIHdheSB0byBkbyB0aGlzIGlzIHRvIGNoYW5nZSBlYWNoIGdlbmUgc3VjaCB0aGF0CnRoZSBtZWFuIGV4cHJlc3Npb24gaXMgMCwgYW5kIHRoZSB2YXJpYW5jZSBpcyAxLiAgVGhlIGFic29sdXRlIGdlbmUKZXhwcmVzc2lvbiB2YWx1ZXMgd2lsbCBiZSBjaGFuZ2VkLCBidXQgdGhlIHJlbGF0aXZlIGV4cHJlc3Npb24KKHdoZXRoZXIgaXQgaXMgaGlnaGVyIG9yIGxvd2VyIGluIGEgcGFydGljdWxhciBzYW1wbGUpIHdpbGwgYmUKcHJlc2VydmVkLgoKClIgaGFzIGFuIGVhc3kgd2F5IHRvIGRvIHRoaXMgd2l0aCB0aGUgZnVuY3Rpb24gYHNjYWxlKClgLiAgCgoKCmBgYHtyfQoKaGVscChzY2FsZSkKYGBgCgoKTm90aWNlIHRoYXQgYHNjYWxlKClgIHdpbGwgc2NhbGUgdGhlICpjb2x1bW5zKiBvZiBvdXIgbWF0cml4LgpCdXQgd2Ugd2FudCB0byBzY2FsZSB0aGUgZ2VuZXMsIHdoaWNoIGFyZSBpbiB0aGUgcm93cyEKClRoaXMgaXMgbm90IGEgcHJvYmxlbSwgYmVjYXVzZSB3ZSBjYW4gdXNlIHRoZSB0cmFuc3Bvc2UgZnVuY3Rpb24gYHQoKWAKdG8gY29udmVydCByb3dzIHRvIGNvbHVtbnMgYW5kIGNvbHVtbnMgdG8gcm93cy4KClNvIHdlIHNjYWxlIHRoZSB0cmFuc3Bvc2VkIG1hdHJpeC4KVGhlbiB3ZSBoYXZlIHRvIHRyYW5zcG9zZSBpdCBiYWNrISEKCkhlcmUsIHdlIGFyZSB1c2luZyB0aHJlZSBuZXN0ZWQgZnVuY3Rpb25zOgoqIGB0KClgIHRvIHRyYW5zcG9zZSBvdXIgbWF0cml4CiogYHNjYWxlKClgIHRvIHNjYWxlIHRoZSB0cmFuc3Bvc2VkIG1hdHJpeAoqIGFub3RoZXIgYHQoKWAgdG8gdHJhbnNwb3NlIGJhY2sgc28gdGhhdCBnZW5lcyBhcmUgcm93cyBhZ2Fpbi4KCmBgYHtyfQpicmNhLmxvZy5zdWIuc2NhbGUgPC0gdChzY2FsZSh0KGJyY2EubG9nLnN1YikpKSAgIApgYGAKCgpXaGF0IGRvZXMgdGhlIGhlYXRtYXAgbG9vayBsaWtlIG5vdz8KCmBgYHtyfQpoZWF0bWFwKGJyY2EubG9nLnN1Yi5zY2FsZVssSS5zYW1wbGVdLAogICBSb3d2PU5BLCBDb2x2PU5BLCBzY2FsZT0ibm9uZSIsIGxhYlJvdz0iIiwgICAgICMgVGhlIHJlc3Qgb2YgdGhlIGFyZ3VtZW50cyBhcmUgZm9yIHBsb3R0aW5nLgogICBsYWJDb2w9IiIsIGNvbD1icmV3ZXIucGFsKDEwLCJSZEJ1IiksIHpsaW09YygtMiwgMiksIG1hcmdpbnMgPSBjKDEsIDApKQoKYGBgCgoKTmljZSEgIFRoaXMgaXMgbXVjaCBiZXR0ZXIuICBXZSBjYW4gbW9yZSBlYXNpbHkgc2VlIHdoaWNoIGdlbmVzIGFyZSBvdmVyZXhwcmVzc2VkIChyZWQpIGFuZCB1bmRlcmV4cHJlc3NlZCAoYmx1ZSkgaW4gYSByZWxhdGl2ZSBtYW5uZXIuICAKCk91ciBuZXh0IGFjdGl2aXR5IHdpbGwgYmUgdG8gb3JnYW5pemUgdGhpcyBtYXRyaXggc28gd2UgY2FuIHNlZSBwYXR0ZXJucyBhbW9uZyB0aGUgCiogZ2VuZXMgKHJvdyksIGFuZCAKKiBzYW1wbGVzIChjb2x1bW5zKSwKCmFuZCBhc3NvY2lhdGUgdGhlIHBhdHRlcm5zIHdpdGggY2xpbmljYWwgaW5mb3JtYXRpb25zLgoKCiMjIFByYWN0aWNlCgoxLiBTYXZlIHRoaXMgZmlsZSB3aXRoIGFub3RoZXIgbmFtZS4KCjIuIFdvcmsgdGhyb3VnaCB0aGUgYWN0aXZpdHkgYWdhaW4gYW5kIGNoYW5nZSB2YWx1ZXMgc3VjaCBhcyBOVU0uR0VORVMgYW5kIEkuc2FtcGxlLgoKMy4gSG93IGRvIHlvdXIgY2hvaWNlcyBjaGFuZ2Ugd2hhdCB5b3Ugc2VlPwoKIyMgQ2x1c3RlcmluZzogRmluZGluZyBwYXR0ZXJucyBpbiB0aGUgZGF0YQoKSGVyZSwgd2Ugd2lsbCBkbyAqaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcqLiAKCldlIGZpcnN0IGNhbGN1YXRlIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiB0aGUgcm93cyAoZ2VuZXMpIGluIG91ciBtYXRyaXguIApUbyBkbyB0aGlzLCB3ZSBhcHBseSB0aGUgZnVuY3Rpb24gYGRpc3QoKWAgdG8gb3VyIG1hdHJpeC4KClRoZW4gd2UgY2FsY3VsYXRlIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiB0aGUgY29sdW1ucyAoc2FtcGxlcykgaW4gb3VyIG1hdHJpeC4KVG8gZG8gdGhpcywgd2UgYXBwbHkgYGRpc3QoKWAgdG8gdGhlICp0cmFuc3Bvc2UqIG9mIG91ciBtYXRyaXguCgoKYGBge3J9CgpvdXJfbWF0cml4IDwtIGJyY2EubG9nLnN1Yi5zY2FsZVssSS5zYW1wbGVdCgpyb3cuZGlzdCA8LSBkaXN0KG91cl9tYXRyaXgpICAgICAgICMgVXNlIGRpc3QoKSBvbiBvdXIgbWF0cml4CmNvbC5kaXN0IDwtIGRpc3QodChvdXJfbWF0cml4KSkgICAgIyBVc2UgZGlzdHIoKSBvbiB0aGUgdHJhbnNwb3NlIG9mIG91ciBtYXRyaXgKCgpgYGAKCgpUaGVuIHdlICpjbHVzdGVyKiB0aGUgcm93cyBhbmQgY29sdW1ucyBiYXNlZCBvbiB0aGUgZGlzdGFuY2VzIHdlIGNhbGN1bGF0ZWQgYW5kIApjcmVhdGUgdGhlIGRlbmRyb2dyYW1zLCBpLmUuIHRoZSBkaXN0YW5jZSB0cmVlcywgd2l0aCB0aGUgZnVuY3Rpb24gYGhjbHVzdCgpYC4KCmBgYHtyfQoKcm93X2NsdXN0ZXJzIDwtIGhjbHVzdChyb3cuZGlzdCkgICAgICAgICMgQXBwbHkgYGhjbHVzdCgpYCB0byB0aGUgcm93cyAgCmNvbHVtbl9jbHVzdGVycyA8LSBoY2x1c3QoY29sLmRpc3QpICAgICAjIEFwcGx5IGBoY2x1c3QoKWAgdG8gdGhlIGNvbHVtbnMKCmBgYAoKCk5vdywgd2Ugd2lsbCBnZW5lcmF0ZSB0aGUgY2x1c3RlcmVkIGhlYXRtYXBzLgoKYGBge3J9CgpoZWF0bWFwKAogIG91cl9tYXRyaXgsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgbWF0cml4IG9mIGV4cHJlc3Npb24gdmFsdWVzCiAgUm93dj1hcy5kZW5kcm9ncmFtKHJvd19jbHVzdGVycyksICAgICAjIENyZWF0ZSByb3cgZGVuZHJvZ3JhbSBmcm9tIGByb3dfY2x1c3RlcnNgCiAgQ29sdj1hcy5kZW5kcm9ncmFtKGNvbHVtbl9jbHVzdGVycyksICAjIENyZWF0ZSBjb2x1bW4gZGVuZG9ncmFtIGZyb20gYGNvbHVtbl9jbHVzdGVyc2AKICAKICBzY2FsZT0ibm9uZSIsIGxhYlJvdz0iIiwgbGFiQ29sPSIiLCBjb2w9YnJld2VyLnBhbCgxMCwiUmRCdSIpLCAgICMgcGxvdHRpbmcgYXJndW1lbnRzCiAgemxpbT1jKC0yLCAyKSwgbWFyZ2lucyA9IGMoMSwgMCkgICAgICAjIG1vcmUgcGxvdHRpbmcgYXJndW1lbnRzCikKCmBgYAoKCgojIyBDbHVzdGVycyBhbmQgdGhlIHVuZGVybHlpbmcgYmlvbG9neQoKRnJvbSB0aGlzIGhlYXRtYXAsIHdlIGNhbiBzZWUgdGhhdCB0aGUgdHVtb3JzIHNwbGl0IHVwIGludG8gZGlmZmVyZW50Cmdyb3Vwcy4gIElzIHRoZXJlIGFueSBiaW9sb2dpY2FsIHNpZ25pZmljYW5jZSB0byB0aGVzZSBjbHVzdGVycz8gIEFyZQp0aGVzZSBncm91cHMgb25seSBzZWVuIGluIHRoZSBnZW5lIGV4cHJlc3Npb24gcGF0dGVybnMsIG9yIGlzIGl0CnJlbGF0ZWQgdG8gYW55IGtub3duIGJpb2xvZ3k/CgpBcyB5b3UgcHJldmlvdXNseSBsZWFybmVkLCBicmVhc3QgY2FuY2VyIGlzIHNwbGl0IHVwIGludG8gRVIrIGFuZCBFUi0KdHVtb3JzLCBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgdHVtb3IgZXhwcmVzc2VzIGhpZ2ggbGV2ZWxzIG9mCnJlY2VwdG9ycyBmb3IgZXN0cm9nZW4uICBUaGlzIGlzIHJlYWxseSBpbXBvcnRhbnQgdG8ga25vdyBiZWNhdXNlIGl0CnRlbGxzIHlvdSB3aGF0J3MgZHJpdmluZyB0aGUgZGlzZWFzZSwgYW5kIGFsc28gaG93IHRvIHRyZWF0IGl0LiAgVG8Kc2VlIHdoZXRoZXIgRVIgc3RhdHVzIGlzIGFzc29jaWF0ZWQgd2l0aCB0aGVzZSBjbHVzdGVycywgbGV0J3MgbGFiZWwKdGhlIHNhbXBsZXMgd2l0aCBhIGAiKyJgIGZvciBFUisgYnJlYXN0IGNhbmNlcnMsIGFuZCBgIi4iYCBmb3IgbmVnYXRpdmUuCgoKYGBge3J9CmNsaW4gPC0gYnJjYV9jbGluX2RmW0kuc2FtcGxlLF0gICAgICAjIFdlIGluZGV4IHRoZSBjbGluaWNhbCBkYXRhIGZvciAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgb3VyIHN1YnNldCBvZiBzYW1wbGVzLgoKCnggPC0gY2xpbltbImJyZWFzdF9jYXJjaW5vbWFfZXN0cm9nZW5fcmVjZXB0b3Jfc3RhdHVzIl1dICAgICAjIFdlIGZpbmQgYW5kIG5hbWUgdGhlCnByaW50KHNvcnQodW5pcXVlKHgpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGRpZmZlcmVudCB0eXBlcyBvZiBFUiBzdGF0dXMuCmVyIDwtIHJlcCgiIiwgbGVuZ3RoKHgpKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdlIHdpbGwgbGFiZWwgdGhlIGNvbHVtbnMgb2YgCmVyW3ggPT0gIlBvc2l0aXZlIl0gPC0gIisiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG91ciBoZWF0IG1hcCB3aXRoIGBlcmAuCmVyW3ggPT0gIk5lZ2F0aXZlIl0gPC0gIi4iCnByaW50KHN1bShlciA9PSAiIikpCgpoZWF0bWFwKAogIG91cl9tYXRyaXgsICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUaGUgbWF0cml4IG9mIGV4cHJlc3Npb24gdmFsdWVzCiAgUm93dj1hcy5kZW5kcm9ncmFtKHJvd19jbHVzdGVycyksICAgICAjIENyZWF0ZSByb3cgZGVuZHJvZ3JhbSBmcm9tIGByb3dfY2x1c3RlcnNgCiAgQ29sdj1hcy5kZW5kcm9ncmFtKGNvbHVtbl9jbHVzdGVycyksICAjIENyZWF0ZSBjb2x1bW4gZGVuZG9ncmFtIGZyb20gYGNvbHVtbl9jbHVzdGVyc2AKICAKICBzY2FsZT0ibm9uZSIsIGxhYlJvdz0iIiwgbGFiQ29sPWVyLCBjb2w9YnJld2VyLnBhbCgxMCwiUmRCdSIpLCAgICMgTk9USUNFIGxhYkNvbD1lcgogIHpsaW09YygtMiwgMiksIG1hcmdpbnMgPSBjKDEsIDApICAgICAgIyBtb3JlIHBsb3R0aW5nIGFyZ3VtZW50cwopCgoKYGBgCgpJbiB0aGlzIHBsb3QsIEkgc2VlIGFib3V0IGZvdXIgb3IgZml2ZSBjbHVzdGVycyBvZiBzYW1wbGVzLiAgTW9zdCBvZiAKdGhlbSBoYXZlIGEgbG90IG9mIEVSKyB0dW1vcnMgKG1hcmtlZCBieSB0aGUgcm93IG9mIHBsdXNlcyBvbiB0aGUgYm90dG9tKSwgCndoaWxlIG9uZSBpcyBuZWFybHkgYWxsIEVSLS4gCgpCZWF1dGlmdWwhIQoKCg==