Using map.QTL()
The main function map.QTL()
can be used to perform a
single-marker test. Depending on your input a linear model (without
kinship correction) or a mixed model (with kinship correction) will be
chosen. Missing genotypes can be imputed using this function. Finally, a
permutation significance threshold can also be computed (although it
increases computational time quite a bit).
Let us look a the different tools of map.QTL()
Linear model
The basic four parameters of map.QTL() are:
phenotypes
: a vector or matrix with a column for each
phenotype.
genotypes
: either a dosage or a haplotype matrix.
ploidy
: an integer indicating the ploidy of the
organism.
map
: a genetic map with the columns “marker”,
“chromosome”, and “position”.
The simplest QTL model that map.QTL()
provides is a
linear model with no knship correction. In this case, the p-values
originate from an F-test.
result_dos <- map.QTL(phenotypes = mppheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap)
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## No Q matrix will be used.
## Linear model will be used.
## Association completed.
str(result_dos,max.level = 2, give.attr = F)
## List of 1
## $ phenotype1:List of 4
## ..$ beta :List of 1010
## ..$ Ftest: Named num [1:1010] 30.43 27.04 1.64 1.43 83.1 ...
## ..$ pval : Named num [1:1010] 5.81e-08 3.02e-07 2.01e-01 2.33e-01 2.47e-18 ...
## ..$ se : Named num [1:1010] 0.969 0.973 0.999 1 0.921 ...
The results are provided in form of a nested list. Firstly, there is
an element for each phenotype, and within each phenotype there are four
elements:
beta
: a list containing the fixed effects of the model.
There is a set of estimates for each marker.
Ftest
: a vector containing the Ftest results only for
the genetic component of each model.
pval
: a vector containing the p-values only of the
genetic model at each marker.
se
: a vector containing the standard error of the
estimates.
It is important to realize that the p-values indicate the
significance of the genetic model only (p-value for the hypothesis that
a marker is associated with a phenotype) and thus does not reflect the
usefulness of any other parameters of the model (like structure terms or
cofactors).
In this case the model only contains an intercept
and a dosage effect, thus the beta contains only two
parameters per marker.
result_dos[[1]]$beta[1:3]
## $PotVar0119912
## phenotype1
## 49.5691560
## marker 0.9199989
##
## $PotVar0071966
## phenotype1
## 49.4661016
## marker 0.7807853
##
## $PotVar0119973
## phenotype1
## 49.816541
## marker 0.244213
We can use the skyplot()
function to visualize the
distribution of p-values.
#We get the p-values from the results
pv <- -log10(result_dos$phenotype1$pval)
skyplot(pv, mpmap, main="QTL detection with linear model using dosages")
We see that due to the structure present in this dataset, the
p-values using a simple linear model are not sufficient: structure
corrections are needed. We will see how to implement them using two
kinship corrections in the following section.
Instead of using snp dosages we can provide a haplotype
matrix. All the output will be the same, except the beta
element, which will now include one effect for each haplotype and an
intercept. The rownames of each beta element will be the name of each
haplotype.
result_hap <- map.QTL(phenotypes = mppheno,
genotypes = mphapdose,
ploidy = 4,
map = mpmap)
## Haplotypes have been detected in the genotype matrix.
## 0 % missing genotypes detected.
## No imputation will be performed.
## No Q matrix will be used.
## Linear model will be used.
## Association completed.
str(result_hap,max.level = 2, give.attr = F)
## List of 1
## $ phenotype1:List of 4
## ..$ beta :List of 1010
## ..$ Ftest: Named num [1:1010] 25.9 25.9 27.6 24.5 24.5 ...
## ..$ pval : Named num [1:1010] 1.11e-53 1.28e-53 3.38e-54 3.99e-51 3.99e-51 ...
## ..$ se : Named num [1:1010] 0.731 0.731 0.731 0.741 0.741 ...
result_hap$phenotype1$beta[1]
## $PotVar0119912
## phenotype1
## 48.99728401
## 28 1.32604561
## 69 4.07176654
## 35 1.63556846
## 50 4.11998907
## 9 2.16105648
## 42 1.91555576
## 14 0.52375789
## 39 0.55954709
## 18 0.48009766
## 77 1.89372018
## 10 -0.05168308
## 45 -0.01807987
## 47 0.21642624
## 4 0.69367241
## 15 -0.08707898
## 72 0.14703304
If we look at the p-value distribution we see that there is still a
problem with the p-value calculation due to not accounting for genetic
structure.
pv <- -log10(result_hap$phenotype1$pval)
skyplot(pv, mpmap, main="QTL detection with linear model using haplotypes")
During the rest of this vignette we will use snp dosage and haplotype
dosage data with different analysis. For all the analysis, any of the
two kinds of genotypes can be provided.
Kinship correction
Our tool implements two ways of working with kinship correction which
in literature are sometimes known as the Q and the K methods. The Q
method uses a cofactor that indicates sub-population effect, while the K
method estimates a kinship matrix that is then passed to a mixed model,
where a kinship correction is performed. If the genetic structure is
very strong -so there are clearly identifiable groups within the data-
the Q and K method can provide very similar results. In practice,
subpopulation identity of samples is inaccurate or incomplete, the
relatedness between subpopulations is not equivalent (some are very
similar, some very different), making the K method more accurate.
Let us briefly have a look at Q and K matrices.
\(Q\) matrix
The Q matrix can be calculated using a vector that indicates the
subpopulation of each sample. In the example data this can be seen in
the individual names.
We can use the function calc.Q()
to create a Q matrix
that has the appropiate parametrization for a linear model. This
funciton is not an exported object of mpQTL
since usually
it is only used internally, but we can have a look at it anyway.
#We take the first two characters as population indicator
pop <- substr(colnames(mpsnpdose),1,2)
Q <- mpQTL:::calc.Q(pop)
#Here we assume that our subpopulation identification adequately represents population structure
heatmap(Q,Colv = NA, Rowv = NA,labRow = colnames(mpsnpdose))
\(K\) matrix
We can also calculate a kinship matrix using calc.K()
.
Our formulation is based on the “realized
relationship” in Rosyara et al. 2016. The result has values
that can go below 0 and over 1. Although this scale is somewhat strange,
it allows for a very fast calculation of kinship. The interpretation of
the measure is that 0 is the average relatedness between individuals in
the population, and 1 is the average relatedness of an individual with
itself.
The parameters are:
- matrix: either a dosage matrix (markers in columns
and individuals in rows), or a haplotype matrix (p rows per individual,
where p is ploidy).
- haplotypes: logical, are haplotypes present?
Defaults to False.
- ploidy: integer indicating ploidy. Only used if
haplotypes = T
Kd <- calc.K(t(mpsnpdose))
Kh <- calc.K(t(mphapdose), haplotypes = T, ploidy = 4)
#We can visualize the matrices using heatmaps
heatmap(Kd, Colv = NA, Rowv = NA, main = "Dosage matrix")
heatmap(Kh, Colv = NA, Rowv = NA, main = "Haplotype matrix")
#This helps us identify each family of individuals
pop <- substr(rownames(Kd),1,2)
#Or a bit more clearly using PCoA plots
pcoa.plot(Kd,col = pop, main = "Dosage matrix", h = 240)
#Or a bit more clearly using PCoA plots
pcoa.plot(Kh,col = pop, main = "Haplotype matrix", h = 240)
Although the values within the dosage and haplotype matrix are
somewhat different, we can see using heatmaps and PCoA plots that the
overall structure estimated with the matrices is equivalent.
In the heatmaps, when two individuals are more genetically similar,
their cells are darker, and since our individuals are ordered per
family, this creates a checkboard-like pattern. The heatmaps also show
us which families are more closer related to each other, due to sharing
similar parents.
Lastly, we can use a special type of \(Q\) matrix based on kinship. If we apply
PCoA on the \(K\) matrix (as we do for
visualization) we can obtain a \(Q\)
matrix that identifies subpopulations in a similar way to the K matrix.
This is not as accurate but can reduce computational time substantially
and provide equivalent results if the subpopulation structure is
clear.
K <- calc.K(t(mpsnpdose))
Q <- cmdscale(1 - K, k = 2, eig = FALSE, add = FALSE, x.ret = FALSE)
#This helps us identify each family of individuals
pop <- substr(rownames(Kd),1,2)
pcoa.plot(Q, col = pop, main = "Q matrix", h = 240)
Mixed model (and Q model)
To use the mixed model we do not need to provide directly the kinship
matrix \(K\), map.QTL()
will directly calculate it if K = TRUE
. You can provide
your own distance matrix using
K = your_distance_matrix
.
Importantly, K is calculated using a sample of markers across the
genome, rather than all markers. This prevents that regions where marker
density is higher to contribute more to the kinship than regions that
are less dense. By default, 1 marker per cM, when possible, is used.
This can be changed with the parameter binsize
. If you use
a base-pair map you need to adapt this value to an adequate base-pair
(for example one marker every 10Kb).
result_mix <- map.QTL(phenotypes = mppheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap,
K = T,
binsize = 1)
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## No Q matrix will be used.
## Mixed model will be used.
## Association completed.
str(result_mix, max.level = 2, give.attr = F)
## List of 1
## $ phenotype1:List of 7
## ..$ beta :List of 1010
## ..$ Fstat : Named num [1:1010] 0.2639 0.1422 0.0786 0.5265 0.0551 ...
## ..$ residual:List of 1010
## ..$ pval : Named num [1:1010] 0.608 0.706 0.779 0.468 0.815 ...
## ..$ se : Named num [1:1010] 0.522 0.522 0.522 0.522 0.522 ...
## ..$ wald : Named num [1:1010] -13.21 -11.2 1.46 -2.9 9.07 ...
## ..$ real.df : Named num [1:1010] 0.997 0.997 0.997 0.997 0.997 ...
Besides the results obtained before, we see a wald
result and a real.df
, these relate to the fact that
p-values in mixed models are obtained via an approximation using Wald
tests and realized degrees of freedom. Additionally, the residuals have
been included for each test.
result_mix$phenotype1$beta[1:3]
## $PotVar0119912
## phenotype1
## 161.70236
## marker -0.32833
##
## $PotVar0071966
## phenotype1
## 161.7023575
## marker -0.2943979
##
## $PotVar0119973
## phenotype1
## 161.702357
## marker 0.151665
Note also that in the mixed model no intercept is calculated and thus
the effects can be interpreted as differences with the general mean.
Lastly, if we visualize the p-value distribution we can see that this
time it follows a more reasonable distribution.
pv <- -log10(result_mix$phenotype1$pval)
skyplot(pv, mpmap, main="QTL detection with mixed model")
We can use the Qpco method to obtain a similar result. If we set
Q = TRUE
without providing a vector of population
identifiers, map.QTL()
will automatically calculate a Q
matrix based on kinship. The distribution is now better, but our
population’s structure is too complex to only use the Q correction
method.
result_qpco <- map.QTL(phenotypes = mppheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap,
Q = T,
binsize = 0.1)
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## Linear model with Q correction will be used.
## Association completed.
pv <- -log10(result_qpco$phenotype1$pval)
skyplot(pv, mpmap, main="QTL detection with mixed model")
Covariates and cofactors
Sometimes extra variables might help us explain the phenotypic
variation in our datasets. Depending on whether they are numerical or
categorical variables, they can be called covariates or cofactors,
respectively. In the map.QTL()
function we have included
the possibility of adding additional variables to the model through the
parameters:
cofactor
: a vector or matrix where each column is a
cofactor/covariate.
cofactor.type
: vector with one character string per
column in the cofactor matrix, containing either “numerical” or
“categorical”. If numerical, the variable is used as a regressor in the
model and a single extra effect will be added, thus the covariate must
be numerical. If categorical, an incidence matrix is created using each
unique value of the categorical variable, thus the cofactor can be
numerical/character. One effect per cofactor will be calculated.
We can see the usefulness of including such parameter to the QTL
analysis by looking at the differences in p-values. We will create a
phenotype influenced by a cofactor to observe the differences.
cof <- sample(1:3,size = nrow(mppheno), replace = TRUE)
table(cof)
## cof
## 1 2 3
## 167 134 158
cofpheno <- mppheno + mppheno*cof/10
result_mix <- map.QTL(phenotypes = cofpheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap,
K = T)
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## No Q matrix will be used.
## Mixed model will be used.
## Association completed.
result_cof <- map.QTL(phenotypes = cofpheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap,
K = T,
cofactor = cof,
cofactor.type = "categorical")
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## No Q matrix will be used.
## Mixed model will be used.
## Association completed.
without_cofactor <- -log10(result_mix$phenotype1$pval)
with_cofactor <- -log10(result_cof$phenotype1$pval)
comp.skyplot(list(without_cofactor,with_cofactor),
mpmap,
legnames = c("No cofactor","Cofactor"),
main = "QTL detection with and without cofactor")
Permutation threshold
In order to calculate a significance threshold, a permutation test
can be performed which allows us to find what is the “average” level of
significance in this dataset by permuting phenotypes and genotypes. To
obtain the threshold the QTL mapping process is repeated multiple times,
which may require a long computation time. For our example we will use
only 10 permutations, but generally, the more permutations the higher
the accuracy (e.g. 1000 - 10000).
There are three parameters for threshold calculation:
nperm
: integer, the number of permutations.
permutation
: either “pop” or “fam”, determines the
permutation strategy. If “pop”, it permutes over the whole population,
and if “fam”, only within families. If there is a strong family
structure, the “fam” strategy will produce more accurate thresholds.
However, in a population consisting of many small families (< 30
sibs) the “pop” approach is still preferable.
fam
: a vector of family membership (or population
structure membership), to be provided if
permutation = “fam”
.
alpha
: number between 0 and 1, indicating for which
probability should the threshold be calculated. Default is 0.05 (i.e. a
maximum of 5% of false positives).
For each phenotype a new output can be found named
perm.thresh
result_perm <- map.QTL(phenotypes = mppheno,
genotypes = mpsnpdose,
ploidy = 4,
map = mpmap,
Q = T,
permutation = "pop",
nperm = 10,
alpha = 0.05)
## SNP dosages have been detected in the genotype matrix,
## 0 % missing genotypes detected.
## No imputation will be performed.
## Linear model with Q correction will be used.
## Association completed.
## Permutation threshold will now be calculated.
pv <- -log10(result_perm$phenotype1$pval)
thr <- result_perm$phenotype1$perm.thr
skyplot(pv,mpmap,threshold = thr,main = "QTL detection with permutation test")
Li & Ji threshold
Alternatively, we can compute the number of expected independent
tests as proposed by Li
& Ji 2005.
The effective number of independent test is expected to be lower than
the total number of tests used in the Bonferroni adjustment. For this
reason, the Li & Ji threshold is usually less strict than the
Bonferroni threshold. Notice that this threshold depends on genotypes
only, therefore the same threshold can be used for QTL analyses of
different traits.
result_liji <- thr.LiJi(m = mpsnpdose,
chrom = mpmap$chromosome,
alpha = 0.05,
ploidy = 4)
## Warning in thr.LiJi(m = mpsnpdose, chrom = mpmap$chromosome, alpha = 0.05, : There are 1 monomorphic markers.
## Remove them for subsequent analyses.
## Meff of chromosome 1 = 78
## Meff of chromosome 2 = 72
## Meff of chromosome 3 = 67
## Meff of chromosome 4 = 71
## Meff of chromosome 0 = 32
result_liji
## $threshold
## [1] 0.0001602787
##
## $Meff
## [1] 78 72 67 71 32
-log10(result_liji$threshold)
## [1] 3.795124
Imputation of missing values
In general, few missing values should be included both in phenotypes
and genotypes. The functions, however, are well adapted to handle
missing values. Part of this adaption is the possibility of imputing
genotypes using a k-nearest neighbours (knn) approach. That is, for each
missing value, the \(k\) most
correlated individuals at that genetic region are taken, and a consensus
genotype is obtained to fill in the missing data. If nothing is
specified, map.QTL()
will try to impute the missing
genotypes, although in some cases it might decide it does not have
enough information to accurately impute a genotype.
To control the behaviour of the imputator use:
impute
: logical, should missing genotypes be
imputed?
k
: integer, how many neighbours should be used for
imputation? Defaults to 20.
snp_NA <- mpsnpdose
snp_NA[sample(1:length(mpsnpdose),2000)] <- NA
result_NA <- map.QTL(phenotypes = mppheno,
genotypes = snp_NA,
ploidy = 4,
map = mpmap,
Q = T)
## SNP dosages have been detected in the genotype matrix,
## 0.43 % missing genotypes detected.
## Imputation will be performed on dosages.
## Imputation performed.
## Linear model with Q correction will be used.
## Association completed.
pv <- -log10(result_NA$phenotype1$pval)
skyplot(pv,mpmap,main = "QTL detection with missing genotypes",
threshold = result_perm$phenotype1$perm.thr)
Advanced parameters
Some extra parameters allow to modify the behaviour of certain parts
of map.QTL()
. These parameters include:
no_cores
: integer specifying the number of cores to use
for parallel computing. Defaults to number of cores in the machine - 1.
If running on a server or computer cluster, this parameter should be
specified to avoid asking for too many cores.
linear
: logical, specifying whether a linear model
should be used. If F a mixed model is used. This argument will overrule
the automatic behaviour of map.QTL(), so it is useful to force linear
model behaviour when a custom K matrix wants to be provided (for
imputation, or Qpco definition).
approximate
: by default, the mixed model solution is
achieved using an algorith presented paralelly as P3D or EMMAX, which
essentially approximates part of the calculation between markers which
is very similar, but not identical. By doing that, the speed of
computation is greatly increased, at a practically null cost of
accuracy. If this approximation needs to be deactivated, one can set
approximate = F.
K_identity
: logical. If True, it forces to use a mixed
model that does not correct for genetic structure, but where the K
matrix is used for other purposes (NA imputation, Qpco…).
Visualization
Let us now discuss the visualization functions included in
mpQTL
as well as their interpretations. The visualizations
provided include:
- Principal Coordinate Analysis (PCoA) plots
- Quantile-quantile plots (QQ-plots)
- Manhattan plots (we call them skyline plots)
- Phenotype boxplots based on SNP or haplotype dosage
We can distinguish betwen those visualizations useful before the QTL
analysis (1), those useful to understand the QTL analysis (2 and 3), and
finally those that help us dissect the results of the QTL analysis
(4).
Principal Coordinate Analysis
We have included a function, pcoa.plot() that calculates the
principal components and variance percentages (using R’s own prcomp()
function) and uses them to plot whatever components are desired (1 and 2
by default). It also includes a little function to add colour to these
plots to help dissect the structure. The arguments of pcoa.plot()
include:
K
: any distance/similarity matrix.
comp
: numeric vector of length 2. The principle
components to be used for plotting. A PCoA generates as many components
as dimensions had the original distance matrix. They are ordered by
their explanatory value (components 1 and 2 will always be the most
explanatory). The amount of explained variance of each is always present
in the xlab and ylab of the plot.
plot_legend
: logical, whether the legend should be
plotted.
legspace
: numeric, indicating the amount of space to
leave for the legend. In case a grouping is provided, a legend will be
plotted to the right of the plot. In order not to overlap with the plot,
additional space is added to the right of the plot. The parameter
legspace controls the amount of space left as a proportion to the range
of x values. By default, it takes the value of 0.1 (10% of extra
space).
legname
: the legend names for the colours chosen,
automatically taken from col if not specified.
For PCoA plots, the most useful is to be able to map categories onto
the plot, to assess whether the genetic structure correlates with the
distribution of those categories. To achieve that functionality, two
common plotting parameters have been modified:
col
: instead of expecting a colour definition, col
expects a vector of values (either categorical or numerical). It will
then assign a colour to each unique value and apply it to the PCoA plot
(e.g. c(“pop1”,“pop1”,“pop2”,“pop2”,“pop3”) will generate three colour
categories). With continuous values, this system will not produce ideal
results if not all values in a series are present (i.e. works well for
contiguous values like 1,2,3,4,5; does not work well for non-contiguous
values like 1,5,10,11,46).
pch
: this parameter is used mostly as the classical pch
parameter, but if multiple pch values are provided, the first will be
applied to the first category, the second to the second category, etc.
This might be helpful when there are many categories and only colour
differences might not be enough.
Let us see how this colour and point type mapping work.
#calculate the similarity matrix K
K <- calc.K(t(mpsnpdose))
pop <- substr(colnames(K),1,2)
layout(matrix(1:4,byrow=T,ncol=2))
par(mar=c(4,4,3,1))
pcoa.plot(K,main = "Default PCoA plot", h = c(0,360))
pcoa.plot(K,col = pop,
pch=19, main = "Colour mapping", h = c(0,360))
pcoa.plot(K,col = pop,pch = c(15,17,19),
main = "Different point types", h = c(0,360))
#In this case the legend does not work properly, it's better to turn it off
pcoa.plot(K,col = 1:nrow(K), plot_legend = F,
pch=19, main =" One colour per individual", h = c(0,360))
Quantile-Quantile plot
QQ-plots are useful for determining whether the p-value distribution
of a QTL analysis (y axis) follows the expected distribution (x axis).
In this case, the expected distribution corresponds to a null hypothesis
where there are no truly significant markers (there are only a few false
positives). This expectation can be seen in the plot with a red diagonal
line.
The function QQ.plot()
can be used to generate QQ-plots
of one or multiple vectors of p-values. Each vector of p-values can be
given as a column in a matrix or as an element of a list. The number of
p-values in each vector does not matter. A single set of p-values can
also be provided.
#The p-values are from 500 random normal values
not_sig <- pnorm(rnorm(500),lower.tail = F)
#The p-values are from some random normal values and some non-random
some_sig <- pnorm(c(rnorm(450),rnorm(50,mean = 3)),lower.tail = F)
#The p-values are all too significant
high_sig <- pnorm(rnorm(500,mean=3),lower.tail = F)
#The p-values are all too non-significant
low_sig <- pnorm(rnorm(500),sd = 3,lower.tail = F)
examples <- list(not_sig,some_sig,high_sig,low_sig)
QQ.plot(examples,main="Example QQ-plot",
legnames = c("Not significant",
"Good significant",
"Overestimated",
"Underestimated"))
A legend is automatically added when multiple pvalue sets are
provided. There are some parameters related with the legend:
plot_legend
: logical, can be used to avoid the
generation of the legend.
legnames
: a vector can be provided for the legend
names.
legspace
: numeric, a number indicating how much extra
space (in proportion) must be added to the left of the plot. Can be
useful to tweak it when legend names are too long and they overlap with
points on the QQ-plot.
pvals <- list(lin_dosage = result_dos$phenotype1$pval,
lin_hap = result_hap$phenotype1$pval,
lin_qpco = result_qpco$phenotype1$pval,
mix = result_mix$phenotype1$pval,
lin_Q_NA = result_NA$phenotype1$pval)
QQ.plot(pvals, main = "Model comparison for p-values of pheno1",legspace = 0.22)
#The non-corrected linear models have grossly overestimated p-values
#Better take them out to compare the other models more clearly
QQ.plot(pvals[-1:-2], main = "Model comparison for pvalues of pheno1")
We clearly see how the lack of structure correction in the linear models
has caused a great inflation of the p-values.
Skyline plot
Probably the most relevant plot for QTL mapping is the “Manhattan
plot”, named as such due to its structure, which can remind of the
skyline of of the famous skyscraper district of New York. In this
package, we have opted to name the function skyplot()
,
following the visual metaphor.
A Manhattan plot is a form of marker significance visualization that
helps us recognise regions where multiple significant markers co-locate
around a region, defining a QTL location. On the x-axis the marker’s
position in the genome (physical or genetic) and on the y-axis, the
marker’s significance. Generally, significance is expressed as \(−log10(pval)\), but any other score will
do, as long as it follows that more significant markers have higher
scores, and less significant markers have lower scores (for instance a
Wald score or F-test).
To generate the visualization one must provide both p-values and a
genetic/physical map. The skyplot()
function has the
following arguments:
pval
: vector of pvalues. Importantly, the function
-log10() must be performed by the user. This has been done on purpose,
as we might want to plot other kind of score values with this function
that do not require the -log10 transformation.
map
: a genetic map data.frame with at least columns
“position” and “chromosome”.
threshold
: numeric, optional value to draw a threshold
line.
chrom
: numeric or character vector. If provided, it is
used to select the p-values based on the “chromosome” column of
map.
small
: logical, should the cM scale be drawn? By
default it will only be drawn if only two or a single chromosome are
plotted, otherwise it is too crowded and barely legible. …: other
parameters can be passed to the plot() function. The most relevant is
probably main for the plot title.
pv1 <- -log10(result_mix$phenotype1$pval)
#Just a skyline plot
skyplot(pv1,map = mpmap,main="Example Skyline plot")
#We want to focus on chromosome 2
skyplot(pv1,mpmap,chrom = 2,main ="Skyline lot of chromosome 2",threshold = 3.95)
#The "chromosomes" can also be expressed as characters
map_letters <- mpmap
map_letters$chromosome <- LETTERS[map_letters$chromosome+1]
skyplot(pv1,map_letters,
main="Skyline plot where chromosomes are characters",
chrom = c("C","E"),small = T,threshold = 3.95)
Comparative skyline plot
For our research, we wanted to compare the skyline plots of multiple
models, namely the haplotype-based and dosage-based models. For that
reason, another skyline plot function was developed:
comp.skplot()
. The function is used in a very similar
fashion, with some differences:
- `pval``: each p-value vector must be provided as a column in a
matrix or a vector in a list.
- map: a map must be provided for each set of p-values. If a single
map is provided, it will be assumed that all p-value vectors correspond
to the same map. For the moment, we have not tested what happens if each
map has a different set of chromosomes (i.e. map1 has chromosomes 1, 2
and 3 and map2 has chromosomes 1, 2 and 4), that might cause
conflicts.
chrom
: similarly, we have not tested what would happen
if chromosomes are selected that are present only in one of the two
maps.
legnames
: a vector of names for the legend elements. If
not provided, it will be read from the pvalue list, and if the list has
no names, it will be simply labelled pval 1, pval 2, etc.
legspace
: numeric, the proportion of space to add to
the left of the plot for the legend. It is useful when the legend is too
close to points, and defaults to 0.1.
pch
: the usual numeric pch for plot(), but each value
given will be assigned to each set of p-values. Can help distinguish
points when there are many colours. Will be recycled if not enough pch
values are provided.
alpha
: numeric between 0 (transparent) and 1 (opaque).
By default, the points are plotted with some transparency to be able to
see the multiple distributions, change this parameter to make it
more/less transparent.
pv2 <- -log10(result_dos$phenotype1$pval)
comp.skyplot(list(pv1,pv2),mpmap,
main = "Comparison of two p-value distributions",
threshold = 3.95,pch = c(19,17))
We can also use it to compare the results we have been
generating.
#Remember we must apply the -log10 transformation
pvals <- lapply(pvals,function(i) -log10(i))
comp.skyplot(pvals,map = mpmap,
legspace = 0.22)
#Again the non-corrected models are too outlying
comp.skyplot(pvals[-1:-2],
map= mpmap,
pch = c(15,17,19),legspace = 0.22,
main= "Comparison of models on example data")
Phenotype boxplot
It is useful to correlate the dosage of a single marker with the
value of a phenotype, as sometimes that can reveal “dosage effects”. In
order to simplify the process of generating such boxplots, we have
created a wrapper that makes them using dosages or haplotypes.
There is a single function, pheno_box()
that can use two
different methods, either for dosages or for haplotypes, by changing the
parameter haplotype (False by default). Some parameters behave
identically no matter what the value of haplotype is:
phe
: is a numerical vector of phenotypes
gen
: if haplotype = F, gen should be a numeric vector
of dosages with a single observation per individual. If haplotype = T,
gen should be a vector with p numeric/character observations per
individual where p is the ploidy, and each observation is a haplotype
class (e.g. 120, 140, 128…;A, B, C…; hap1, hap2, hap3…).
draw.points
: is a logical that indicates whether points
should be drawn.
...
: further arguments to be passed to plot()
When haplotype = T other parameters can be used:
ploidy
: is an integer indicating the ploidy.
hap.select
: is a vector of numeric/character indicating
which haplotypes should be plotted.
#We choose the most significant marker in our QTL analysis
best_snp <- which.min(result_mix$phenotype1$pval)
best_hap <- which.min(result_qpco$phenotype1$pval)
gen_snp <- unlist(mpsnpdose[best_snp,])
gen_hap <- unlist(mphapdose[best_hap,])
pheno_box(mppheno[,1],gen_snp,
xlab="Dosage",ylab="phenotype",main="A boxplot of dosages")
#But now there are too many things plotted and I can't see anything
pheno_box(mppheno[,1],gen_hap,haplotype = T,ploidy = 4,
xlab="Haplotypes",ylab="phenotype",main="A boxplot of haplotype dosages")
#This is better but still too many boxes
pheno_box(mppheno[,1],gen_hap,haplotype = T,ploidy = 4,draw.points = F,
xlab="Haplotypes",ylab="phenotype",main="A boxplot of haplotype dosages (no points)")
#This is better
pheno_box(mppheno[,1],gen_hap,haplotype = T,ploidy = 4,
hap.select = c("57","46", "37","69"),
xlab="Haplotypes",ylab="phenotype",
main="A boxplot of some haplotype dosages")
Colour choice
The colour system of the visualization functions in the mpQTL package
is a bit different than the default methods that most R plots include.
To perform colour choice we use the package colorspace, which uses the
HCL (hue, colour tone; chroma, colour intensity; and luminance, colour
lightness) system to define colour. This package has been designed with
data visualization in mind and offers great functionalities for
intelligent and effective colour choice. If you are interested, I highly
recommend visiting their web
page, where they explain the package and many important concepts of
colour theory and design.
In essence, we can choose between qualitative,
sequential or divergent color
palettes. Setting coltype
in most functions to any of these
three systems will change the type of palette used.
A qualitative palette has different colours (hues), and is useful for
categorical variables.
A sequential palette has several tones of the same color
(luminances), and is useful for numerical
variables.
A divergent palette has two opposing colors and an in-between neutral
color. For example red, white and blue. These palettes are useful for
numerical variables that have either very low or very
high values.
In all plotting functions you can use the parameters:
coltype
: standing for colour palette type, we can
choose between “sequential”, “qualitative”, “divergent” or
“rainbow”.
h
: one or two numerical values, standing for hue. The
values are degrees within the colour wheel, and thus values between 0
and 360 are recommended, where 0 and 360 are the same. For a colour
reference you can use hue_wheel()
which produces the plot
below.
l
: one (for qualitative) or two (for sequential)
numerical values. This controls the lightness of the colours, by default
set to 60. Values should be between 20 and 100, below or above the
results will be unexpected.
par(mfrow = c(2,2),
mar = c(1,0,3,0))
for(l in c(40,60,80,100)){
hue_wheel(l = l)
}
LS0tDQp0aXRsZTogIlFUTCBhbmFseXNpcyB3aXRoIHRoZSBSIHBhY2thZ2UgbXBRVEwiDQphdXRob3I6DQogIC0gQWxlamFuZHJvIFRoZXJlc2UgTmF2YXJyb15bV2FnZW5pbmdlbiBVbml2ZXJzaXR5ICYgUmVzZWFyY2gsIGFsZWphbmRyby50aGVyZXNlbmF2YXJyb0B3dXIubmxdDQogIC0gR2lvcmdpbyBUdW1pbm9eW1dhZ2VuaW5nZW4gVW5pdmVyc2l0eSAmIFJlc2VhcmNoLCBnaW9yZ2lvLnR1bWlub0B3dXIubmxdDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvY19jb2xsYXBzZWQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCnRoZW1lOiBmbGF0bHkNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmxpYnJhcnkobXBRVEwpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyB2aWduZXR0ZSB3aWxsIGhlbHAgeW91IHVuZGVyc3RhbmQgaG93IHRvIHVzZSBgbXBRVExgIHRvIHBlcmZvcm0gYSBRVEwgYW5hbHlzaXMuIE91ciBwYWNrYWdlIGlzIA0Kc3BlY2lhbGx5IHN1aXRhYmxlIGlmIHlvdSBhcmUgd29ya2luZyBvbiBwb2x5cGxvaWQgcG9wdWxhdGlvbnMgb3IgaWYgeW91IHVzZSBoYXBsb3R5cGUtYmFzZWQgbWFya2Vycy4gVGhpcyBtZWFucw0KdGhhdCBib3RoIGNsYXNzaWNhbCBiaWFsbGVsaWMgbWFya2VycyAoZS5nLiBjb250aW51b3VzIG9yIGRpc2NyZXRlIFNOUCBkYXRhKSBhbmQgbXVsdGlhbGxlbGljIG1hcmtlcnMgKFNTUnMsIGhhcGxvdHlwZXMsIGV0Yy4pIGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gYSBRVEwgYW5hbHlzaXMuIA0KDQpBdCB0aGUgbW9tZW50IHdlIG9ubHkgc3VwcG9ydCB0aGUgY2xhc3NpY2FsICoqbWl4ZWQgbW9kZWwqKiBhcHByb2FjaCB0byBwZXJmb3JtIEdXQVMtc3R5bGUgUVRMIGFuYWx5c2VzDQpvbiB0aGVzZSBtYXJrZXJzICh3aXRoIG1vZGVscyBzaW1pbGFyIHRvIFtZdSBldCBhbC4gMjAwNl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvbmcxNzAyICJBIHVuaWZpZWQgbWl4ZWQtbW9kZWwgbWV0aG9kIGZvciBhc3NvY2lhdGlvbiBtYXBwaW5nIHRoYXQgYWNjb3VudHMgZm9yIG11bHRpcGxlIGxldmVscyBvZiByZWxhdGVkbmVzcyIpIGFuZCBbR2FyaW4gZXQgYWwuIDIwMTddKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDA3L3MwMDEyMi0wMTctMjkyMy0zICJIb3cgZG8gdGhlIHR5cGUgb2YgUVRMIGVmZmVjdCBhbmQgdGhlIGZvcm0gb2YgdGhlIHJlc2lkdWFsIHRlcm0gaW5mbHVlbmNlIFFUTCBkZXRlY3Rpb24gaW4gbXVsdGktcGFyZW50IHBvcHVsYXRpb25zPyBBIGNhc2Ugc3R1ZHkgaW4gdGhlIG1haXplIEVVLU5BTSBwb3B1bGF0aW9uIikpLiBZb3UgY2FuIGZpbmQgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiANCmluIFtvdXIgcHVibGljYXRpb25dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMjg1OS0wMjItMDQ2MDcteiAiTXVsdGlhbGxlbGljIG1vZGVscyBmb3IgUVRMIG1hcHBpbmcgaW4gZGl2ZXJzZSBwb2x5cGxvaWQgcG9wdWxhdGlvbnMiKS4NCg0KIyMjIyBXaGVuIGlzIGBtcFFUTGAgbW9zdCB1c2VmdWw/DQoNCkluIG91ciByZXNlYXJjaCB3ZSBmb3VuZCB0aGF0IG91ciBtb2RlbCBpcyBtb3N0IHVzZWZ1bCB3aGVuIGFsbGVsaWMgZGl2ZXJzaXR5IGlzIGhpZ2gsIG1lYW5pbmcNCnRoYXQgd2UgY2FuIGV4cGVjdCBtdWx0aXBsZSBhbGxlbGVzIHNlZ3JlZ2F0aW5nIGF0IG9uY2UgaW4gb3VyIHN0dWR5IHBvcHVsYXRpb24uIFdlIGNhbiBleHBlY3QgdGhpcyANCnNpdHVhdGlvbiBpbiBkaXZlcnNlIHBvcHVsYXRpb25zIHdpdGggbXVsdGlwbGUgYW5jZXN0cmFsIGZvdW5kZXJzLCBwb3B1bGF0aW9ucyB3aGVyZSBoZXRlcm96eWdvc2l0eQ0KaXMgaGlnaCwgb3Igd2hlbiBwb2x5cGxvaWR5IChhbmQgaGV0ZXJvenlnb3NpdHkpIGlzIGhpZ2guDQoNCiMgSW5wdXQNCg0KVGhlIHBhY2thZ2UgY29tZXMgd2l0aCBzb21lIGV4YW1wbGUgZGF0YSAoYG1wcXNucGRvc2VgLCBgbXBoYXBkb3NlYCwgYG1wcGVkYCwgYG1wbWFwYCBhbmQgYG1wcGhlbm9gKS4gV2UNCndpbGwgdXNlIHRoaXMgZGF0YSB0byBleHBsYWluIHRoZSBjYXBhYmlsaXRlcyBvZiBgbXBRVExgLg0KDQojIyBHZW5vdHlwZSBkYXRhDQoNClRocmVlIHR5cGVzIG9mIGdlbm90eXBlIGRhdGEgY2FuIGJlIHVuZGVyc3Rvb2QgYnkgYG1wUVRMYDoNCg0KLSAqKlNOUCBkb3NhZ2VzKio6IGEgbWF0cml4IG9mIGRpc2NyZXRlIGRvc2FnZXMsIGZyb20gMCB1bnRpbCB3aGF0ZXZlciBwbG9pZHkgbGV2ZWwsIHdpdGggU05QcyBvbiByb3dzIGFuZCBpbmRpdmlkdWFscyBpbiBjb2x1bW5zLg0KLSAqKkNvbnRpbnVvdXMgU05QIGdlbm90eXBlcyoqOiBhIG1hdHJpeCBvZiBjb250aW51b3VzIGdlbm90eXBlcyB3aXRoIG1hcmtlcnMgaW4gcm93cyBhbmQgaW5kaXZpZHVhbHMgaW4gY29sdW1ucy4gRm9yIGluc3RhbmNlIHRoZSBhbGxlbGUgcmF0aW8gb2YgYXJyYXkgaW50ZW5zaXRpZXMgb3IgcmVhZCBjb3VudHMgJHJlZiAvIChhbHQgKyByZWYpJC4NCi0gKipIYXBsb3R5cGUgbmFtZXMqKjogYSBtYXRyaXggcmVwcmVzZW50aW5nIHRoZSBoYXBsb3R5cGUgY29tcG9zaXRpb24gb2YgZWFjaCBpbmRpdmlkdWFsLiBFYWNoIGNvbHVtbg0KY29ycmVzcG9uZHMgdG8gYW4gaW5kaXZpZHVhbCBjaHJvbW9zb21lLCBhbmQgdGhleSBtdXN0IGJlIG5hbWVkICoqSU5EX24qKiB3aGVyZSAqKklORCoqIGlzIHRoZSBuYW1lIG9mIHRoZSBpbmRpdmlkdWFsIGFuZCAqKm4qKiBpcyB0aGUgY2hyb21vc29tZSBudW1iZXIuIFRoZSB2YWx1ZXMgaW4gdGhpcyBtYXRyaXggYXJlIHRyZWF0ZWQgYXMgY2hhcmFjdGVycywgc28gDQphbnkgdmFsdWUgc2hvdWxkIHdvcmsgd2VsbCBoZXJlLiBJbXBvcnRhbnRseSwgdGhlICpoYXBsb3R5cGUgcGhhc2UqIGRvZXMgbm90IG1hdHRlciAoKmkuZS4qIHlvdSBjYW4gc3dpdGNoDQpoYXBsb3R5cGUgbmFtZXMgYmV0d2VlbiBjb2x1bW5zIGFuZCB0aGUgcmVzdWx0cyB3aWxsIG5vdCBjaGFuZ2UpLg0KDQpJbiBhbGwgbWF0cml4IHR5cGVzIG1pc3NpbmcgdmFsdWVzIGFyZSBhbGxvd2VkLg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShtcHNucGRvc2VbMTo1LDE6OV0sY2FwdGlvbiA9ICJTTlAgZG9zYWdlIikNCg0KI0NvbnRpbnVvdXMgU05QIGdlbm90eXBlcyB3b3VsZCBiZSBzcGVjaWZpZWQgc2ltaWxhcmx5DQprbml0cjo6a2FibGUocm91bmQobXBzbnBkb3NlWzE6NSwxOjldLzQsMiksY2FwdGlvbiA9ICJDb250aW51b3VzIFNOUCBkb3NhZ2UiKQ0KDQojTm90ZSB0aGF0IGhlcmUgd2UgYXJlIG9ubHkgc2hvd2luZyB0aGUgaGFwbG90eXBlcyBvZiB0d28gaW5kaXZpZHVhbHMNCmtuaXRyOjprYWJsZShtcGhhcGRvc2VbMTo1LDE6OF0sIGNhcHRpb24gPSAiSGFwbG90eXBlIG5hbWVzIikNCmBgYA0KDQojIyBHZW5ldGljIG1hcA0KDQpBIGdlbmV0aWMgbWFwIHNob3VsZCBiZSBhIGBkYXRhLmZyYW1lYCB3aXRoIGF0IGxlYXN0IHRocmVlIGNvbHVtbnMgbmFtZWQgYG1hcmtlcmAsIGBjaHJvbW9zb21lYCBhbmQgYHBvc2l0aW9uYC4NCkJ5IGRlZmF1bHQgYG1wUVRMYCBmdW5jdGlvbnMgZXhwZWN0IGEgY2VudGltb3JnYW4gcG9zaXRpb24sIGJ1dCBwYXJhbWV0ZXJzIGNhbiBiZSB0d2Vha2VkIHRvIGFjY2VwdCBhIGJhc2VwYWlyIHBvc2l0aW9uLCB3ZSdsbCBzZWUgdGhhdCBsYXRlci4gDQoNCllvdSBjYW4gYWxzbyBpbmNsdWRlIHVubWFwcGVkIG1hcmtlcnMgaW4gdGhlIGFuYWx5c2lzIGJ5IGluZGljYXRpbmcgdGhhdCB0aGV5IGJlbG9uZyB0byAqKmNocm9tb3NvbWUgMCoqLiBJbXBvcnRhbnRseSwgdGhleSBzaG91bGQgYWxzbyBoYXZlIGEgcG9zaXRpb24uIFRoZSBlYXNpZXN0IG9wdGlvbiBpcyB0byBnaXZlIHRoZW0gc3Vic2VxdWVudCBudW1iZXJzIChmaXJzdCANCm1hcmtlciBwb3NpdGlvbiAxLCBzZWNvbmQgcG9zaXRpb24gMiwgZXRjLikuIElmIG1hbnkgdW5tYXBwZWQgbWFya2VycyBhcmUgcHJlc2VudCBpdCBpcyBiZXR0ZXIgdG8gaGF2ZSB0aGUgbGVuZ3RoIG9mIGNocm9tb3NvbWUgMCBiZSBhIHJlYXNvbmFibGUgc2l6ZSBmb3Igd2hhdGV2ZXIgcG9zaXRpb24gdW5pdCB5b3UgdXNlLiBGb3IgZXhhbXBsZSBpZiB0aGVyZSBhcmUgMTAwMCB1bm1hcHBlZCBtYXJrZXJzIGFuZCB5b3UgdXNlIGNNLCBlYWNoIGNocm9tb3NvbWUgc2hvdWxkIG1lYXN1cmUgYWJvdXQgMTAwIGNNLiBZb3UgY2FuIGdlbmVyYXRlIGVxdWFsbHkgc3BhY2VkIHBvc2l0aW9ucyB3aXRoIGBzZXEoMCwgMTAwLCBsZW5ndGgub3V0ID0gMTAwMClgLg0KDQoNCmBgYHtyfQ0Ka25pdHI6OmthYmxlKG1wbWFwWzE6NSxdLCBjYXB0aW9uID0gIkdlbmV0aWMgbWFwIixyb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBQaGVub3R5cGVzDQoNCkxhc3RseSwgcGhlbm90eXBlcyBjYW4gYmUgZXhwcmVzc2VkIGVpdGhlciBhcyBhIG51bWVyaWMgdmVjdG9yIG9yIGEgbnVtZXJpYyBtYXRyaXgsIHdoZXJlIGVhY2ggY29sdW1uIGlzIGEgZGlmZmVyZW50IHBoZW5vdHlwZS4gV2l0aCBib3RoIGZvcm1hdHMgdGhlIHBoZW5vdHlwZXMgY2FuIGJlIHBhc3NlZCB0byBgbWFwLlFUTCgpYCB0byBnZW5lcmF0ZSB0aGUgUVRMIG1hcHBpbmcgcmVzdWx0cy4NCg0KIyBVc2luZyBgbWFwLlFUTCgpYA0KDQpUaGUgbWFpbiBmdW5jdGlvbiBgbWFwLlFUTCgpYCBjYW4gYmUgdXNlZCB0byBwZXJmb3JtIGEgc2luZ2xlLW1hcmtlciB0ZXN0LiBEZXBlbmRpbmcgb24geW91ciBpbnB1dCBhIGxpbmVhciBtb2RlbCAod2l0aG91dCBraW5zaGlwIGNvcnJlY3Rpb24pIG9yIGEgbWl4ZWQgbW9kZWwgKHdpdGgga2luc2hpcCBjb3JyZWN0aW9uKSB3aWxsIGJlIGNob3Nlbi4gTWlzc2luZyBnZW5vdHlwZXMgY2FuIGJlIGltcHV0ZWQgdXNpbmcgdGhpcyBmdW5jdGlvbi4gRmluYWxseSwgYSBwZXJtdXRhdGlvbiBzaWduaWZpY2FuY2UgdGhyZXNob2xkIGNhbiBhbHNvIGJlIGNvbXB1dGVkIChhbHRob3VnaCBpdCBpbmNyZWFzZXMgY29tcHV0YXRpb25hbCB0aW1lIHF1aXRlIGEgYml0KS4NCg0KTGV0IHVzIGxvb2sgYSB0aGUgZGlmZmVyZW50IHRvb2xzIG9mIGBtYXAuUVRMKClgDQoNCiMjIExpbmVhciBtb2RlbA0KDQpUaGUgYmFzaWMgZm91ciBwYXJhbWV0ZXJzIG9mIG1hcC5RVEwoKSBhcmU6DQoNCiogYHBoZW5vdHlwZXNgOiBhIHZlY3RvciBvciBtYXRyaXggd2l0aCBhIGNvbHVtbiBmb3IgZWFjaCBwaGVub3R5cGUuDQoqIGBnZW5vdHlwZXNgOiBlaXRoZXIgYSBkb3NhZ2Ugb3IgYSBoYXBsb3R5cGUgbWF0cml4Lg0KKiBgcGxvaWR5YDogYW4gaW50ZWdlciBpbmRpY2F0aW5nIHRoZSBwbG9pZHkgb2YgdGhlIG9yZ2FuaXNtLg0KKiBgbWFwYDogYSBnZW5ldGljIG1hcCB3aXRoIHRoZSBjb2x1bW5zIOKAnG1hcmtlcuKAnSwg4oCcY2hyb21vc29tZeKAnSwgYW5kIOKAnHBvc2l0aW9u4oCdLg0KDQpUaGUgc2ltcGxlc3QgUVRMIG1vZGVsIHRoYXQgYG1hcC5RVEwoKWAgcHJvdmlkZXMgaXMgYSBsaW5lYXIgbW9kZWwgd2l0aCBubyBrbnNoaXAgY29ycmVjdGlvbi4gSW4gdGhpcyBjYXNlLCB0aGUgcC12YWx1ZXMgb3JpZ2luYXRlIGZyb20gYW4gRi10ZXN0Lg0KDQpgYGB7cn0NCnJlc3VsdF9kb3MgPC0gbWFwLlFUTChwaGVub3R5cGVzID0gbXBwaGVubywNCiAgICAgICAgZ2Vub3R5cGVzID0gbXBzbnBkb3NlLA0KICAgICAgICBwbG9pZHkgPSA0LA0KICAgICAgICBtYXAgPSBtcG1hcCkNCg0Kc3RyKHJlc3VsdF9kb3MsbWF4LmxldmVsID0gMiwgZ2l2ZS5hdHRyID0gRikNCg0KYGBgDQoNClRoZSByZXN1bHRzIGFyZSBwcm92aWRlZCBpbiBmb3JtIG9mIGEgbmVzdGVkIGxpc3QuIEZpcnN0bHksIHRoZXJlIGlzIGFuIGVsZW1lbnQgZm9yIGVhY2ggcGhlbm90eXBlLCBhbmQgd2l0aGluIGVhY2ggcGhlbm90eXBlIHRoZXJlIGFyZSBmb3VyIGVsZW1lbnRzOg0KDQoqIGBiZXRhYDogYSBsaXN0IGNvbnRhaW5pbmcgdGhlIGZpeGVkIGVmZmVjdHMgb2YgdGhlIG1vZGVsLiBUaGVyZSBpcyBhIHNldCBvZiBlc3RpbWF0ZXMgZm9yIGVhY2ggbWFya2VyLg0KKiBgRnRlc3RgOiBhIHZlY3RvciBjb250YWluaW5nIHRoZSBGdGVzdCByZXN1bHRzIG9ubHkgZm9yIHRoZSBnZW5ldGljIGNvbXBvbmVudCBvZiBlYWNoIG1vZGVsLg0KKiBgcHZhbGA6IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHAtdmFsdWVzIG9ubHkgb2YgdGhlIGdlbmV0aWMgbW9kZWwgYXQgZWFjaCBtYXJrZXIuDQoqIGBzZWA6IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBlc3RpbWF0ZXMuDQoNCkl0IGlzIGltcG9ydGFudCB0byByZWFsaXplIHRoYXQgdGhlIHAtdmFsdWVzIGluZGljYXRlIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIGdlbmV0aWMgbW9kZWwgb25seSAocC12YWx1ZSBmb3IgdGhlIGh5cG90aGVzaXMgdGhhdCBhIG1hcmtlciBpcyBhc3NvY2lhdGVkIHdpdGggYSBwaGVub3R5cGUpIGFuZCB0aHVzIGRvZXMgbm90IHJlZmxlY3QgdGhlIHVzZWZ1bG5lc3Mgb2YgYW55IG90aGVyIHBhcmFtZXRlcnMgb2YgdGhlIG1vZGVsIChsaWtlIHN0cnVjdHVyZSB0ZXJtcyBvciBjb2ZhY3RvcnMpLg0KDQpJbiB0aGlzIGNhc2UgdGhlIG1vZGVsIG9ubHkgY29udGFpbnMgYW4gKippbnRlcmNlcHQqKiBhbmQgYSAqKmRvc2FnZSBlZmZlY3QqKiwgdGh1cyB0aGUgYmV0YSBjb250YWlucyBvbmx5IHR3byBwYXJhbWV0ZXJzIHBlciBtYXJrZXIuDQoNCmBgYHtyfQ0KcmVzdWx0X2Rvc1tbMV1dJGJldGFbMTozXQ0KYGBgDQoNCldlIGNhbiB1c2UgdGhlIGBza3lwbG90KClgIGZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzLg0KDQpgYGB7cn0NCiNXZSBnZXQgdGhlIHAtdmFsdWVzIGZyb20gdGhlIHJlc3VsdHMNCnB2IDwtIC1sb2cxMChyZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbGluZWFyIG1vZGVsIHVzaW5nIGRvc2FnZXMiKQ0KYGBgDQoNCldlIHNlZSB0aGF0IGR1ZSB0byB0aGUgc3RydWN0dXJlIHByZXNlbnQgaW4gdGhpcyBkYXRhc2V0LCB0aGUgcC12YWx1ZXMgdXNpbmcgYSBzaW1wbGUgbGluZWFyIG1vZGVsIGFyZSBub3Qgc3VmZmljaWVudDogc3RydWN0dXJlIGNvcnJlY3Rpb25zIGFyZSBuZWVkZWQuIFdlIHdpbGwgc2VlIGhvdyB0byBpbXBsZW1lbnQgdGhlbSB1c2luZyB0d28ga2luc2hpcCBjb3JyZWN0aW9ucyBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb24uDQoNCkluc3RlYWQgb2YgdXNpbmcgc25wIGRvc2FnZXMgd2UgY2FuIHByb3ZpZGUgYSAqKmhhcGxvdHlwZSBtYXRyaXgqKi4gQWxsIHRoZSBvdXRwdXQgd2lsbCBiZSB0aGUgc2FtZSwgZXhjZXB0IHRoZSBiZXRhIGVsZW1lbnQsIHdoaWNoIHdpbGwgbm93IGluY2x1ZGUgb25lIGVmZmVjdCBmb3IgZWFjaCBoYXBsb3R5cGUgYW5kIGFuIGludGVyY2VwdC4gVGhlIHJvd25hbWVzIG9mIGVhY2ggYmV0YSBlbGVtZW50IHdpbGwgYmUgdGhlIG5hbWUgb2YgZWFjaCBoYXBsb3R5cGUuDQoNCmBgYHtyfQ0KcmVzdWx0X2hhcCA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBtcHBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcGhhcGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwKQ0KDQpzdHIocmVzdWx0X2hhcCxtYXgubGV2ZWwgPSAyLCBnaXZlLmF0dHIgPSBGKQ0KcmVzdWx0X2hhcCRwaGVub3R5cGUxJGJldGFbMV0NCmBgYA0KDQpJZiB3ZSBsb29rIGF0IHRoZSBwLXZhbHVlIGRpc3RyaWJ1dGlvbiB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBzdGlsbCBhIHByb2JsZW0gd2l0aCB0aGUgcC12YWx1ZSBjYWxjdWxhdGlvbiBkdWUgdG8gbm90IGFjY291bnRpbmcgZm9yIGdlbmV0aWMgc3RydWN0dXJlLg0KDQpgYGB7cn0NCnB2IDwtIC1sb2cxMChyZXN1bHRfaGFwJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbGluZWFyIG1vZGVsIHVzaW5nIGhhcGxvdHlwZXMiKQ0KYGBgDQoNCkR1cmluZyB0aGUgcmVzdCBvZiB0aGlzIHZpZ25ldHRlIHdlIHdpbGwgdXNlIHNucCBkb3NhZ2UgYW5kIGhhcGxvdHlwZSBkb3NhZ2UgZGF0YSB3aXRoIGRpZmZlcmVudCBhbmFseXNpcy4gRm9yIGFsbCB0aGUgYW5hbHlzaXMsIGFueSBvZiB0aGUgdHdvIGtpbmRzIG9mIGdlbm90eXBlcyBjYW4gYmUgcHJvdmlkZWQuDQoNCiMjIEtpbnNoaXAgY29ycmVjdGlvbg0KDQpPdXIgdG9vbCBpbXBsZW1lbnRzIHR3byB3YXlzIG9mIHdvcmtpbmcgd2l0aCBraW5zaGlwIGNvcnJlY3Rpb24gd2hpY2ggaW4gbGl0ZXJhdHVyZSBhcmUgc29tZXRpbWVzIGtub3duIGFzIHRoZSBRIGFuZCB0aGUgSyBtZXRob2RzLiBUaGUgUSBtZXRob2QgdXNlcyBhIGNvZmFjdG9yIHRoYXQgaW5kaWNhdGVzIHN1Yi1wb3B1bGF0aW9uIGVmZmVjdCwgd2hpbGUgdGhlIEsgbWV0aG9kIGVzdGltYXRlcyBhIGtpbnNoaXAgbWF0cml4IHRoYXQgaXMgdGhlbiBwYXNzZWQgdG8gYSBtaXhlZCBtb2RlbCwgd2hlcmUgYSBraW5zaGlwIGNvcnJlY3Rpb24gaXMgcGVyZm9ybWVkLiBJZiB0aGUgZ2VuZXRpYyBzdHJ1Y3R1cmUgaXMgdmVyeSBzdHJvbmcgLXNvIHRoZXJlIGFyZSBjbGVhcmx5IGlkZW50aWZpYWJsZSBncm91cHMgd2l0aGluIHRoZSBkYXRhLSB0aGUgUSBhbmQgSyBtZXRob2QgY2FuIHByb3ZpZGUgdmVyeSBzaW1pbGFyIHJlc3VsdHMuIEluIHByYWN0aWNlLCBzdWJwb3B1bGF0aW9uIGlkZW50aXR5IG9mIHNhbXBsZXMgaXMgaW5hY2N1cmF0ZSBvciBpbmNvbXBsZXRlLCB0aGUgcmVsYXRlZG5lc3MgYmV0d2VlbiBzdWJwb3B1bGF0aW9ucyBpcyBub3QgZXF1aXZhbGVudCAoc29tZSBhcmUgdmVyeSBzaW1pbGFyLCBzb21lIHZlcnkgZGlmZmVyZW50KSwgbWFraW5nIHRoZSBLIG1ldGhvZCBtb3JlIGFjY3VyYXRlLg0KDQpMZXQgdXMgYnJpZWZseSBoYXZlIGEgbG9vayBhdCBRIGFuZCBLIG1hdHJpY2VzLg0KDQojIyMgJFEkIG1hdHJpeA0KDQpUaGUgUSBtYXRyaXggY2FuIGJlIGNhbGN1bGF0ZWQgdXNpbmcgYSB2ZWN0b3IgdGhhdCBpbmRpY2F0ZXMgdGhlIHN1YnBvcHVsYXRpb24gb2YgZWFjaCBzYW1wbGUuIEluIHRoZSBleGFtcGxlIGRhdGEgdGhpcyBjYW4gYmUgc2VlbiBpbiB0aGUgaW5kaXZpZHVhbCBuYW1lcy4NCg0KV2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGNhbGMuUSgpYCB0byBjcmVhdGUgYSBRIG1hdHJpeCB0aGF0IGhhcyB0aGUgYXBwcm9waWF0ZSBwYXJhbWV0cml6YXRpb24gZm9yIGEgbGluZWFyIG1vZGVsLiBUaGlzIGZ1bmNpdG9uIGlzIG5vdCBhbiBleHBvcnRlZCBvYmplY3Qgb2YgYG1wUVRMYCBzaW5jZSB1c3VhbGx5IGl0IGlzIG9ubHkgdXNlZCBpbnRlcm5hbGx5LCBidXQgd2UgY2FuIGhhdmUgYSBsb29rIGF0IGl0IGFueXdheS4NCg0KYGBge3J9DQojV2UgdGFrZSB0aGUgZmlyc3QgdHdvIGNoYXJhY3RlcnMgYXMgcG9wdWxhdGlvbiBpbmRpY2F0b3INCnBvcCA8LSBzdWJzdHIoY29sbmFtZXMobXBzbnBkb3NlKSwxLDIpDQpRIDwtIG1wUVRMOjo6Y2FsYy5RKHBvcCkNCg0KI0hlcmUgd2UgYXNzdW1lIHRoYXQgb3VyIHN1YnBvcHVsYXRpb24gaWRlbnRpZmljYXRpb24gYWRlcXVhdGVseSByZXByZXNlbnRzIHBvcHVsYXRpb24gc3RydWN0dXJlDQpoZWF0bWFwKFEsQ29sdiA9IE5BLCBSb3d2ID0gTkEsbGFiUm93ID0gY29sbmFtZXMobXBzbnBkb3NlKSkNCmBgYA0KDQojIyMgJEskIG1hdHJpeA0KDQpXZSBjYW4gYWxzbyBjYWxjdWxhdGUgYSBraW5zaGlwIG1hdHJpeCB1c2luZyBgY2FsYy5LKClgLiBPdXIgZm9ybXVsYXRpb24gaXMgYmFzZWQgb24gdGhlIFsicmVhbGl6ZWQgcmVsYXRpb25zaGlwIiBpbiBSb3N5YXJhICpldCBhbC4qIDIwMTZdKGh0dHBzOi8vZG9pLm9yZy8xMC4zODM1L3BsYW50Z2Vub21lMjAxNS4wOC4wMDczICJTb2Z0d2FyZSBmb3IgR2Vub21lLVdpZGUgQXNzb2NpYXRpb24gU3R1ZGllcyBpbiBBdXRvcG9seXBsb2lkcyBhbmQgSXRzIEFwcGxpY2F0aW9uIHRvIFBvdGF0byIpLiBUaGUgcmVzdWx0IGhhcyB2YWx1ZXMgdGhhdCBjYW4gZ28gYmVsb3cgMCBhbmQgb3ZlciAxLiBBbHRob3VnaCB0aGlzIHNjYWxlIGlzIHNvbWV3aGF0IHN0cmFuZ2UsIGl0IGFsbG93cyBmb3IgYSB2ZXJ5IGZhc3QgY2FsY3VsYXRpb24gb2Yga2luc2hpcC4gVGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBtZWFzdXJlIGlzIHRoYXQgMCBpcyB0aGUgYXZlcmFnZSByZWxhdGVkbmVzcyBiZXR3ZWVuIGluZGl2aWR1YWxzIGluIHRoZSBwb3B1bGF0aW9uLCBhbmQgMSBpcyB0aGUgYXZlcmFnZSByZWxhdGVkbmVzcyBvZiBhbiBpbmRpdmlkdWFsIHdpdGggaXRzZWxmLiANCg0KVGhlIHBhcmFtZXRlcnMgYXJlOg0KDQotICoqbWF0cml4Kio6IGVpdGhlciBhIGRvc2FnZSBtYXRyaXggKG1hcmtlcnMgaW4gY29sdW1ucyBhbmQgaW5kaXZpZHVhbHMgaW4gcm93cyksIG9yIGEgaGFwbG90eXBlIG1hdHJpeCAocA0KIHJvd3MgcGVyIGluZGl2aWR1YWwsIHdoZXJlIHANCiBpcyBwbG9pZHkpLg0KLSAqKmhhcGxvdHlwZXMqKjogbG9naWNhbCwgYXJlIGhhcGxvdHlwZXMgcHJlc2VudD8gRGVmYXVsdHMgdG8gRmFsc2UuDQotICoqcGxvaWR5Kio6IGludGVnZXIgaW5kaWNhdGluZyBwbG9pZHkuIE9ubHkgdXNlZCBpZiBoYXBsb3R5cGVzID0gVA0KDQpgYGB7cn0NCktkIDwtIGNhbGMuSyh0KG1wc25wZG9zZSkpDQpLaCA8LSBjYWxjLksodChtcGhhcGRvc2UpLCBoYXBsb3R5cGVzID0gVCwgcGxvaWR5ID0gNCkNCg0KI1dlIGNhbiB2aXN1YWxpemUgdGhlIG1hdHJpY2VzIHVzaW5nIGhlYXRtYXBzDQpoZWF0bWFwKEtkLCBDb2x2ID0gTkEsIFJvd3YgPSBOQSwgbWFpbiA9ICJEb3NhZ2UgbWF0cml4IikNCmhlYXRtYXAoS2gsIENvbHYgPSBOQSwgUm93diA9IE5BLCBtYWluID0gIkhhcGxvdHlwZSBtYXRyaXgiKQ0KDQojVGhpcyBoZWxwcyB1cyBpZGVudGlmeSBlYWNoIGZhbWlseSBvZiBpbmRpdmlkdWFscw0KcG9wIDwtIHN1YnN0cihyb3duYW1lcyhLZCksMSwyKQ0KI09yIGEgYml0IG1vcmUgY2xlYXJseSB1c2luZyBQQ29BIHBsb3RzDQpwY29hLnBsb3QoS2QsY29sID0gcG9wLCBtYWluID0gIkRvc2FnZSBtYXRyaXgiLCBoID0gMjQwKQ0KI09yIGEgYml0IG1vcmUgY2xlYXJseSB1c2luZyBQQ29BIHBsb3RzDQpwY29hLnBsb3QoS2gsY29sID0gcG9wLCBtYWluID0gIkhhcGxvdHlwZSBtYXRyaXgiLCBoID0gMjQwKQ0KYGBgDQoNCkFsdGhvdWdoIHRoZSB2YWx1ZXMgd2l0aGluIHRoZSBkb3NhZ2UgYW5kIGhhcGxvdHlwZSBtYXRyaXggYXJlIHNvbWV3aGF0IGRpZmZlcmVudCwgd2UgY2FuIHNlZSB1c2luZyBoZWF0bWFwcyBhbmQgUENvQSBwbG90cyB0aGF0IHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBlc3RpbWF0ZWQgd2l0aCB0aGUgbWF0cmljZXMgaXMgZXF1aXZhbGVudC4NCg0KSW4gdGhlIGhlYXRtYXBzLCB3aGVuIHR3byBpbmRpdmlkdWFscyBhcmUgbW9yZSBnZW5ldGljYWxseSBzaW1pbGFyLCB0aGVpciBjZWxscyBhcmUgZGFya2VyLCBhbmQgc2luY2Ugb3VyIGluZGl2aWR1YWxzIGFyZSBvcmRlcmVkIHBlciBmYW1pbHksIHRoaXMgY3JlYXRlcyBhIGNoZWNrYm9hcmQtbGlrZSBwYXR0ZXJuLiBUaGUgaGVhdG1hcHMgYWxzbyBzaG93IHVzIHdoaWNoIGZhbWlsaWVzIGFyZSBtb3JlIGNsb3NlciByZWxhdGVkIHRvIGVhY2ggb3RoZXIsIGR1ZSB0byBzaGFyaW5nIHNpbWlsYXIgcGFyZW50cy4NCg0KTGFzdGx5LCB3ZSBjYW4gdXNlIGEgc3BlY2lhbCB0eXBlIG9mICRRJCBtYXRyaXggYmFzZWQgb24ga2luc2hpcC4gSWYgd2UgYXBwbHkgUENvQSBvbiB0aGUgJEskIG1hdHJpeCAoYXMgd2UgZG8gZm9yIHZpc3VhbGl6YXRpb24pIHdlIGNhbiBvYnRhaW4gYSAkUSQgbWF0cml4IHRoYXQgaWRlbnRpZmllcyBzdWJwb3B1bGF0aW9ucyBpbiBhIHNpbWlsYXIgd2F5IHRvIHRoZSBLIG1hdHJpeC4gVGhpcyBpcyBub3QgYXMgYWNjdXJhdGUgYnV0IGNhbiByZWR1Y2UgY29tcHV0YXRpb25hbCB0aW1lIHN1YnN0YW50aWFsbHkgYW5kIHByb3ZpZGUgZXF1aXZhbGVudCByZXN1bHRzIGlmIHRoZSBzdWJwb3B1bGF0aW9uIHN0cnVjdHVyZSBpcyBjbGVhci4NCg0KDQpgYGB7cn0NCksgPC0gY2FsYy5LKHQobXBzbnBkb3NlKSkNClEgPC0gY21kc2NhbGUoMSAtIEssIGsgPSAyLCBlaWcgPSBGQUxTRSwgYWRkID0gRkFMU0UsIHgucmV0ID0gRkFMU0UpDQoNCiNUaGlzIGhlbHBzIHVzIGlkZW50aWZ5IGVhY2ggZmFtaWx5IG9mIGluZGl2aWR1YWxzDQpwb3AgPC0gc3Vic3RyKHJvd25hbWVzKEtkKSwxLDIpDQoNCnBjb2EucGxvdChRLCBjb2wgPSBwb3AsIG1haW4gPSAiUSBtYXRyaXgiLCBoID0gMjQwKQ0KYGBgDQoNCiMjIE1peGVkIG1vZGVsIChhbmQgUSBtb2RlbCkNCg0KVG8gdXNlIHRoZSBtaXhlZCBtb2RlbCB3ZSBkbyBub3QgbmVlZCB0byBwcm92aWRlIGRpcmVjdGx5IHRoZSBraW5zaGlwIG1hdHJpeCAkSyQsIGBtYXAuUVRMKClgIHdpbGwgZGlyZWN0bHkgY2FsY3VsYXRlIGl0IGlmIGBLID0gVFJVRWAuIFlvdSBjYW4gcHJvdmlkZSB5b3VyIG93biBkaXN0YW5jZSBtYXRyaXggdXNpbmcgYEsgPSB5b3VyX2Rpc3RhbmNlX21hdHJpeGAuDQoNCkltcG9ydGFudGx5LCBLIGlzIGNhbGN1bGF0ZWQgdXNpbmcgYSBzYW1wbGUgb2YgbWFya2VycyBhY3Jvc3MgdGhlIGdlbm9tZSwgcmF0aGVyIHRoYW4gYWxsIG1hcmtlcnMuIFRoaXMgcHJldmVudHMgdGhhdCByZWdpb25zIHdoZXJlIG1hcmtlciBkZW5zaXR5IGlzIGhpZ2hlciB0byBjb250cmlidXRlIG1vcmUgdG8gdGhlIGtpbnNoaXAgdGhhbiByZWdpb25zIHRoYXQgYXJlIGxlc3MgZGVuc2UuIEJ5IGRlZmF1bHQsIDEgbWFya2VyIHBlciBjTSwgd2hlbiBwb3NzaWJsZSwgaXMgdXNlZC4gVGhpcyBjYW4gYmUgY2hhbmdlZCB3aXRoIHRoZSBwYXJhbWV0ZXIgYGJpbnNpemVgLiBJZiB5b3UgdXNlIGEgYmFzZS1wYWlyIG1hcCB5b3UgbmVlZCB0byBhZGFwdCB0aGlzIHZhbHVlIHRvIGFuIGFkZXF1YXRlIGJhc2UtcGFpciAoZm9yIGV4YW1wbGUgb25lIG1hcmtlciBldmVyeSAxMEtiKS4NCg0KYGBge3J9DQpyZXN1bHRfbWl4IDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IG1wc25wZG9zZSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIEsgPSBULA0KICAgICAgICBiaW5zaXplID0gMSkNCg0Kc3RyKHJlc3VsdF9taXgsIG1heC5sZXZlbCA9IDIsIGdpdmUuYXR0ciA9IEYpDQpgYGANCg0KQmVzaWRlcyB0aGUgcmVzdWx0cyBvYnRhaW5lZCBiZWZvcmUsIHdlIHNlZSBhIGB3YWxkYCByZXN1bHQgYW5kIGEgYHJlYWwuZGZgLCB0aGVzZSByZWxhdGUgdG8gdGhlIGZhY3QgdGhhdCBwLXZhbHVlcyBpbiBtaXhlZCBtb2RlbHMgYXJlIG9idGFpbmVkIHZpYSBhbiBhcHByb3hpbWF0aW9uIHVzaW5nIFdhbGQgdGVzdHMgYW5kIHJlYWxpemVkIGRlZ3JlZXMgb2YgZnJlZWRvbS4gQWRkaXRpb25hbGx5LCB0aGUgcmVzaWR1YWxzIGhhdmUgYmVlbiBpbmNsdWRlZCBmb3IgZWFjaCB0ZXN0Lg0KDQpgYGB7cn0NCnJlc3VsdF9taXgkcGhlbm90eXBlMSRiZXRhWzE6M10NCmBgYA0KDQpOb3RlIGFsc28gdGhhdCBpbiB0aGUgbWl4ZWQgbW9kZWwgbm8gaW50ZXJjZXB0IGlzIGNhbGN1bGF0ZWQgYW5kIHRodXMgdGhlIGVmZmVjdHMgY2FuIGJlIGludGVycHJldGVkIGFzIGRpZmZlcmVuY2VzIHdpdGggdGhlIGdlbmVyYWwgbWVhbi4NCg0KTGFzdGx5LCBpZiB3ZSB2aXN1YWxpemUgdGhlIHAtdmFsdWUgZGlzdHJpYnV0aW9uIHdlIGNhbiBzZWUgdGhhdCB0aGlzIHRpbWUgaXQgZm9sbG93cyBhIG1vcmUgcmVhc29uYWJsZSBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KcHYgPC0gLWxvZzEwKHJlc3VsdF9taXgkcGhlbm90eXBlMSRwdmFsKQ0Kc2t5cGxvdChwdiwgbXBtYXAsIG1haW49IlFUTCBkZXRlY3Rpb24gd2l0aCBtaXhlZCBtb2RlbCIpDQpgYGANCg0KV2UgY2FuIHVzZSB0aGUgUXBjbyBtZXRob2QgdG8gb2J0YWluIGEgc2ltaWxhciByZXN1bHQuIElmIHdlIHNldCBgUSA9IFRSVUVgIHdpdGhvdXQgcHJvdmlkaW5nIGEgdmVjdG9yIG9mIHBvcHVsYXRpb24gaWRlbnRpZmllcnMsIGBtYXAuUVRMKClgIHdpbGwgYXV0b21hdGljYWxseSBjYWxjdWxhdGUgYSBRIG1hdHJpeCBiYXNlZCBvbiBraW5zaGlwLiBUaGUgZGlzdHJpYnV0aW9uIGlzIG5vdyBiZXR0ZXIsIGJ1dCBvdXIgcG9wdWxhdGlvbidzIHN0cnVjdHVyZSBpcyB0b28gY29tcGxleCB0byBvbmx5IHVzZSB0aGUgUSBjb3JyZWN0aW9uIG1ldGhvZC4NCg0KYGBge3J9DQpyZXN1bHRfcXBjbyA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBtcHBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcHNucGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwLA0KICAgICAgICBRID0gVCwNCiAgICAgICAgYmluc2l6ZSA9IDAuMSkNCg0KcHYgPC0gLWxvZzEwKHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbWl4ZWQgbW9kZWwiKQ0KYGBgDQoNCiMjIENvdmFyaWF0ZXMgYW5kIGNvZmFjdG9ycw0KDQpTb21ldGltZXMgZXh0cmEgdmFyaWFibGVzIG1pZ2h0IGhlbHAgdXMgZXhwbGFpbiB0aGUgcGhlbm90eXBpYyB2YXJpYXRpb24gaW4gb3VyIGRhdGFzZXRzLiBEZXBlbmRpbmcgb24gd2hldGhlciB0aGV5IGFyZSBudW1lcmljYWwgb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB0aGV5IGNhbiBiZSBjYWxsZWQgY292YXJpYXRlcyBvciBjb2ZhY3RvcnMsIHJlc3BlY3RpdmVseS4gSW4gdGhlIGBtYXAuUVRMKClgIGZ1bmN0aW9uIHdlIGhhdmUgaW5jbHVkZWQgdGhlIHBvc3NpYmlsaXR5IG9mIGFkZGluZyBhZGRpdGlvbmFsIHZhcmlhYmxlcyB0byB0aGUgbW9kZWwgdGhyb3VnaCB0aGUgcGFyYW1ldGVyczoNCg0KKiBgY29mYWN0b3JgOiBhIHZlY3RvciBvciBtYXRyaXggd2hlcmUgZWFjaCBjb2x1bW4gaXMgYSBjb2ZhY3Rvci9jb3ZhcmlhdGUuDQoqIGBjb2ZhY3Rvci50eXBlYDogdmVjdG9yIHdpdGggb25lIGNoYXJhY3RlciBzdHJpbmcgcGVyIGNvbHVtbiBpbiB0aGUgY29mYWN0b3IgbWF0cml4LCBjb250YWluaW5nIGVpdGhlciDigJxudW1lcmljYWzigJ0gb3Ig4oCcY2F0ZWdvcmljYWzigJ0uIElmIG51bWVyaWNhbCwgdGhlIHZhcmlhYmxlIGlzIHVzZWQgYXMgYSByZWdyZXNzb3IgaW4gdGhlIG1vZGVsIGFuZCBhIHNpbmdsZSBleHRyYSBlZmZlY3Qgd2lsbCBiZSBhZGRlZCwgdGh1cyB0aGUgY292YXJpYXRlIG11c3QgYmUgbnVtZXJpY2FsLiBJZiBjYXRlZ29yaWNhbCwgYW4gaW5jaWRlbmNlIG1hdHJpeCBpcyBjcmVhdGVkIHVzaW5nIGVhY2ggdW5pcXVlIHZhbHVlIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgdGh1cyB0aGUgY29mYWN0b3IgY2FuIGJlIG51bWVyaWNhbC9jaGFyYWN0ZXIuIE9uZSBlZmZlY3QgcGVyIGNvZmFjdG9yIHdpbGwgYmUgY2FsY3VsYXRlZC4NCg0KV2UgY2FuIHNlZSB0aGUgdXNlZnVsbmVzcyBvZiBpbmNsdWRpbmcgc3VjaCBwYXJhbWV0ZXIgdG8gdGhlIFFUTCBhbmFseXNpcyBieSBsb29raW5nIGF0IHRoZSBkaWZmZXJlbmNlcyBpbiBwLXZhbHVlcy4gV2Ugd2lsbCBjcmVhdGUgYSBwaGVub3R5cGUgaW5mbHVlbmNlZCBieSBhIGNvZmFjdG9yIHRvIG9ic2VydmUgdGhlIGRpZmZlcmVuY2VzLg0KDQpgYGB7cn0NCmNvZiA8LSBzYW1wbGUoMTozLHNpemUgPSBucm93KG1wcGhlbm8pLCByZXBsYWNlID0gVFJVRSkNCnRhYmxlKGNvZikNCmNvZnBoZW5vIDwtIG1wcGhlbm8gKyBtcHBoZW5vKmNvZi8xMA0KDQpyZXN1bHRfbWl4IDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IGNvZnBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcHNucGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwLA0KICAgICAgICBLID0gVCkNCg0KcmVzdWx0X2NvZiA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBjb2ZwaGVubywNCiAgICAgICAgZ2Vub3R5cGVzID0gbXBzbnBkb3NlLA0KICAgICAgICBwbG9pZHkgPSA0LA0KICAgICAgICBtYXAgPSBtcG1hcCwNCiAgICAgICAgSyA9IFQsDQogICAgICAgIGNvZmFjdG9yID0gY29mLA0KICAgICAgICBjb2ZhY3Rvci50eXBlID0gImNhdGVnb3JpY2FsIikNCg0Kd2l0aG91dF9jb2ZhY3RvciA8LSAtbG9nMTAocmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwpDQp3aXRoX2NvZmFjdG9yIDwtIC1sb2cxMChyZXN1bHRfY29mJHBoZW5vdHlwZTEkcHZhbCkNCmNvbXAuc2t5cGxvdChsaXN0KHdpdGhvdXRfY29mYWN0b3Isd2l0aF9jb2ZhY3RvciksDQogICAgICAgICAgICAgbXBtYXAsDQogICAgICAgICAgICAgbGVnbmFtZXMgPSBjKCJObyBjb2ZhY3RvciIsIkNvZmFjdG9yIiksDQogICAgICAgICAgICAgbWFpbiA9ICJRVEwgZGV0ZWN0aW9uIHdpdGggYW5kIHdpdGhvdXQgY29mYWN0b3IiKQ0KYGBgDQoNCiMjIFBlcm11dGF0aW9uIHRocmVzaG9sZA0KDQpJbiBvcmRlciB0byBjYWxjdWxhdGUgYSBzaWduaWZpY2FuY2UgdGhyZXNob2xkLCBhIHBlcm11dGF0aW9uIHRlc3QgY2FuIGJlIHBlcmZvcm1lZCB3aGljaCBhbGxvd3MgdXMgdG8gZmluZCB3aGF0IGlzIHRoZSDigJxhdmVyYWdl4oCdIGxldmVsIG9mIHNpZ25pZmljYW5jZSBpbiB0aGlzIGRhdGFzZXQgYnkgcGVybXV0aW5nIHBoZW5vdHlwZXMgYW5kIGdlbm90eXBlcy4gVG8gb2J0YWluIHRoZSB0aHJlc2hvbGQgdGhlIFFUTCBtYXBwaW5nIHByb2Nlc3MgaXMgcmVwZWF0ZWQgbXVsdGlwbGUgdGltZXMsIHdoaWNoIG1heSByZXF1aXJlIGEgbG9uZyBjb21wdXRhdGlvbiB0aW1lLiBGb3Igb3VyIGV4YW1wbGUgd2Ugd2lsbCB1c2Ugb25seSAxMCBwZXJtdXRhdGlvbnMsIGJ1dCBnZW5lcmFsbHksIHRoZSBtb3JlIHBlcm11dGF0aW9ucyB0aGUgaGlnaGVyIHRoZSBhY2N1cmFjeSAoZS5nLiAxMDAwIC0gMTAwMDApLg0KDQpUaGVyZSBhcmUgdGhyZWUgcGFyYW1ldGVycyBmb3IgdGhyZXNob2xkIGNhbGN1bGF0aW9uOg0KDQoqIGBucGVybWA6IGludGVnZXIsIHRoZSBudW1iZXIgb2YgcGVybXV0YXRpb25zLg0KKiBgcGVybXV0YXRpb25gOiBlaXRoZXIg4oCccG9w4oCdIG9yIOKAnGZhbeKAnSwgZGV0ZXJtaW5lcyB0aGUgcGVybXV0YXRpb24gc3RyYXRlZ3kuIElmIOKAnHBvcOKAnSwgaXQgcGVybXV0ZXMgb3ZlciB0aGUgd2hvbGUgcG9wdWxhdGlvbiwgYW5kIGlmIOKAnGZhbeKAnSwgb25seSB3aXRoaW4gZmFtaWxpZXMuIElmIHRoZXJlIGlzIGEgc3Ryb25nIGZhbWlseSBzdHJ1Y3R1cmUsIHRoZSDigJxmYW3igJ0gc3RyYXRlZ3kgd2lsbCBwcm9kdWNlIG1vcmUgYWNjdXJhdGUgdGhyZXNob2xkcy4gSG93ZXZlciwgaW4gYSBwb3B1bGF0aW9uIGNvbnNpc3Rpbmcgb2YgbWFueSBzbWFsbCBmYW1pbGllcyAoPCAzMCBzaWJzKSB0aGUg4oCccG9w4oCdIGFwcHJvYWNoIGlzIHN0aWxsIHByZWZlcmFibGUuDQoqIGBmYW1gOiBhIHZlY3RvciBvZiBmYW1pbHkgbWVtYmVyc2hpcCAob3IgcG9wdWxhdGlvbiBzdHJ1Y3R1cmUgbWVtYmVyc2hpcCksIHRvIGJlIHByb3ZpZGVkIGlmIGBwZXJtdXRhdGlvbiA9IOKAnGZhbeKAnWAuDQoqIGBhbHBoYWA6IG51bWJlciBiZXR3ZWVuIDAgYW5kIDEsIGluZGljYXRpbmcgZm9yIHdoaWNoIHByb2JhYmlsaXR5IHNob3VsZCB0aGUgdGhyZXNob2xkIGJlIGNhbGN1bGF0ZWQuIERlZmF1bHQgaXMgMC4wNSAoaS5lLiBhIG1heGltdW0gb2YgNSUgb2YgZmFsc2UgcG9zaXRpdmVzKS4NCg0KRm9yIGVhY2ggcGhlbm90eXBlIGEgbmV3IG91dHB1dCBjYW4gYmUgZm91bmQgbmFtZWQgYHBlcm0udGhyZXNoYA0KDQpgYGB7cn0NCnJlc3VsdF9wZXJtIDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IG1wc25wZG9zZSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIFEgPSBULA0KICAgICAgICBwZXJtdXRhdGlvbiA9ICJwb3AiLA0KICAgICAgICBucGVybSA9IDEwLA0KICAgICAgICBhbHBoYSA9IDAuMDUpDQoNCnB2IDwtIC1sb2cxMChyZXN1bHRfcGVybSRwaGVub3R5cGUxJHB2YWwpDQp0aHIgPC0gcmVzdWx0X3Blcm0kcGhlbm90eXBlMSRwZXJtLnRocg0KDQpza3lwbG90KHB2LG1wbWFwLHRocmVzaG9sZCA9IHRocixtYWluID0gIlFUTCBkZXRlY3Rpb24gd2l0aCBwZXJtdXRhdGlvbiB0ZXN0IikNCmBgYA0KDQojIyMgTGkgJiBKaSB0aHJlc2hvbGQNCg0KQWx0ZXJuYXRpdmVseSwgd2UgY2FuIGNvbXB1dGUgdGhlIG51bWJlciBvZiBleHBlY3RlZCBpbmRlcGVuZGVudCB0ZXN0cyBhcyBwcm9wb3NlZCBieSBbTGkgJiBKaSAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zai5oZHkuNjgwMDcxNyAiQWRqdXN0aW5nIG11bHRpcGxlIHRlc3RpbmcgaW4gbXVsdGlsb2N1cyBhbmFseXNlcyB1c2luZyB0aGUgZWlnZW52YWx1ZXMgb2YgYSBjb3JyZWxhdGlvbiBtYXRyaXgiKS4NCg0KVGhlIGVmZmVjdGl2ZSBudW1iZXIgb2YgaW5kZXBlbmRlbnQgdGVzdCBpcyBleHBlY3RlZCB0byBiZSBsb3dlciB0aGFuIHRoZSB0b3RhbCBudW1iZXIgb2YgdGVzdHMgdXNlZCBpbiB0aGUgQm9uZmVycm9uaSBhZGp1c3RtZW50LiBGb3IgdGhpcyByZWFzb24sIHRoZSBMaSAmIEppIHRocmVzaG9sZCBpcyB1c3VhbGx5IGxlc3Mgc3RyaWN0IHRoYW4gdGhlIEJvbmZlcnJvbmkgdGhyZXNob2xkLiBOb3RpY2UgdGhhdCB0aGlzIHRocmVzaG9sZCBkZXBlbmRzIG9uIGdlbm90eXBlcyBvbmx5LCB0aGVyZWZvcmUgdGhlIHNhbWUgdGhyZXNob2xkIGNhbiBiZSB1c2VkIGZvciBRVEwgYW5hbHlzZXMgb2YgZGlmZmVyZW50IHRyYWl0cy4NCg0KYGBge3J9DQoNCnJlc3VsdF9saWppIDwtIHRoci5MaUppKG0gPSBtcHNucGRvc2UsDQogICAgICAgICAgICAgICAgICAgICAgICBjaHJvbSA9IG1wbWFwJGNocm9tb3NvbWUsDQogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUsDQogICAgICAgICAgICAgICAgICAgICAgICBwbG9pZHkgPSA0KQ0KDQpyZXN1bHRfbGlqaQ0KLWxvZzEwKHJlc3VsdF9saWppJHRocmVzaG9sZCkNCmBgYA0KDQojIyBJbXB1dGF0aW9uIG9mIG1pc3NpbmcgdmFsdWVzDQoNCkluIGdlbmVyYWwsIGZldyBtaXNzaW5nIHZhbHVlcyBzaG91bGQgYmUgaW5jbHVkZWQgYm90aCBpbiBwaGVub3R5cGVzIGFuZCBnZW5vdHlwZXMuIFRoZSBmdW5jdGlvbnMsIGhvd2V2ZXIsIGFyZSB3ZWxsIGFkYXB0ZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzLiBQYXJ0IG9mIHRoaXMgYWRhcHRpb24gaXMgdGhlIHBvc3NpYmlsaXR5IG9mIGltcHV0aW5nIGdlbm90eXBlcyB1c2luZyBhIGstbmVhcmVzdCBuZWlnaGJvdXJzIChrbm4pIGFwcHJvYWNoLiBUaGF0IGlzLCBmb3IgZWFjaCBtaXNzaW5nIHZhbHVlLCB0aGUgJGskIG1vc3QgY29ycmVsYXRlZCBpbmRpdmlkdWFscyBhdCB0aGF0IGdlbmV0aWMgcmVnaW9uIGFyZSB0YWtlbiwgYW5kIGEgY29uc2Vuc3VzIGdlbm90eXBlIGlzIG9idGFpbmVkIHRvIGZpbGwgaW4gdGhlIG1pc3NpbmcgZGF0YS4gSWYgbm90aGluZyBpcyBzcGVjaWZpZWQsIGBtYXAuUVRMKClgIHdpbGwgdHJ5IHRvIGltcHV0ZSB0aGUgbWlzc2luZyBnZW5vdHlwZXMsIGFsdGhvdWdoIGluIHNvbWUgY2FzZXMgaXQgbWlnaHQgZGVjaWRlIGl0IGRvZXMgbm90IGhhdmUgZW5vdWdoIGluZm9ybWF0aW9uIHRvIGFjY3VyYXRlbHkgaW1wdXRlIGEgZ2Vub3R5cGUuDQoNClRvIGNvbnRyb2wgdGhlIGJlaGF2aW91ciBvZiB0aGUgaW1wdXRhdG9yIHVzZToNCg0KKiBgaW1wdXRlYDogbG9naWNhbCwgc2hvdWxkIG1pc3NpbmcgZ2Vub3R5cGVzIGJlIGltcHV0ZWQ/DQoqIGBrYDogaW50ZWdlciwgaG93IG1hbnkgbmVpZ2hib3VycyBzaG91bGQgYmUgdXNlZCBmb3IgaW1wdXRhdGlvbj8gRGVmYXVsdHMgdG8gMjAuDQoNCmBgYHtyfQ0Kc25wX05BIDwtIG1wc25wZG9zZQ0Kc25wX05BW3NhbXBsZSgxOmxlbmd0aChtcHNucGRvc2UpLDIwMDApXSA8LSBOQQ0KcmVzdWx0X05BIDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IHNucF9OQSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIFEgPSBUKQ0KDQpwdiA8LSAtbG9nMTAocmVzdWx0X05BJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsbXBtYXAsbWFpbiA9ICJRVEwgZGV0ZWN0aW9uIHdpdGggbWlzc2luZyBnZW5vdHlwZXMiLCANCiAgICAgICAgdGhyZXNob2xkID0gcmVzdWx0X3Blcm0kcGhlbm90eXBlMSRwZXJtLnRocikNCg0KYGBgDQoNCiMjIEFkdmFuY2VkIHBhcmFtZXRlcnMNClNvbWUgZXh0cmEgcGFyYW1ldGVycyBhbGxvdyB0byBtb2RpZnkgdGhlIGJlaGF2aW91ciBvZiBjZXJ0YWluIHBhcnRzIG9mIGBtYXAuUVRMKClgLiBUaGVzZSBwYXJhbWV0ZXJzIGluY2x1ZGU6DQoNCiogYG5vX2NvcmVzYDogaW50ZWdlciBzcGVjaWZ5aW5nIHRoZSBudW1iZXIgb2YgY29yZXMgdG8gdXNlIGZvciBwYXJhbGxlbCBjb21wdXRpbmcuIERlZmF1bHRzIHRvIG51bWJlciBvZiBjb3JlcyBpbiB0aGUgbWFjaGluZSAtIDEuIElmIHJ1bm5pbmcgb24gYSBzZXJ2ZXIgb3IgY29tcHV0ZXIgY2x1c3RlciwgdGhpcyBwYXJhbWV0ZXIgc2hvdWxkIGJlIHNwZWNpZmllZCB0byBhdm9pZCBhc2tpbmcgZm9yIHRvbyBtYW55IGNvcmVzLg0KKiBgbGluZWFyYDogbG9naWNhbCwgc3BlY2lmeWluZyB3aGV0aGVyIGEgbGluZWFyIG1vZGVsIHNob3VsZCBiZSB1c2VkLiBJZiBGIGEgbWl4ZWQgbW9kZWwgaXMgdXNlZC4gVGhpcyBhcmd1bWVudCB3aWxsIG92ZXJydWxlIHRoZSBhdXRvbWF0aWMgYmVoYXZpb3VyIG9mIG1hcC5RVEwoKSwgc28gaXQgaXMgdXNlZnVsIHRvIGZvcmNlIGxpbmVhciBtb2RlbCBiZWhhdmlvdXIgd2hlbiBhIGN1c3RvbSBLDQogbWF0cml4IHdhbnRzIHRvIGJlIHByb3ZpZGVkIChmb3IgaW1wdXRhdGlvbiwgb3IgUXBjbyBkZWZpbml0aW9uKS4NCiogYGFwcHJveGltYXRlYDogYnkgZGVmYXVsdCwgdGhlIG1peGVkIG1vZGVsIHNvbHV0aW9uIGlzIGFjaGlldmVkIHVzaW5nIGFuIGFsZ29yaXRoIHByZXNlbnRlZCBwYXJhbGVsbHkgYXMgUDNEIG9yIEVNTUFYLCB3aGljaCBlc3NlbnRpYWxseSBhcHByb3hpbWF0ZXMgcGFydCBvZiB0aGUgY2FsY3VsYXRpb24gYmV0d2VlbiBtYXJrZXJzIHdoaWNoIGlzIHZlcnkgc2ltaWxhciwgYnV0IG5vdCBpZGVudGljYWwuIEJ5IGRvaW5nIHRoYXQsIHRoZSBzcGVlZCBvZiBjb21wdXRhdGlvbiBpcyBncmVhdGx5IGluY3JlYXNlZCwgYXQgYSBwcmFjdGljYWxseSBudWxsIGNvc3Qgb2YgYWNjdXJhY3kuIElmIHRoaXMgYXBwcm94aW1hdGlvbiBuZWVkcyB0byBiZSBkZWFjdGl2YXRlZCwgb25lIGNhbiBzZXQgYXBwcm94aW1hdGUgPSBGLg0KKiBgS19pZGVudGl0eWA6IGxvZ2ljYWwuIElmIFRydWUsIGl0IGZvcmNlcyB0byB1c2UgYSBtaXhlZCBtb2RlbCB0aGF0IGRvZXMgbm90IGNvcnJlY3QgZm9yIGdlbmV0aWMgc3RydWN0dXJlLCBidXQgd2hlcmUgdGhlIEsgbWF0cml4IGlzIHVzZWQgZm9yIG90aGVyIHB1cnBvc2VzIChOQSBpbXB1dGF0aW9uLCBRcGNv4oCmKS4NCg0KIyBWaXN1YWxpemF0aW9uDQoNCkxldCB1cyBub3cgZGlzY3VzcyB0aGUgdmlzdWFsaXphdGlvbiBmdW5jdGlvbnMgaW5jbHVkZWQgaW4gYG1wUVRMYCBhcyB3ZWxsIGFzIHRoZWlyIGludGVycHJldGF0aW9ucy4gVGhlIHZpc3VhbGl6YXRpb25zIHByb3ZpZGVkIGluY2x1ZGU6DQoNCiogUHJpbmNpcGFsIENvb3JkaW5hdGUgQW5hbHlzaXMgKFBDb0EpIHBsb3RzDQoqIFF1YW50aWxlLXF1YW50aWxlIHBsb3RzIChRUS1wbG90cykNCiogTWFuaGF0dGFuIHBsb3RzICh3ZSBjYWxsIHRoZW0gc2t5bGluZSBwbG90cykNCiogUGhlbm90eXBlIGJveHBsb3RzIGJhc2VkIG9uIFNOUCBvciBoYXBsb3R5cGUgZG9zYWdlDQoNCldlIGNhbiBkaXN0aW5ndWlzaCBiZXR3ZW4gdGhvc2UgdmlzdWFsaXphdGlvbnMgdXNlZnVsIGJlZm9yZSB0aGUgUVRMIGFuYWx5c2lzICgxKSwgdGhvc2UgdXNlZnVsIHRvIHVuZGVyc3RhbmQgdGhlIFFUTCBhbmFseXNpcyAoMiBhbmQgMyksIGFuZCBmaW5hbGx5IHRob3NlIHRoYXQgaGVscCB1cyBkaXNzZWN0IHRoZSByZXN1bHRzIG9mIHRoZSBRVEwgYW5hbHlzaXMgKDQpLg0KDQojIyBQcmluY2lwYWwgQ29vcmRpbmF0ZSBBbmFseXNpcw0KDQpXZSBoYXZlIGluY2x1ZGVkIGEgZnVuY3Rpb24sIHBjb2EucGxvdCgpIHRoYXQgY2FsY3VsYXRlcyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5kIHZhcmlhbmNlIHBlcmNlbnRhZ2VzICh1c2luZyBS4oCZcyBvd24gcHJjb21wKCkgZnVuY3Rpb24pIGFuZCB1c2VzIHRoZW0gdG8gcGxvdCB3aGF0ZXZlciBjb21wb25lbnRzIGFyZSBkZXNpcmVkICgxIGFuZCAyIGJ5IGRlZmF1bHQpLiBJdCBhbHNvIGluY2x1ZGVzIGEgbGl0dGxlIGZ1bmN0aW9uIHRvIGFkZCBjb2xvdXIgdG8gdGhlc2UgcGxvdHMgdG8gaGVscCBkaXNzZWN0IHRoZSBzdHJ1Y3R1cmUuIFRoZSBhcmd1bWVudHMgb2YgcGNvYS5wbG90KCkgaW5jbHVkZToNCg0KKiBgS2A6IGFueSBkaXN0YW5jZS9zaW1pbGFyaXR5IG1hdHJpeC4NCiogYGNvbXBgOiBudW1lcmljIHZlY3RvciBvZiBsZW5ndGggMi4gVGhlIHByaW5jaXBsZSBjb21wb25lbnRzIHRvIGJlIHVzZWQgZm9yIHBsb3R0aW5nLiBBIFBDb0EgZ2VuZXJhdGVzIGFzIG1hbnkgY29tcG9uZW50cyBhcyBkaW1lbnNpb25zIGhhZCB0aGUgb3JpZ2luYWwgZGlzdGFuY2UgbWF0cml4LiBUaGV5IGFyZSBvcmRlcmVkIGJ5IHRoZWlyIGV4cGxhbmF0b3J5IHZhbHVlIChjb21wb25lbnRzIDEgYW5kIDIgd2lsbCBhbHdheXMgYmUgdGhlIG1vc3QgZXhwbGFuYXRvcnkpLiBUaGUgYW1vdW50IG9mIGV4cGxhaW5lZCB2YXJpYW5jZSBvZiBlYWNoIGlzIGFsd2F5cyBwcmVzZW50IGluIHRoZSB4bGFiIGFuZCB5bGFiIG9mIHRoZSBwbG90Lg0KKiBgcGxvdF9sZWdlbmRgOiBsb2dpY2FsLCB3aGV0aGVyIHRoZSBsZWdlbmQgc2hvdWxkIGJlIHBsb3R0ZWQuDQoqIGBsZWdzcGFjZWA6IG51bWVyaWMsIGluZGljYXRpbmcgdGhlIGFtb3VudCBvZiBzcGFjZSB0byBsZWF2ZSBmb3IgdGhlIGxlZ2VuZC4gSW4gY2FzZSBhIGdyb3VwaW5nIGlzIHByb3ZpZGVkLCBhIGxlZ2VuZCB3aWxsIGJlIHBsb3R0ZWQgdG8gdGhlIHJpZ2h0IG9mIHRoZSBwbG90LiBJbiBvcmRlciBub3QgdG8gb3ZlcmxhcCB3aXRoIHRoZSBwbG90LCBhZGRpdGlvbmFsIHNwYWNlIGlzIGFkZGVkIHRvIHRoZSByaWdodCBvZiB0aGUgcGxvdC4gVGhlIHBhcmFtZXRlciBsZWdzcGFjZSBjb250cm9scyB0aGUgYW1vdW50IG9mIHNwYWNlIGxlZnQgYXMgYSBwcm9wb3J0aW9uIHRvIHRoZSByYW5nZSBvZiB4IHZhbHVlcy4gQnkgZGVmYXVsdCwgaXQgdGFrZXMgdGhlIHZhbHVlIG9mIDAuMSAoMTAlIG9mIGV4dHJhIHNwYWNlKS4NCiogYGxlZ25hbWVgOiB0aGUgbGVnZW5kIG5hbWVzIGZvciB0aGUgY29sb3VycyBjaG9zZW4sIGF1dG9tYXRpY2FsbHkgdGFrZW4gZnJvbSBjb2wgaWYgbm90IHNwZWNpZmllZC4NCg0KRm9yIFBDb0EgcGxvdHMsIHRoZSBtb3N0IHVzZWZ1bCBpcyB0byBiZSBhYmxlIHRvIG1hcCBjYXRlZ29yaWVzIG9udG8gdGhlIHBsb3QsIHRvIGFzc2VzcyB3aGV0aGVyIHRoZSBnZW5ldGljIHN0cnVjdHVyZSBjb3JyZWxhdGVzIHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aG9zZSBjYXRlZ29yaWVzLiBUbyBhY2hpZXZlIHRoYXQgZnVuY3Rpb25hbGl0eSwgdHdvIGNvbW1vbiBwbG90dGluZyBwYXJhbWV0ZXJzIGhhdmUgYmVlbiBtb2RpZmllZDoNCg0KKiBgY29sYDogaW5zdGVhZCBvZiBleHBlY3RpbmcgYSBjb2xvdXIgZGVmaW5pdGlvbiwgY29sIGV4cGVjdHMgYSB2ZWN0b3Igb2YgdmFsdWVzIChlaXRoZXIgY2F0ZWdvcmljYWwgb3IgbnVtZXJpY2FsKS4gSXQgd2lsbCB0aGVuIGFzc2lnbiBhIGNvbG91ciB0byBlYWNoIHVuaXF1ZSB2YWx1ZSBhbmQgYXBwbHkgaXQgdG8gdGhlIFBDb0EgcGxvdCAoZS5nLiBjKOKAnHBvcDHigJ0s4oCccG9wMeKAnSzigJxwb3Ay4oCdLOKAnHBvcDLigJ0s4oCccG9wM+KAnSkgd2lsbCBnZW5lcmF0ZSB0aHJlZSBjb2xvdXIgY2F0ZWdvcmllcykuIFdpdGggY29udGludW91cyB2YWx1ZXMsIHRoaXMgc3lzdGVtIHdpbGwgbm90IHByb2R1Y2UgaWRlYWwgcmVzdWx0cyBpZiBub3QgYWxsIHZhbHVlcyBpbiBhIHNlcmllcyBhcmUgcHJlc2VudCAoaS5lLiB3b3JrcyB3ZWxsIGZvciBjb250aWd1b3VzIHZhbHVlcyBsaWtlIDEsMiwzLDQsNTsgZG9lcyBub3Qgd29yayB3ZWxsIGZvciBub24tY29udGlndW91cyB2YWx1ZXMgbGlrZSAxLDUsMTAsMTEsNDYpLg0KKiBgcGNoYDogdGhpcyBwYXJhbWV0ZXIgaXMgdXNlZCBtb3N0bHkgYXMgdGhlIGNsYXNzaWNhbCBwY2ggcGFyYW1ldGVyLCBidXQgaWYgbXVsdGlwbGUgcGNoIHZhbHVlcyBhcmUgcHJvdmlkZWQsIHRoZSBmaXJzdCB3aWxsIGJlIGFwcGxpZWQgdG8gdGhlIGZpcnN0IGNhdGVnb3J5LCB0aGUgc2Vjb25kIHRvIHRoZSBzZWNvbmQgY2F0ZWdvcnksIGV0Yy4gVGhpcyBtaWdodCBiZSBoZWxwZnVsIHdoZW4gdGhlcmUgYXJlIG1hbnkgY2F0ZWdvcmllcyBhbmQgb25seSBjb2xvdXIgZGlmZmVyZW5jZXMgbWlnaHQgbm90IGJlIGVub3VnaC4NCg0KTGV0IHVzIHNlZSBob3cgdGhpcyBjb2xvdXIgYW5kIHBvaW50IHR5cGUgbWFwcGluZyB3b3JrLg0KDQpgYGB7cn0NCiNjYWxjdWxhdGUgdGhlIHNpbWlsYXJpdHkgbWF0cml4IEsNCksgPC0gY2FsYy5LKHQobXBzbnBkb3NlKSkNCg0KcG9wIDwtIHN1YnN0cihjb2xuYW1lcyhLKSwxLDIpDQpsYXlvdXQobWF0cml4KDE6NCxieXJvdz1ULG5jb2w9MikpDQpwYXIobWFyPWMoNCw0LDMsMSkpDQpwY29hLnBsb3QoSyxtYWluID0gIkRlZmF1bHQgUENvQSBwbG90IiwgaCA9IGMoMCwzNjApKQ0KDQpwY29hLnBsb3QoSyxjb2wgPSBwb3AsDQogICAgICAgICAgcGNoPTE5LCBtYWluID0gIkNvbG91ciBtYXBwaW5nIiwgaCA9IGMoMCwzNjApKQ0KDQpwY29hLnBsb3QoSyxjb2wgPSBwb3AscGNoID0gYygxNSwxNywxOSksDQogICAgICAgICAgbWFpbiA9ICJEaWZmZXJlbnQgcG9pbnQgdHlwZXMiLCBoID0gYygwLDM2MCkpDQoNCiNJbiB0aGlzIGNhc2UgdGhlIGxlZ2VuZCBkb2VzIG5vdCB3b3JrIHByb3Blcmx5LCBpdCdzIGJldHRlciB0byB0dXJuIGl0IG9mZg0KcGNvYS5wbG90KEssY29sID0gMTpucm93KEspLCBwbG90X2xlZ2VuZCA9IEYsDQogICAgICAgICAgcGNoPTE5LCBtYWluID0iIE9uZSBjb2xvdXIgcGVyIGluZGl2aWR1YWwiLCBoID0gYygwLDM2MCkpDQpgYGANCg0KIyMgUXVhbnRpbGUtUXVhbnRpbGUgcGxvdA0KDQpRUS1wbG90cyBhcmUgdXNlZnVsIGZvciBkZXRlcm1pbmluZyB3aGV0aGVyIHRoZSBwLXZhbHVlIGRpc3RyaWJ1dGlvbiBvZiBhIFFUTCBhbmFseXNpcyAoeSBheGlzKSBmb2xsb3dzIHRoZSBleHBlY3RlZCBkaXN0cmlidXRpb24gKHggYXhpcykuIEluIHRoaXMgY2FzZSwgdGhlIGV4cGVjdGVkIGRpc3RyaWJ1dGlvbiBjb3JyZXNwb25kcyB0byBhIG51bGwgaHlwb3RoZXNpcyB3aGVyZSB0aGVyZSBhcmUgbm8gdHJ1bHkgc2lnbmlmaWNhbnQgbWFya2VycyAodGhlcmUgYXJlIG9ubHkgYSBmZXcgZmFsc2UgcG9zaXRpdmVzKS4gVGhpcyBleHBlY3RhdGlvbiBjYW4gYmUgc2VlbiBpbiB0aGUgcGxvdCB3aXRoIGEgcmVkIGRpYWdvbmFsIGxpbmUuDQoNClRoZSBmdW5jdGlvbiBgUVEucGxvdCgpYCBjYW4gYmUgdXNlZCB0byBnZW5lcmF0ZSBRUS1wbG90cyBvZiBvbmUgb3IgbXVsdGlwbGUgdmVjdG9ycyBvZiBwLXZhbHVlcy4gRWFjaCB2ZWN0b3Igb2YgcC12YWx1ZXMgY2FuIGJlIGdpdmVuIGFzIGEgY29sdW1uIGluIGEgbWF0cml4IG9yIGFzIGFuIGVsZW1lbnQgb2YgYSBsaXN0LiBUaGUgbnVtYmVyIG9mIHAtdmFsdWVzIGluIGVhY2ggdmVjdG9yIGRvZXMgbm90IG1hdHRlci4gQSBzaW5nbGUgc2V0IG9mIHAtdmFsdWVzIGNhbiBhbHNvIGJlIHByb3ZpZGVkLg0KDQpgYGB7cn0NCiNUaGUgcC12YWx1ZXMgYXJlIGZyb20gNTAwIHJhbmRvbSBub3JtYWwgdmFsdWVzDQpub3Rfc2lnIDwtIHBub3JtKHJub3JtKDUwMCksbG93ZXIudGFpbCA9IEYpDQoNCiNUaGUgcC12YWx1ZXMgYXJlIGZyb20gc29tZSByYW5kb20gbm9ybWFsIHZhbHVlcyBhbmQgc29tZSBub24tcmFuZG9tDQpzb21lX3NpZyA8LSBwbm9ybShjKHJub3JtKDQ1MCkscm5vcm0oNTAsbWVhbiA9IDMpKSxsb3dlci50YWlsID0gRikNCg0KI1RoZSBwLXZhbHVlcyBhcmUgYWxsIHRvbyBzaWduaWZpY2FudA0KaGlnaF9zaWcgPC0gcG5vcm0ocm5vcm0oNTAwLG1lYW49MyksbG93ZXIudGFpbCA9IEYpDQoNCiNUaGUgcC12YWx1ZXMgYXJlIGFsbCB0b28gbm9uLXNpZ25pZmljYW50DQpsb3dfc2lnIDwtIHBub3JtKHJub3JtKDUwMCksc2QgPSAzLGxvd2VyLnRhaWwgPSBGKQ0KDQoNCmV4YW1wbGVzIDwtIGxpc3Qobm90X3NpZyxzb21lX3NpZyxoaWdoX3NpZyxsb3dfc2lnKQ0KUVEucGxvdChleGFtcGxlcyxtYWluPSJFeGFtcGxlIFFRLXBsb3QiLA0KICAgICAgICBsZWduYW1lcyA9IGMoIk5vdCBzaWduaWZpY2FudCIsDQogICAgICAgICAgICAgICAgICAgICAiR29vZCBzaWduaWZpY2FudCIsDQogICAgICAgICAgICAgICAgICAgICAiT3ZlcmVzdGltYXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAiVW5kZXJlc3RpbWF0ZWQiKSkNCmBgYA0KDQpBIGxlZ2VuZCBpcyBhdXRvbWF0aWNhbGx5IGFkZGVkIHdoZW4gbXVsdGlwbGUgcHZhbHVlIHNldHMgYXJlIHByb3ZpZGVkLiBUaGVyZSBhcmUgc29tZSBwYXJhbWV0ZXJzIHJlbGF0ZWQgd2l0aCB0aGUgbGVnZW5kOiANCg0KKiBgcGxvdF9sZWdlbmRgOiBsb2dpY2FsLCBjYW4gYmUgdXNlZCB0byBhdm9pZCB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgbGVnZW5kLiANCiogYGxlZ25hbWVzYDogYSB2ZWN0b3IgY2FuIGJlIHByb3ZpZGVkIGZvciB0aGUgbGVnZW5kIG5hbWVzLiANCiogYGxlZ3NwYWNlYDogbnVtZXJpYywgYSBudW1iZXIgaW5kaWNhdGluZyBob3cgbXVjaCBleHRyYSBzcGFjZSAoaW4gcHJvcG9ydGlvbikgbXVzdCBiZSBhZGRlZCB0byB0aGUgbGVmdCBvZiB0aGUgcGxvdC4gQ2FuIGJlIHVzZWZ1bCB0byB0d2VhayBpdCB3aGVuIGxlZ2VuZCBuYW1lcyBhcmUgdG9vIGxvbmcgYW5kIHRoZXkgb3ZlcmxhcCB3aXRoIHBvaW50cyBvbiB0aGUgUVEtcGxvdC4NCg0KYGBge3J9DQpwdmFscyA8LSBsaXN0KGxpbl9kb3NhZ2UgPSByZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCwNCiAgICAgICAgICAgICAgbGluX2hhcCA9IHJlc3VsdF9oYXAkcGhlbm90eXBlMSRwdmFsLA0KICAgICAgICAgICAgICBsaW5fcXBjbyA9IHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCwNCiAgICAgICAgICAgICAgbWl4ID0gcmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwsDQogICAgICAgICAgICAgIGxpbl9RX05BID0gcmVzdWx0X05BJHBoZW5vdHlwZTEkcHZhbCkNCg0KUVEucGxvdChwdmFscywgbWFpbiA9ICJNb2RlbCBjb21wYXJpc29uIGZvciBwLXZhbHVlcyBvZiBwaGVubzEiLGxlZ3NwYWNlID0gMC4yMikNCmBgYA0KYGBge3J9DQojVGhlIG5vbi1jb3JyZWN0ZWQgbGluZWFyIG1vZGVscyBoYXZlIGdyb3NzbHkgb3ZlcmVzdGltYXRlZCBwLXZhbHVlcw0KI0JldHRlciB0YWtlIHRoZW0gb3V0IHRvIGNvbXBhcmUgdGhlIG90aGVyIG1vZGVscyBtb3JlIGNsZWFybHkNClFRLnBsb3QocHZhbHNbLTE6LTJdLCBtYWluID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHB2YWx1ZXMgb2YgcGhlbm8xIikNCmBgYA0KV2UgY2xlYXJseSBzZWUgaG93IHRoZSBsYWNrIG9mIHN0cnVjdHVyZSBjb3JyZWN0aW9uIGluIHRoZSBsaW5lYXIgbW9kZWxzIGhhcyBjYXVzZWQgYSBncmVhdCBpbmZsYXRpb24gb2YgdGhlIHAtdmFsdWVzLg0KDQojIyBTa3lsaW5lIHBsb3QNCg0KUHJvYmFibHkgdGhlIG1vc3QgcmVsZXZhbnQgcGxvdCBmb3IgUVRMIG1hcHBpbmcgaXMgdGhlIOKAnE1hbmhhdHRhbiBwbG904oCdLCBuYW1lZCBhcyBzdWNoIGR1ZSB0byBpdHMgc3RydWN0dXJlLCB3aGljaCBjYW4gcmVtaW5kIG9mIHRoZSBza3lsaW5lIG9mIG9mIHRoZSBmYW1vdXMgc2t5c2NyYXBlciBkaXN0cmljdCBvZiBOZXcgWW9yay4gSW4gdGhpcyBwYWNrYWdlLCB3ZSBoYXZlIG9wdGVkIHRvIG5hbWUgdGhlIGZ1bmN0aW9uIGBza3lwbG90KClgLCBmb2xsb3dpbmcgdGhlIHZpc3VhbCBtZXRhcGhvci4NCg0KQSBNYW5oYXR0YW4gcGxvdCBpcyBhIGZvcm0gb2YgbWFya2VyIHNpZ25pZmljYW5jZSB2aXN1YWxpemF0aW9uIHRoYXQgaGVscHMgdXMgcmVjb2duaXNlIHJlZ2lvbnMgd2hlcmUgbXVsdGlwbGUgc2lnbmlmaWNhbnQgbWFya2VycyBjby1sb2NhdGUgYXJvdW5kIGEgcmVnaW9uLCBkZWZpbmluZyBhIFFUTCBsb2NhdGlvbi4gT24gdGhlIHgtYXhpcyB0aGUgbWFya2Vy4oCZcyBwb3NpdGlvbiBpbiB0aGUgZ2Vub21lIChwaHlzaWNhbCBvciBnZW5ldGljKSBhbmQgb24gdGhlIHktYXhpcywgdGhlIG1hcmtlcuKAmXMgc2lnbmlmaWNhbmNlLiBHZW5lcmFsbHksIHNpZ25pZmljYW5jZSBpcyBleHByZXNzZWQgYXMgJOKIkmxvZzEwKHB2YWwpJCwgYnV0IGFueSBvdGhlciBzY29yZSB3aWxsIGRvLCBhcyBsb25nIGFzIGl0IGZvbGxvd3MgdGhhdCBtb3JlIHNpZ25pZmljYW50IG1hcmtlcnMgaGF2ZSBoaWdoZXIgc2NvcmVzLCBhbmQgbGVzcyBzaWduaWZpY2FudCBtYXJrZXJzIGhhdmUgbG93ZXIgc2NvcmVzIChmb3IgaW5zdGFuY2UgYSBXYWxkIHNjb3JlIG9yIEYtdGVzdCkuDQoNClRvIGdlbmVyYXRlIHRoZSB2aXN1YWxpemF0aW9uIG9uZSBtdXN0IHByb3ZpZGUgYm90aCBwLXZhbHVlcyBhbmQgYSBnZW5ldGljL3BoeXNpY2FsIG1hcC4gVGhlIGBza3lwbG90KClgIGZ1bmN0aW9uIGhhcyB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czoNCg0KKiBgcHZhbGA6IHZlY3RvciBvZiBwdmFsdWVzLiBJbXBvcnRhbnRseSwgdGhlIGZ1bmN0aW9uIC1sb2cxMCgpIG11c3QgYmUgcGVyZm9ybWVkIGJ5IHRoZSB1c2VyLiBUaGlzIGhhcyBiZWVuIGRvbmUgb24gcHVycG9zZSwgYXMgd2UgbWlnaHQgd2FudCB0byBwbG90IG90aGVyIGtpbmQgb2Ygc2NvcmUgdmFsdWVzIHdpdGggdGhpcyBmdW5jdGlvbiB0aGF0IGRvIG5vdCByZXF1aXJlIHRoZSAtbG9nMTAgdHJhbnNmb3JtYXRpb24uDQoqIGBtYXBgOiBhIGdlbmV0aWMgbWFwIGRhdGEuZnJhbWUgd2l0aCBhdCBsZWFzdCBjb2x1bW5zIOKAnHBvc2l0aW9u4oCdIGFuZCDigJxjaHJvbW9zb21l4oCdLg0KKiBgdGhyZXNob2xkYDogbnVtZXJpYywgb3B0aW9uYWwgdmFsdWUgdG8gZHJhdyBhIHRocmVzaG9sZCBsaW5lLg0KKiBgY2hyb21gOiBudW1lcmljIG9yIGNoYXJhY3RlciB2ZWN0b3IuIElmIHByb3ZpZGVkLCBpdCBpcyB1c2VkIHRvIHNlbGVjdCB0aGUgcC12YWx1ZXMgYmFzZWQgb24gdGhlIOKAnGNocm9tb3NvbWXigJ0gY29sdW1uIG9mIG1hcC4NCiogYHNtYWxsYDogbG9naWNhbCwgc2hvdWxkIHRoZSBjTSBzY2FsZSBiZSBkcmF3bj8gQnkgZGVmYXVsdCBpdCB3aWxsIG9ubHkgYmUgZHJhd24gaWYgb25seSB0d28gb3IgYSBzaW5nbGUgY2hyb21vc29tZSBhcmUgcGxvdHRlZCwgb3RoZXJ3aXNlIGl0IGlzIHRvbyBjcm93ZGVkIGFuZCBiYXJlbHkgbGVnaWJsZS4NCi4uLjogb3RoZXIgcGFyYW1ldGVycyBjYW4gYmUgcGFzc2VkIHRvIHRoZSBwbG90KCkgZnVuY3Rpb24uIFRoZSBtb3N0IHJlbGV2YW50IGlzIHByb2JhYmx5IG1haW4gZm9yIHRoZSBwbG90IHRpdGxlLg0KDQpgYGB7cn0NCnB2MSA8LSAtbG9nMTAocmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwpDQojSnVzdCBhIHNreWxpbmUgcGxvdA0Kc2t5cGxvdChwdjEsbWFwID0gbXBtYXAsbWFpbj0iRXhhbXBsZSBTa3lsaW5lIHBsb3QiKQ0KDQojV2Ugd2FudCB0byBmb2N1cyBvbiBjaHJvbW9zb21lIDINCnNreXBsb3QocHYxLG1wbWFwLGNocm9tID0gMixtYWluID0iU2t5bGluZSBsb3Qgb2YgY2hyb21vc29tZSAyIix0aHJlc2hvbGQgPSAzLjk1KQ0KDQojVGhlICJjaHJvbW9zb21lcyIgY2FuIGFsc28gYmUgZXhwcmVzc2VkIGFzIGNoYXJhY3RlcnMNCm1hcF9sZXR0ZXJzIDwtIG1wbWFwDQptYXBfbGV0dGVycyRjaHJvbW9zb21lIDwtIExFVFRFUlNbbWFwX2xldHRlcnMkY2hyb21vc29tZSsxXQ0KDQpza3lwbG90KHB2MSxtYXBfbGV0dGVycywNCiAgICAgICAgbWFpbj0iU2t5bGluZSBwbG90IHdoZXJlIGNocm9tb3NvbWVzIGFyZSBjaGFyYWN0ZXJzIiwNCiAgICAgICAgY2hyb20gPSBjKCJDIiwiRSIpLHNtYWxsID0gVCx0aHJlc2hvbGQgPSAzLjk1KQ0KYGBgDQoNCiMjIyBDb21wYXJhdGl2ZSBza3lsaW5lIHBsb3QNCg0KRm9yIG91ciByZXNlYXJjaCwgd2Ugd2FudGVkIHRvIGNvbXBhcmUgdGhlIHNreWxpbmUgcGxvdHMgb2YgbXVsdGlwbGUgbW9kZWxzLCBuYW1lbHkgdGhlIGhhcGxvdHlwZS1iYXNlZCBhbmQgZG9zYWdlLWJhc2VkIG1vZGVscy4gRm9yIHRoYXQgcmVhc29uLCBhbm90aGVyIHNreWxpbmUgcGxvdCBmdW5jdGlvbiB3YXMgZGV2ZWxvcGVkOiBgY29tcC5za3Bsb3QoKWAuIFRoZSBmdW5jdGlvbiBpcyB1c2VkIGluIGEgdmVyeSBzaW1pbGFyIGZhc2hpb24sIHdpdGggc29tZSBkaWZmZXJlbmNlczoNCg0KKiBgcHZhbGBgOiBlYWNoIHAtdmFsdWUgdmVjdG9yIG11c3QgYmUgcHJvdmlkZWQgYXMgYSBjb2x1bW4gaW4gYSBtYXRyaXggb3IgYSB2ZWN0b3IgaW4gYSBsaXN0Lg0KKiBtYXA6IGEgbWFwIG11c3QgYmUgcHJvdmlkZWQgZm9yIGVhY2ggc2V0IG9mIHAtdmFsdWVzLiBJZiBhIHNpbmdsZSBtYXAgaXMgcHJvdmlkZWQsIGl0IHdpbGwgYmUgYXNzdW1lZCB0aGF0IGFsbCBwLXZhbHVlIHZlY3RvcnMgY29ycmVzcG9uZCB0byB0aGUgc2FtZSBtYXAuIEZvciB0aGUgbW9tZW50LCB3ZSBoYXZlIG5vdCB0ZXN0ZWQgd2hhdCBoYXBwZW5zIGlmIGVhY2ggbWFwIGhhcyBhIGRpZmZlcmVudCBzZXQgb2YgY2hyb21vc29tZXMgKGkuZS4gbWFwMSBoYXMgY2hyb21vc29tZXMgMSwgMiBhbmQgMyBhbmQgbWFwMiBoYXMgY2hyb21vc29tZXMgMSwgMiBhbmQgNCksIHRoYXQgbWlnaHQgY2F1c2UgY29uZmxpY3RzLg0KKiBgY2hyb21gOiBzaW1pbGFybHksIHdlIGhhdmUgbm90IHRlc3RlZCB3aGF0IHdvdWxkIGhhcHBlbiBpZiBjaHJvbW9zb21lcyBhcmUgc2VsZWN0ZWQgdGhhdCBhcmUgIHByZXNlbnQgb25seSBpbiBvbmUgb2YgdGhlIHR3byBtYXBzLg0KKiBgbGVnbmFtZXNgOiBhIHZlY3RvciBvZiBuYW1lcyBmb3IgdGhlIGxlZ2VuZCBlbGVtZW50cy4gSWYgbm90IHByb3ZpZGVkLCBpdCB3aWxsIGJlIHJlYWQgZnJvbSB0aGUgcHZhbHVlIGxpc3QsIGFuZCBpZiB0aGUgbGlzdCBoYXMgbm8gbmFtZXMsIGl0IHdpbGwgYmUgc2ltcGx5IGxhYmVsbGVkIHB2YWwgMSwgcHZhbCAyLCBldGMuDQoqIGBsZWdzcGFjZWA6IG51bWVyaWMsIHRoZSBwcm9wb3J0aW9uIG9mIHNwYWNlIHRvIGFkZCB0byB0aGUgbGVmdCBvZiB0aGUgcGxvdCBmb3IgdGhlIGxlZ2VuZC4gSXQgaXMgdXNlZnVsIHdoZW4gdGhlIGxlZ2VuZCBpcyB0b28gY2xvc2UgdG8gcG9pbnRzLCBhbmQgZGVmYXVsdHMgdG8gMC4xLg0KKiBgcGNoYDogdGhlIHVzdWFsIG51bWVyaWMgcGNoIGZvciBwbG90KCksIGJ1dCBlYWNoIHZhbHVlIGdpdmVuIHdpbGwgYmUgYXNzaWduZWQgdG8gZWFjaCBzZXQgb2YgcC12YWx1ZXMuIENhbiBoZWxwIGRpc3Rpbmd1aXNoIHBvaW50cyB3aGVuIHRoZXJlIGFyZSBtYW55IGNvbG91cnMuIFdpbGwgYmUgcmVjeWNsZWQgaWYgbm90IGVub3VnaCBwY2ggdmFsdWVzIGFyZSBwcm92aWRlZC4NCiogYGFscGhhYDogbnVtZXJpYyBiZXR3ZWVuIDAgKHRyYW5zcGFyZW50KSBhbmQgMSAob3BhcXVlKS4gQnkgZGVmYXVsdCwgdGhlIHBvaW50cyBhcmUgcGxvdHRlZCB3aXRoIHNvbWUgdHJhbnNwYXJlbmN5IHRvIGJlIGFibGUgdG8gc2VlIHRoZSBtdWx0aXBsZSBkaXN0cmlidXRpb25zLCBjaGFuZ2UgdGhpcyBwYXJhbWV0ZXIgdG8gbWFrZSBpdCBtb3JlL2xlc3MgdHJhbnNwYXJlbnQuDQoNCmBgYHtyfQ0KcHYyIDwtIC1sb2cxMChyZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCkNCmNvbXAuc2t5cGxvdChsaXN0KHB2MSxwdjIpLG1wbWFwLA0KICAgICAgICAgICAgIG1haW4gPSAiQ29tcGFyaXNvbiBvZiB0d28gcC12YWx1ZSBkaXN0cmlidXRpb25zIiwNCiAgICAgICAgICAgICB0aHJlc2hvbGQgPSAzLjk1LHBjaCA9IGMoMTksMTcpKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHVzZSBpdCB0byBjb21wYXJlIHRoZSByZXN1bHRzIHdlIGhhdmUgYmVlbiBnZW5lcmF0aW5nLg0KDQpgYGB7cn0NCiNSZW1lbWJlciB3ZSBtdXN0IGFwcGx5IHRoZSAtbG9nMTAgdHJhbnNmb3JtYXRpb24NCnB2YWxzIDwtIGxhcHBseShwdmFscyxmdW5jdGlvbihpKSAtbG9nMTAoaSkpDQpjb21wLnNreXBsb3QocHZhbHMsbWFwID0gbXBtYXAsDQogICAgICAgICAgICAgbGVnc3BhY2UgPSAwLjIyKQ0KYGBgDQpgYGB7cn0NCiNBZ2FpbiB0aGUgbm9uLWNvcnJlY3RlZCBtb2RlbHMgYXJlIHRvbyBvdXRseWluZw0KY29tcC5za3lwbG90KHB2YWxzWy0xOi0yXSwNCiAgICAgICAgICAgICBtYXA9IG1wbWFwLA0KICAgICAgICAgICAgIHBjaCA9IGMoMTUsMTcsMTkpLGxlZ3NwYWNlID0gMC4yMiwNCiAgICAgICAgICAgICBtYWluPSAiQ29tcGFyaXNvbiBvZiBtb2RlbHMgb24gZXhhbXBsZSBkYXRhIikNCmBgYA0KDQojIyBQaGVub3R5cGUgYm94cGxvdA0KDQpJdCBpcyB1c2VmdWwgdG8gY29ycmVsYXRlIHRoZSBkb3NhZ2Ugb2YgYSBzaW5nbGUgbWFya2VyIHdpdGggdGhlIHZhbHVlIG9mIGEgcGhlbm90eXBlLCBhcyBzb21ldGltZXMgdGhhdCBjYW4gcmV2ZWFsIOKAnGRvc2FnZSBlZmZlY3Rz4oCdLiBJbiBvcmRlciB0byBzaW1wbGlmeSB0aGUgcHJvY2VzcyBvZiBnZW5lcmF0aW5nIHN1Y2ggYm94cGxvdHMsIHdlIGhhdmUgY3JlYXRlZCBhIHdyYXBwZXIgdGhhdCBtYWtlcyB0aGVtIHVzaW5nIGRvc2FnZXMgb3IgaGFwbG90eXBlcy4NCg0KVGhlcmUgaXMgYSBzaW5nbGUgZnVuY3Rpb24sIGBwaGVub19ib3goKWAgdGhhdCBjYW4gdXNlIHR3byBkaWZmZXJlbnQgbWV0aG9kcywgZWl0aGVyIGZvciBkb3NhZ2VzIG9yIGZvciBoYXBsb3R5cGVzLCBieSBjaGFuZ2luZyB0aGUgcGFyYW1ldGVyIGhhcGxvdHlwZSAoRmFsc2UgYnkgZGVmYXVsdCkuIFNvbWUgcGFyYW1ldGVycyBiZWhhdmUgaWRlbnRpY2FsbHkgbm8gbWF0dGVyIHdoYXQgdGhlIHZhbHVlIG9mIGhhcGxvdHlwZSBpczoNCg0KKiBgcGhlYDogaXMgYSBudW1lcmljYWwgdmVjdG9yIG9mIHBoZW5vdHlwZXMNCiogYGdlbmA6IGlmIGhhcGxvdHlwZSA9IEYsIGdlbiBzaG91bGQgYmUgYSBudW1lcmljIHZlY3RvciBvZiBkb3NhZ2VzIHdpdGggYSBzaW5nbGUgb2JzZXJ2YXRpb24gcGVyIGluZGl2aWR1YWwuIElmIGhhcGxvdHlwZSA9IFQsIGdlbiBzaG91bGQgYmUgYSB2ZWN0b3Igd2l0aCBwDQogbnVtZXJpYy9jaGFyYWN0ZXIgb2JzZXJ2YXRpb25zIHBlciBpbmRpdmlkdWFsIHdoZXJlIHANCiBpcyB0aGUgcGxvaWR5LCBhbmQgZWFjaCBvYnNlcnZhdGlvbiBpcyBhIGhhcGxvdHlwZSBjbGFzcyAoZS5nLiAxMjAsIDE0MCwgMTI44oCmO0EsIEIsIEPigKY7IGhhcDEsIGhhcDIsIGhhcDPigKYpLg0KKiBgZHJhdy5wb2ludHNgOiBpcyBhIGxvZ2ljYWwgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciBwb2ludHMgc2hvdWxkIGJlIGRyYXduLg0KKiBgLi4uYDogZnVydGhlciBhcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHBsb3QoKQ0KDQpXaGVuIGhhcGxvdHlwZSA9IFQgb3RoZXIgcGFyYW1ldGVycyBjYW4gYmUgdXNlZDogDQoNCiogYHBsb2lkeWA6IGlzIGFuIGludGVnZXIgaW5kaWNhdGluZyB0aGUgcGxvaWR5LiANCiogYGhhcC5zZWxlY3RgOiBpcyBhIHZlY3RvciBvZiBudW1lcmljL2NoYXJhY3RlciBpbmRpY2F0aW5nIHdoaWNoIGhhcGxvdHlwZXMgc2hvdWxkIGJlIHBsb3R0ZWQuDQoNCmBgYHtyfQ0KI1dlIGNob29zZSB0aGUgbW9zdCBzaWduaWZpY2FudCBtYXJrZXIgaW4gb3VyIFFUTCBhbmFseXNpcw0KYmVzdF9zbnAgPC0gd2hpY2gubWluKHJlc3VsdF9taXgkcGhlbm90eXBlMSRwdmFsKQ0KYmVzdF9oYXAgPC0gd2hpY2gubWluKHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCkNCmdlbl9zbnAgPC0gdW5saXN0KG1wc25wZG9zZVtiZXN0X3NucCxdKQ0KZ2VuX2hhcCA8LSB1bmxpc3QobXBoYXBkb3NlW2Jlc3RfaGFwLF0pDQoNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5fc25wLA0KICAgICAgICAgIHhsYWI9IkRvc2FnZSIseWxhYj0icGhlbm90eXBlIixtYWluPSJBIGJveHBsb3Qgb2YgZG9zYWdlcyIpDQoNCiNCdXQgbm93IHRoZXJlIGFyZSB0b28gbWFueSB0aGluZ3MgcGxvdHRlZCBhbmQgSSBjYW4ndCBzZWUgYW55dGhpbmcNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCwNCiAgICAgICAgICB4bGFiPSJIYXBsb3R5cGVzIix5bGFiPSJwaGVub3R5cGUiLG1haW49IkEgYm94cGxvdCBvZiBoYXBsb3R5cGUgZG9zYWdlcyIpDQoNCiNUaGlzIGlzIGJldHRlciBidXQgc3RpbGwgdG9vIG1hbnkgYm94ZXMNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCxkcmF3LnBvaW50cyA9IEYsDQogICAgICAgICAgeGxhYj0iSGFwbG90eXBlcyIseWxhYj0icGhlbm90eXBlIixtYWluPSJBIGJveHBsb3Qgb2YgaGFwbG90eXBlIGRvc2FnZXMgKG5vIHBvaW50cykiKQ0KDQojVGhpcyBpcyBiZXR0ZXINCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCwgDQogICAgICAgICAgaGFwLnNlbGVjdCA9IGMoIjU3IiwiNDYiLCAiMzciLCI2OSIpLA0KICAgICAgICAgIHhsYWI9IkhhcGxvdHlwZXMiLHlsYWI9InBoZW5vdHlwZSIsDQogICAgICAgICAgbWFpbj0iQSBib3hwbG90IG9mIHNvbWUgaGFwbG90eXBlIGRvc2FnZXMiKQ0KYGBgDQoNCiMjIENvbG91ciBjaG9pY2UNCg0KVGhlIGNvbG91ciBzeXN0ZW0gb2YgdGhlIHZpc3VhbGl6YXRpb24gZnVuY3Rpb25zIGluIHRoZSBtcFFUTCBwYWNrYWdlIGlzIGEgYml0IGRpZmZlcmVudCB0aGFuIHRoZSBkZWZhdWx0IG1ldGhvZHMgdGhhdCBtb3N0IFIgcGxvdHMgaW5jbHVkZS4gVG8gcGVyZm9ybSBjb2xvdXIgY2hvaWNlIHdlIHVzZSB0aGUgcGFja2FnZSBjb2xvcnNwYWNlLCB3aGljaCB1c2VzIHRoZSBIQ0wgKGh1ZSwgY29sb3VyIHRvbmU7IGNocm9tYSwgY29sb3VyIGludGVuc2l0eTsgYW5kIGx1bWluYW5jZSwgY29sb3VyIGxpZ2h0bmVzcykgc3lzdGVtIHRvIGRlZmluZSBjb2xvdXIuIFRoaXMgcGFja2FnZSBoYXMgYmVlbiBkZXNpZ25lZCB3aXRoIGRhdGEgdmlzdWFsaXphdGlvbiBpbiBtaW5kIGFuZCBvZmZlcnMgZ3JlYXQgZnVuY3Rpb25hbGl0aWVzIGZvciBpbnRlbGxpZ2VudCBhbmQgZWZmZWN0aXZlIGNvbG91ciBjaG9pY2UuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCwgSSBoaWdobHkgcmVjb21tZW5kIHZpc2l0aW5nIHRoZWlyIFt3ZWIgcGFnZV0oaHR0cDovL2NvbG9yc3BhY2Uuci1mb3JnZS5yLXByb2plY3Qub3JnLyAiQSBUb29sYm94IGZvciBNYW5pcHVsYXRpbmcgYW5kIEFzc2Vzc2luZyBDb2xvcnMgYW5kIFBhbGV0dGVzIiksIHdoZXJlIHRoZXkgZXhwbGFpbiB0aGUgcGFja2FnZSBhbmQgbWFueSBpbXBvcnRhbnQgY29uY2VwdHMgb2YgY29sb3VyIHRoZW9yeSBhbmQgZGVzaWduLg0KDQpJbiBlc3NlbmNlLCB3ZSBjYW4gY2hvb3NlIGJldHdlZW4gKipxdWFsaXRhdGl2ZSoqLCAqKnNlcXVlbnRpYWwqKiBvciAqKmRpdmVyZ2VudCoqIGNvbG9yIHBhbGV0dGVzLiBTZXR0aW5nIGBjb2x0eXBlYCBpbiBtb3N0IGZ1bmN0aW9ucyB0byBhbnkgb2YgdGhlc2UgdGhyZWUgc3lzdGVtcyB3aWxsIGNoYW5nZSB0aGUgdHlwZSBvZiBwYWxldHRlIHVzZWQuIA0KDQpBIHF1YWxpdGF0aXZlIHBhbGV0dGUgaGFzIGRpZmZlcmVudCBjb2xvdXJzIChodWVzKSwgYW5kIGlzIHVzZWZ1bCBmb3IgKipjYXRlZ29yaWNhbCoqIHZhcmlhYmxlcy4NCg0KQSBzZXF1ZW50aWFsIHBhbGV0dGUgaGFzIHNldmVyYWwgdG9uZXMgb2YgdGhlIHNhbWUgY29sb3IgKGx1bWluYW5jZXMpLCBhbmQgaXMgdXNlZnVsIGZvciAqKm51bWVyaWNhbCoqIHZhcmlhYmxlcy4NCg0KQSBkaXZlcmdlbnQgcGFsZXR0ZSBoYXMgdHdvIG9wcG9zaW5nIGNvbG9ycyBhbmQgYW4gaW4tYmV0d2VlbiBuZXV0cmFsIGNvbG9yLiBGb3IgZXhhbXBsZSByZWQsIHdoaXRlIGFuZCBibHVlLiBUaGVzZSBwYWxldHRlcyBhcmUgdXNlZnVsIGZvciAqKm51bWVyaWNhbCoqIHZhcmlhYmxlcyB0aGF0IGhhdmUgZWl0aGVyIHZlcnkgbG93IG9yIHZlcnkgaGlnaCB2YWx1ZXMuDQoNCkluIGFsbCBwbG90dGluZyBmdW5jdGlvbnMgeW91IGNhbiB1c2UgdGhlIHBhcmFtZXRlcnM6DQoNCiogYGNvbHR5cGVgOiBzdGFuZGluZyBmb3IgY29sb3VyIHBhbGV0dGUgdHlwZSwgd2UgY2FuIGNob29zZSBiZXR3ZWVuIOKAnHNlcXVlbnRpYWzigJ0sIOKAnHF1YWxpdGF0aXZl4oCdLCDigJxkaXZlcmdlbnTigJ0gb3Ig4oCccmFpbmJvd+KAnS4NCiogYGhgOiBvbmUgb3IgdHdvIG51bWVyaWNhbCB2YWx1ZXMsIHN0YW5kaW5nIGZvciBodWUuIFRoZSB2YWx1ZXMgYXJlIGRlZ3JlZXMgd2l0aGluIHRoZSBjb2xvdXIgd2hlZWwsIGFuZCB0aHVzIHZhbHVlcyBiZXR3ZWVuIDAgYW5kIDM2MCBhcmUgcmVjb21tZW5kZWQsIHdoZXJlIDAgYW5kIDM2MCBhcmUgdGhlIHNhbWUuIEZvciBhIGNvbG91ciByZWZlcmVuY2UgeW91IGNhbiB1c2UgYGh1ZV93aGVlbCgpYCB3aGljaCBwcm9kdWNlcyB0aGUgcGxvdCBiZWxvdy4NCiogYGxgOiBvbmUgKGZvciBxdWFsaXRhdGl2ZSkgb3IgdHdvIChmb3Igc2VxdWVudGlhbCkgbnVtZXJpY2FsIHZhbHVlcy4gVGhpcyBjb250cm9scyB0aGUgbGlnaHRuZXNzIG9mIHRoZSBjb2xvdXJzLCBieSBkZWZhdWx0IHNldCB0byA2MC4gVmFsdWVzIHNob3VsZCBiZSBiZXR3ZWVuIDIwIGFuZCAxMDAsIGJlbG93IG9yIGFib3ZlIHRoZSByZXN1bHRzIHdpbGwgYmUgdW5leHBlY3RlZC4NCg0KYGBge3IsIG91dC5oZWlnaHQ9NTAwLCBvdXQud2lkdGg9ODAwfQ0KcGFyKG1mcm93ID0gYygyLDIpLA0KICAgIG1hciA9IGMoMSwwLDMsMCkpDQpmb3IobCBpbiBjKDQwLDYwLDgwLDEwMCkpew0KICBodWVfd2hlZWwobCA9IGwpDQp9DQpgYGANCg0K