Introduction

This vignette will help you understand how to use mpQTL to perform a QTL analysis. Our package is specially suitable if you are working on polyploid populations or if you use haplotype-based markers. This means that both classical biallelic markers (e.g. continuous or discrete SNP data) and multiallelic markers (SSRs, haplotypes, etc.) can be used to perform a QTL analysis.

At the moment we only support the classical mixed model approach to perform GWAS-style QTL analyses on these markers (with models similar to Yu et al. 2006 and Garin et al. 2017). You can find more detailed information in our publication.

When is mpQTL most useful?

In our research we found that our model is most useful when allelic diversity is high, meaning that we can expect multiple alleles segregating at once in our study population. We can expect this situation in diverse populations with multiple ancestral founders, populations where heterozygosity is high, or when polyploidy (and heterozygosity) is high.

Input

The package comes with some example data (mpqsnpdose, mphapdose, mpped, mpmap and mppheno). We will use this data to explain the capabilites of mpQTL.

Genotype data

Three types of genotype data can be understood by mpQTL:

  • SNP dosages: a matrix of discrete dosages, from 0 until whatever ploidy level, with SNPs on rows and individuals in columns.
  • Continuous SNP genotypes: a matrix of continuous genotypes with markers in rows and individuals in columns. For instance the allele ratio of array intensities or read counts \(ref / (alt + ref)\).
  • Haplotype names: a matrix representing the haplotype composition of each individual. Each column corresponds to an individual chromosome, and they must be named IND_n where IND is the name of the individual and n is the chromosome number. The values in this matrix are treated as characters, so any value should work well here. Importantly, the haplotype phase does not matter (i.e. you can switch haplotype names between columns and the results will not change).

In all matrix types missing values are allowed.

knitr::kable(mpsnpdose[1:5,1:9],caption = "SNP dosage")
SNP dosage
A1_P1 A1_P2 A1_P3 A2_P4 A2_P5 A2_P6 A3_P7 A3_P8 A3_P9
PotVar0119912 3 3 3 0 1 0 3 0 3
PotVar0071966 3 4 4 2 1 2 3 0 3
PotVar0119973 3 2 2 3 4 3 3 1 3
PotVar0120130 2 0 1 3 2 3 2 2 2
PotVar0120099 1 0 3 4 3 4 3 4 3
#Continuous SNP genotypes would be specified similarly
knitr::kable(round(mpsnpdose[1:5,1:9]/4,2),caption = "Continuous SNP dosage")
Continuous SNP dosage
A1_P1 A1_P2 A1_P3 A2_P4 A2_P5 A2_P6 A3_P7 A3_P8 A3_P9
PotVar0119912 0.75 0.75 0.75 0.00 0.25 0.00 0.75 0.00 0.75
PotVar0071966 0.75 1.00 1.00 0.50 0.25 0.50 0.75 0.00 0.75
PotVar0119973 0.75 0.50 0.50 0.75 1.00 0.75 0.75 0.25 0.75
PotVar0120130 0.50 0.00 0.25 0.75 0.50 0.75 0.50 0.50 0.50
PotVar0120099 0.25 0.00 0.75 1.00 0.75 1.00 0.75 1.00 0.75
#Note that here we are only showing the haplotypes of two individuals
knitr::kable(mphapdose[1:5,1:8], caption = "Haplotype names")
Haplotype names
A1_P1_1 A1_P1_2 A1_P1_3 A1_P1_4 A1_P2_1 A1_P2_2 A1_P2_3 A1_P2_4
PotVar0119912 28 69 35 50 28 9 42 35
PotVar0071966 28 69 35 50 28 9 24 35
PotVar0119973 76 69 35 50 76 9 28 35
PotVar0120130 76 69 9 50 76 9 76 9
PotVar0120099 76 69 9 50 76 9 76 9

Genetic map

A genetic map should be a data.frame with at least three columns named marker, chromosome and position. By default mpQTL functions expect a centimorgan position, but parameters can be tweaked to accept a basepair position, we’ll see that later.

You can also include unmapped markers in the analysis by indicating that they belong to chromosome 0. Importantly, they should also have a position. The easiest option is to give them subsequent numbers (first marker position 1, second position 2, etc.). If many unmapped markers are present it is better to have the length of chromosome 0 be a reasonable size for whatever position unit you use. For example if there are 1000 unmapped markers and you use cM, each chromosome should measure about 100 cM. You can generate equally spaced positions with seq(0, 100, length.out = 1000).

knitr::kable(mpmap[1:5,], caption = "Genetic map",row.names = FALSE)
Genetic map
marker chromosome position
PotVar0119912 1 0.28
PotVar0071966 1 0.78
PotVar0119973 1 1.15
PotVar0120130 1 1.69
PotVar0120099 1 1.70

Phenotypes

Lastly, phenotypes can be expressed either as a numeric vector or a numeric matrix, where each column is a different phenotype. With both formats the phenotypes can be passed to map.QTL() to generate the QTL mapping results.

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)
}


  1. Wageningen University & Research, ↩︎

  2. Wageningen University & Research, ↩︎

LS0tDQp0aXRsZTogIlFUTCBhbmFseXNpcyB3aXRoIHRoZSBSIHBhY2thZ2UgbXBRVEwiDQphdXRob3I6DQogIC0gQWxlamFuZHJvIFRoZXJlc2UgTmF2YXJyb15bV2FnZW5pbmdlbiBVbml2ZXJzaXR5ICYgUmVzZWFyY2gsIGFsZWphbmRyby50aGVyZXNlbmF2YXJyb0B3dXIubmxdDQogIC0gR2lvcmdpbyBUdW1pbm9eW1dhZ2VuaW5nZW4gVW5pdmVyc2l0eSAmIFJlc2VhcmNoLCBnaW9yZ2lvLnR1bWlub0B3dXIubmxdDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvY19jb2xsYXBzZWQ6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCnRoZW1lOiBmbGF0bHkNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmxpYnJhcnkobXBRVEwpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyB2aWduZXR0ZSB3aWxsIGhlbHAgeW91IHVuZGVyc3RhbmQgaG93IHRvIHVzZSBgbXBRVExgIHRvIHBlcmZvcm0gYSBRVEwgYW5hbHlzaXMuIE91ciBwYWNrYWdlIGlzIA0Kc3BlY2lhbGx5IHN1aXRhYmxlIGlmIHlvdSBhcmUgd29ya2luZyBvbiBwb2x5cGxvaWQgcG9wdWxhdGlvbnMgb3IgaWYgeW91IHVzZSBoYXBsb3R5cGUtYmFzZWQgbWFya2Vycy4gVGhpcyBtZWFucw0KdGhhdCBib3RoIGNsYXNzaWNhbCBiaWFsbGVsaWMgbWFya2VycyAoZS5nLiBjb250aW51b3VzIG9yIGRpc2NyZXRlIFNOUCBkYXRhKSBhbmQgbXVsdGlhbGxlbGljIG1hcmtlcnMgKFNTUnMsIGhhcGxvdHlwZXMsIGV0Yy4pIGNhbiBiZSB1c2VkIHRvIHBlcmZvcm0gYSBRVEwgYW5hbHlzaXMuIA0KDQpBdCB0aGUgbW9tZW50IHdlIG9ubHkgc3VwcG9ydCB0aGUgY2xhc3NpY2FsICoqbWl4ZWQgbW9kZWwqKiBhcHByb2FjaCB0byBwZXJmb3JtIEdXQVMtc3R5bGUgUVRMIGFuYWx5c2VzDQpvbiB0aGVzZSBtYXJrZXJzICh3aXRoIG1vZGVscyBzaW1pbGFyIHRvIFtZdSBldCBhbC4gMjAwNl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMzgvbmcxNzAyICJBIHVuaWZpZWQgbWl4ZWQtbW9kZWwgbWV0aG9kIGZvciBhc3NvY2lhdGlvbiBtYXBwaW5nIHRoYXQgYWNjb3VudHMgZm9yIG11bHRpcGxlIGxldmVscyBvZiByZWxhdGVkbmVzcyIpIGFuZCBbR2FyaW4gZXQgYWwuIDIwMTddKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDA3L3MwMDEyMi0wMTctMjkyMy0zICJIb3cgZG8gdGhlIHR5cGUgb2YgUVRMIGVmZmVjdCBhbmQgdGhlIGZvcm0gb2YgdGhlIHJlc2lkdWFsIHRlcm0gaW5mbHVlbmNlIFFUTCBkZXRlY3Rpb24gaW4gbXVsdGktcGFyZW50IHBvcHVsYXRpb25zPyBBIGNhc2Ugc3R1ZHkgaW4gdGhlIG1haXplIEVVLU5BTSBwb3B1bGF0aW9uIikpLiBZb3UgY2FuIGZpbmQgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiANCmluIFtvdXIgcHVibGljYXRpb25dKGh0dHBzOi8vZG9pLm9yZy8xMC4xMTg2L3MxMjg1OS0wMjItMDQ2MDcteiAiTXVsdGlhbGxlbGljIG1vZGVscyBmb3IgUVRMIG1hcHBpbmcgaW4gZGl2ZXJzZSBwb2x5cGxvaWQgcG9wdWxhdGlvbnMiKS4NCg0KIyMjIyBXaGVuIGlzIGBtcFFUTGAgbW9zdCB1c2VmdWw/DQoNCkluIG91ciByZXNlYXJjaCB3ZSBmb3VuZCB0aGF0IG91ciBtb2RlbCBpcyBtb3N0IHVzZWZ1bCB3aGVuIGFsbGVsaWMgZGl2ZXJzaXR5IGlzIGhpZ2gsIG1lYW5pbmcNCnRoYXQgd2UgY2FuIGV4cGVjdCBtdWx0aXBsZSBhbGxlbGVzIHNlZ3JlZ2F0aW5nIGF0IG9uY2UgaW4gb3VyIHN0dWR5IHBvcHVsYXRpb24uIFdlIGNhbiBleHBlY3QgdGhpcyANCnNpdHVhdGlvbiBpbiBkaXZlcnNlIHBvcHVsYXRpb25zIHdpdGggbXVsdGlwbGUgYW5jZXN0cmFsIGZvdW5kZXJzLCBwb3B1bGF0aW9ucyB3aGVyZSBoZXRlcm96eWdvc2l0eQ0KaXMgaGlnaCwgb3Igd2hlbiBwb2x5cGxvaWR5IChhbmQgaGV0ZXJvenlnb3NpdHkpIGlzIGhpZ2guDQoNCiMgSW5wdXQNCg0KVGhlIHBhY2thZ2UgY29tZXMgd2l0aCBzb21lIGV4YW1wbGUgZGF0YSAoYG1wcXNucGRvc2VgLCBgbXBoYXBkb3NlYCwgYG1wcGVkYCwgYG1wbWFwYCBhbmQgYG1wcGhlbm9gKS4gV2UNCndpbGwgdXNlIHRoaXMgZGF0YSB0byBleHBsYWluIHRoZSBjYXBhYmlsaXRlcyBvZiBgbXBRVExgLg0KDQojIyBHZW5vdHlwZSBkYXRhDQoNClRocmVlIHR5cGVzIG9mIGdlbm90eXBlIGRhdGEgY2FuIGJlIHVuZGVyc3Rvb2QgYnkgYG1wUVRMYDoNCg0KLSAqKlNOUCBkb3NhZ2VzKio6IGEgbWF0cml4IG9mIGRpc2NyZXRlIGRvc2FnZXMsIGZyb20gMCB1bnRpbCB3aGF0ZXZlciBwbG9pZHkgbGV2ZWwsIHdpdGggU05QcyBvbiByb3dzIGFuZCBpbmRpdmlkdWFscyBpbiBjb2x1bW5zLg0KLSAqKkNvbnRpbnVvdXMgU05QIGdlbm90eXBlcyoqOiBhIG1hdHJpeCBvZiBjb250aW51b3VzIGdlbm90eXBlcyB3aXRoIG1hcmtlcnMgaW4gcm93cyBhbmQgaW5kaXZpZHVhbHMgaW4gY29sdW1ucy4gRm9yIGluc3RhbmNlIHRoZSBhbGxlbGUgcmF0aW8gb2YgYXJyYXkgaW50ZW5zaXRpZXMgb3IgcmVhZCBjb3VudHMgJHJlZiAvIChhbHQgKyByZWYpJC4NCi0gKipIYXBsb3R5cGUgbmFtZXMqKjogYSBtYXRyaXggcmVwcmVzZW50aW5nIHRoZSBoYXBsb3R5cGUgY29tcG9zaXRpb24gb2YgZWFjaCBpbmRpdmlkdWFsLiBFYWNoIGNvbHVtbg0KY29ycmVzcG9uZHMgdG8gYW4gaW5kaXZpZHVhbCBjaHJvbW9zb21lLCBhbmQgdGhleSBtdXN0IGJlIG5hbWVkICoqSU5EX24qKiB3aGVyZSAqKklORCoqIGlzIHRoZSBuYW1lIG9mIHRoZSBpbmRpdmlkdWFsIGFuZCAqKm4qKiBpcyB0aGUgY2hyb21vc29tZSBudW1iZXIuIFRoZSB2YWx1ZXMgaW4gdGhpcyBtYXRyaXggYXJlIHRyZWF0ZWQgYXMgY2hhcmFjdGVycywgc28gDQphbnkgdmFsdWUgc2hvdWxkIHdvcmsgd2VsbCBoZXJlLiBJbXBvcnRhbnRseSwgdGhlICpoYXBsb3R5cGUgcGhhc2UqIGRvZXMgbm90IG1hdHRlciAoKmkuZS4qIHlvdSBjYW4gc3dpdGNoDQpoYXBsb3R5cGUgbmFtZXMgYmV0d2VlbiBjb2x1bW5zIGFuZCB0aGUgcmVzdWx0cyB3aWxsIG5vdCBjaGFuZ2UpLg0KDQpJbiBhbGwgbWF0cml4IHR5cGVzIG1pc3NpbmcgdmFsdWVzIGFyZSBhbGxvd2VkLg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZShtcHNucGRvc2VbMTo1LDE6OV0sY2FwdGlvbiA9ICJTTlAgZG9zYWdlIikNCg0KI0NvbnRpbnVvdXMgU05QIGdlbm90eXBlcyB3b3VsZCBiZSBzcGVjaWZpZWQgc2ltaWxhcmx5DQprbml0cjo6a2FibGUocm91bmQobXBzbnBkb3NlWzE6NSwxOjldLzQsMiksY2FwdGlvbiA9ICJDb250aW51b3VzIFNOUCBkb3NhZ2UiKQ0KDQojTm90ZSB0aGF0IGhlcmUgd2UgYXJlIG9ubHkgc2hvd2luZyB0aGUgaGFwbG90eXBlcyBvZiB0d28gaW5kaXZpZHVhbHMNCmtuaXRyOjprYWJsZShtcGhhcGRvc2VbMTo1LDE6OF0sIGNhcHRpb24gPSAiSGFwbG90eXBlIG5hbWVzIikNCmBgYA0KDQojIyBHZW5ldGljIG1hcA0KDQpBIGdlbmV0aWMgbWFwIHNob3VsZCBiZSBhIGBkYXRhLmZyYW1lYCB3aXRoIGF0IGxlYXN0IHRocmVlIGNvbHVtbnMgbmFtZWQgYG1hcmtlcmAsIGBjaHJvbW9zb21lYCBhbmQgYHBvc2l0aW9uYC4NCkJ5IGRlZmF1bHQgYG1wUVRMYCBmdW5jdGlvbnMgZXhwZWN0IGEgY2VudGltb3JnYW4gcG9zaXRpb24sIGJ1dCBwYXJhbWV0ZXJzIGNhbiBiZSB0d2Vha2VkIHRvIGFjY2VwdCBhIGJhc2VwYWlyIHBvc2l0aW9uLCB3ZSdsbCBzZWUgdGhhdCBsYXRlci4gDQoNCllvdSBjYW4gYWxzbyBpbmNsdWRlIHVubWFwcGVkIG1hcmtlcnMgaW4gdGhlIGFuYWx5c2lzIGJ5IGluZGljYXRpbmcgdGhhdCB0aGV5IGJlbG9uZyB0byAqKmNocm9tb3NvbWUgMCoqLiBJbXBvcnRhbnRseSwgdGhleSBzaG91bGQgYWxzbyBoYXZlIGEgcG9zaXRpb24uIFRoZSBlYXNpZXN0IG9wdGlvbiBpcyB0byBnaXZlIHRoZW0gc3Vic2VxdWVudCBudW1iZXJzIChmaXJzdCANCm1hcmtlciBwb3NpdGlvbiAxLCBzZWNvbmQgcG9zaXRpb24gMiwgZXRjLikuIElmIG1hbnkgdW5tYXBwZWQgbWFya2VycyBhcmUgcHJlc2VudCBpdCBpcyBiZXR0ZXIgdG8gaGF2ZSB0aGUgbGVuZ3RoIG9mIGNocm9tb3NvbWUgMCBiZSBhIHJlYXNvbmFibGUgc2l6ZSBmb3Igd2hhdGV2ZXIgcG9zaXRpb24gdW5pdCB5b3UgdXNlLiBGb3IgZXhhbXBsZSBpZiB0aGVyZSBhcmUgMTAwMCB1bm1hcHBlZCBtYXJrZXJzIGFuZCB5b3UgdXNlIGNNLCBlYWNoIGNocm9tb3NvbWUgc2hvdWxkIG1lYXN1cmUgYWJvdXQgMTAwIGNNLiBZb3UgY2FuIGdlbmVyYXRlIGVxdWFsbHkgc3BhY2VkIHBvc2l0aW9ucyB3aXRoIGBzZXEoMCwgMTAwLCBsZW5ndGgub3V0ID0gMTAwMClgLg0KDQoNCmBgYHtyfQ0Ka25pdHI6OmthYmxlKG1wbWFwWzE6NSxdLCBjYXB0aW9uID0gIkdlbmV0aWMgbWFwIixyb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBQaGVub3R5cGVzDQoNCkxhc3RseSwgcGhlbm90eXBlcyBjYW4gYmUgZXhwcmVzc2VkIGVpdGhlciBhcyBhIG51bWVyaWMgdmVjdG9yIG9yIGEgbnVtZXJpYyBtYXRyaXgsIHdoZXJlIGVhY2ggY29sdW1uIGlzIGEgZGlmZmVyZW50IHBoZW5vdHlwZS4gV2l0aCBib3RoIGZvcm1hdHMgdGhlIHBoZW5vdHlwZXMgY2FuIGJlIHBhc3NlZCB0byBgbWFwLlFUTCgpYCB0byBnZW5lcmF0ZSB0aGUgUVRMIG1hcHBpbmcgcmVzdWx0cy4NCg0KIyBVc2luZyBgbWFwLlFUTCgpYA0KDQpUaGUgbWFpbiBmdW5jdGlvbiBgbWFwLlFUTCgpYCBjYW4gYmUgdXNlZCB0byBwZXJmb3JtIGEgc2luZ2xlLW1hcmtlciB0ZXN0LiBEZXBlbmRpbmcgb24geW91ciBpbnB1dCBhIGxpbmVhciBtb2RlbCAod2l0aG91dCBraW5zaGlwIGNvcnJlY3Rpb24pIG9yIGEgbWl4ZWQgbW9kZWwgKHdpdGgga2luc2hpcCBjb3JyZWN0aW9uKSB3aWxsIGJlIGNob3Nlbi4gTWlzc2luZyBnZW5vdHlwZXMgY2FuIGJlIGltcHV0ZWQgdXNpbmcgdGhpcyBmdW5jdGlvbi4gRmluYWxseSwgYSBwZXJtdXRhdGlvbiBzaWduaWZpY2FuY2UgdGhyZXNob2xkIGNhbiBhbHNvIGJlIGNvbXB1dGVkIChhbHRob3VnaCBpdCBpbmNyZWFzZXMgY29tcHV0YXRpb25hbCB0aW1lIHF1aXRlIGEgYml0KS4NCg0KTGV0IHVzIGxvb2sgYSB0aGUgZGlmZmVyZW50IHRvb2xzIG9mIGBtYXAuUVRMKClgDQoNCiMjIExpbmVhciBtb2RlbA0KDQpUaGUgYmFzaWMgZm91ciBwYXJhbWV0ZXJzIG9mIG1hcC5RVEwoKSBhcmU6DQoNCiogYHBoZW5vdHlwZXNgOiBhIHZlY3RvciBvciBtYXRyaXggd2l0aCBhIGNvbHVtbiBmb3IgZWFjaCBwaGVub3R5cGUuDQoqIGBnZW5vdHlwZXNgOiBlaXRoZXIgYSBkb3NhZ2Ugb3IgYSBoYXBsb3R5cGUgbWF0cml4Lg0KKiBgcGxvaWR5YDogYW4gaW50ZWdlciBpbmRpY2F0aW5nIHRoZSBwbG9pZHkgb2YgdGhlIG9yZ2FuaXNtLg0KKiBgbWFwYDogYSBnZW5ldGljIG1hcCB3aXRoIHRoZSBjb2x1bW5zIOKAnG1hcmtlcuKAnSwg4oCcY2hyb21vc29tZeKAnSwgYW5kIOKAnHBvc2l0aW9u4oCdLg0KDQpUaGUgc2ltcGxlc3QgUVRMIG1vZGVsIHRoYXQgYG1hcC5RVEwoKWAgcHJvdmlkZXMgaXMgYSBsaW5lYXIgbW9kZWwgd2l0aCBubyBrbnNoaXAgY29ycmVjdGlvbi4gSW4gdGhpcyBjYXNlLCB0aGUgcC12YWx1ZXMgb3JpZ2luYXRlIGZyb20gYW4gRi10ZXN0Lg0KDQpgYGB7cn0NCnJlc3VsdF9kb3MgPC0gbWFwLlFUTChwaGVub3R5cGVzID0gbXBwaGVubywNCiAgICAgICAgZ2Vub3R5cGVzID0gbXBzbnBkb3NlLA0KICAgICAgICBwbG9pZHkgPSA0LA0KICAgICAgICBtYXAgPSBtcG1hcCkNCg0Kc3RyKHJlc3VsdF9kb3MsbWF4LmxldmVsID0gMiwgZ2l2ZS5hdHRyID0gRikNCg0KYGBgDQoNClRoZSByZXN1bHRzIGFyZSBwcm92aWRlZCBpbiBmb3JtIG9mIGEgbmVzdGVkIGxpc3QuIEZpcnN0bHksIHRoZXJlIGlzIGFuIGVsZW1lbnQgZm9yIGVhY2ggcGhlbm90eXBlLCBhbmQgd2l0aGluIGVhY2ggcGhlbm90eXBlIHRoZXJlIGFyZSBmb3VyIGVsZW1lbnRzOg0KDQoqIGBiZXRhYDogYSBsaXN0IGNvbnRhaW5pbmcgdGhlIGZpeGVkIGVmZmVjdHMgb2YgdGhlIG1vZGVsLiBUaGVyZSBpcyBhIHNldCBvZiBlc3RpbWF0ZXMgZm9yIGVhY2ggbWFya2VyLg0KKiBgRnRlc3RgOiBhIHZlY3RvciBjb250YWluaW5nIHRoZSBGdGVzdCByZXN1bHRzIG9ubHkgZm9yIHRoZSBnZW5ldGljIGNvbXBvbmVudCBvZiBlYWNoIG1vZGVsLg0KKiBgcHZhbGA6IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHAtdmFsdWVzIG9ubHkgb2YgdGhlIGdlbmV0aWMgbW9kZWwgYXQgZWFjaCBtYXJrZXIuDQoqIGBzZWA6IGEgdmVjdG9yIGNvbnRhaW5pbmcgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBlc3RpbWF0ZXMuDQoNCkl0IGlzIGltcG9ydGFudCB0byByZWFsaXplIHRoYXQgdGhlIHAtdmFsdWVzIGluZGljYXRlIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIGdlbmV0aWMgbW9kZWwgb25seSAocC12YWx1ZSBmb3IgdGhlIGh5cG90aGVzaXMgdGhhdCBhIG1hcmtlciBpcyBhc3NvY2lhdGVkIHdpdGggYSBwaGVub3R5cGUpIGFuZCB0aHVzIGRvZXMgbm90IHJlZmxlY3QgdGhlIHVzZWZ1bG5lc3Mgb2YgYW55IG90aGVyIHBhcmFtZXRlcnMgb2YgdGhlIG1vZGVsIChsaWtlIHN0cnVjdHVyZSB0ZXJtcyBvciBjb2ZhY3RvcnMpLg0KDQpJbiB0aGlzIGNhc2UgdGhlIG1vZGVsIG9ubHkgY29udGFpbnMgYW4gKippbnRlcmNlcHQqKiBhbmQgYSAqKmRvc2FnZSBlZmZlY3QqKiwgdGh1cyB0aGUgYmV0YSBjb250YWlucyBvbmx5IHR3byBwYXJhbWV0ZXJzIHBlciBtYXJrZXIuDQoNCmBgYHtyfQ0KcmVzdWx0X2Rvc1tbMV1dJGJldGFbMTozXQ0KYGBgDQoNCldlIGNhbiB1c2UgdGhlIGBza3lwbG90KClgIGZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzLg0KDQpgYGB7cn0NCiNXZSBnZXQgdGhlIHAtdmFsdWVzIGZyb20gdGhlIHJlc3VsdHMNCnB2IDwtIC1sb2cxMChyZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbGluZWFyIG1vZGVsIHVzaW5nIGRvc2FnZXMiKQ0KYGBgDQoNCldlIHNlZSB0aGF0IGR1ZSB0byB0aGUgc3RydWN0dXJlIHByZXNlbnQgaW4gdGhpcyBkYXRhc2V0LCB0aGUgcC12YWx1ZXMgdXNpbmcgYSBzaW1wbGUgbGluZWFyIG1vZGVsIGFyZSBub3Qgc3VmZmljaWVudDogc3RydWN0dXJlIGNvcnJlY3Rpb25zIGFyZSBuZWVkZWQuIFdlIHdpbGwgc2VlIGhvdyB0byBpbXBsZW1lbnQgdGhlbSB1c2luZyB0d28ga2luc2hpcCBjb3JyZWN0aW9ucyBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb24uDQoNCkluc3RlYWQgb2YgdXNpbmcgc25wIGRvc2FnZXMgd2UgY2FuIHByb3ZpZGUgYSAqKmhhcGxvdHlwZSBtYXRyaXgqKi4gQWxsIHRoZSBvdXRwdXQgd2lsbCBiZSB0aGUgc2FtZSwgZXhjZXB0IHRoZSBiZXRhIGVsZW1lbnQsIHdoaWNoIHdpbGwgbm93IGluY2x1ZGUgb25lIGVmZmVjdCBmb3IgZWFjaCBoYXBsb3R5cGUgYW5kIGFuIGludGVyY2VwdC4gVGhlIHJvd25hbWVzIG9mIGVhY2ggYmV0YSBlbGVtZW50IHdpbGwgYmUgdGhlIG5hbWUgb2YgZWFjaCBoYXBsb3R5cGUuDQoNCmBgYHtyfQ0KcmVzdWx0X2hhcCA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBtcHBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcGhhcGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwKQ0KDQpzdHIocmVzdWx0X2hhcCxtYXgubGV2ZWwgPSAyLCBnaXZlLmF0dHIgPSBGKQ0KcmVzdWx0X2hhcCRwaGVub3R5cGUxJGJldGFbMV0NCmBgYA0KDQpJZiB3ZSBsb29rIGF0IHRoZSBwLXZhbHVlIGRpc3RyaWJ1dGlvbiB3ZSBzZWUgdGhhdCB0aGVyZSBpcyBzdGlsbCBhIHByb2JsZW0gd2l0aCB0aGUgcC12YWx1ZSBjYWxjdWxhdGlvbiBkdWUgdG8gbm90IGFjY291bnRpbmcgZm9yIGdlbmV0aWMgc3RydWN0dXJlLg0KDQpgYGB7cn0NCnB2IDwtIC1sb2cxMChyZXN1bHRfaGFwJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbGluZWFyIG1vZGVsIHVzaW5nIGhhcGxvdHlwZXMiKQ0KYGBgDQoNCkR1cmluZyB0aGUgcmVzdCBvZiB0aGlzIHZpZ25ldHRlIHdlIHdpbGwgdXNlIHNucCBkb3NhZ2UgYW5kIGhhcGxvdHlwZSBkb3NhZ2UgZGF0YSB3aXRoIGRpZmZlcmVudCBhbmFseXNpcy4gRm9yIGFsbCB0aGUgYW5hbHlzaXMsIGFueSBvZiB0aGUgdHdvIGtpbmRzIG9mIGdlbm90eXBlcyBjYW4gYmUgcHJvdmlkZWQuDQoNCiMjIEtpbnNoaXAgY29ycmVjdGlvbg0KDQpPdXIgdG9vbCBpbXBsZW1lbnRzIHR3byB3YXlzIG9mIHdvcmtpbmcgd2l0aCBraW5zaGlwIGNvcnJlY3Rpb24gd2hpY2ggaW4gbGl0ZXJhdHVyZSBhcmUgc29tZXRpbWVzIGtub3duIGFzIHRoZSBRIGFuZCB0aGUgSyBtZXRob2RzLiBUaGUgUSBtZXRob2QgdXNlcyBhIGNvZmFjdG9yIHRoYXQgaW5kaWNhdGVzIHN1Yi1wb3B1bGF0aW9uIGVmZmVjdCwgd2hpbGUgdGhlIEsgbWV0aG9kIGVzdGltYXRlcyBhIGtpbnNoaXAgbWF0cml4IHRoYXQgaXMgdGhlbiBwYXNzZWQgdG8gYSBtaXhlZCBtb2RlbCwgd2hlcmUgYSBraW5zaGlwIGNvcnJlY3Rpb24gaXMgcGVyZm9ybWVkLiBJZiB0aGUgZ2VuZXRpYyBzdHJ1Y3R1cmUgaXMgdmVyeSBzdHJvbmcgLXNvIHRoZXJlIGFyZSBjbGVhcmx5IGlkZW50aWZpYWJsZSBncm91cHMgd2l0aGluIHRoZSBkYXRhLSB0aGUgUSBhbmQgSyBtZXRob2QgY2FuIHByb3ZpZGUgdmVyeSBzaW1pbGFyIHJlc3VsdHMuIEluIHByYWN0aWNlLCBzdWJwb3B1bGF0aW9uIGlkZW50aXR5IG9mIHNhbXBsZXMgaXMgaW5hY2N1cmF0ZSBvciBpbmNvbXBsZXRlLCB0aGUgcmVsYXRlZG5lc3MgYmV0d2VlbiBzdWJwb3B1bGF0aW9ucyBpcyBub3QgZXF1aXZhbGVudCAoc29tZSBhcmUgdmVyeSBzaW1pbGFyLCBzb21lIHZlcnkgZGlmZmVyZW50KSwgbWFraW5nIHRoZSBLIG1ldGhvZCBtb3JlIGFjY3VyYXRlLg0KDQpMZXQgdXMgYnJpZWZseSBoYXZlIGEgbG9vayBhdCBRIGFuZCBLIG1hdHJpY2VzLg0KDQojIyMgJFEkIG1hdHJpeA0KDQpUaGUgUSBtYXRyaXggY2FuIGJlIGNhbGN1bGF0ZWQgdXNpbmcgYSB2ZWN0b3IgdGhhdCBpbmRpY2F0ZXMgdGhlIHN1YnBvcHVsYXRpb24gb2YgZWFjaCBzYW1wbGUuIEluIHRoZSBleGFtcGxlIGRhdGEgdGhpcyBjYW4gYmUgc2VlbiBpbiB0aGUgaW5kaXZpZHVhbCBuYW1lcy4NCg0KV2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGNhbGMuUSgpYCB0byBjcmVhdGUgYSBRIG1hdHJpeCB0aGF0IGhhcyB0aGUgYXBwcm9waWF0ZSBwYXJhbWV0cml6YXRpb24gZm9yIGEgbGluZWFyIG1vZGVsLiBUaGlzIGZ1bmNpdG9uIGlzIG5vdCBhbiBleHBvcnRlZCBvYmplY3Qgb2YgYG1wUVRMYCBzaW5jZSB1c3VhbGx5IGl0IGlzIG9ubHkgdXNlZCBpbnRlcm5hbGx5LCBidXQgd2UgY2FuIGhhdmUgYSBsb29rIGF0IGl0IGFueXdheS4NCg0KYGBge3J9DQojV2UgdGFrZSB0aGUgZmlyc3QgdHdvIGNoYXJhY3RlcnMgYXMgcG9wdWxhdGlvbiBpbmRpY2F0b3INCnBvcCA8LSBzdWJzdHIoY29sbmFtZXMobXBzbnBkb3NlKSwxLDIpDQpRIDwtIG1wUVRMOjo6Y2FsYy5RKHBvcCkNCg0KI0hlcmUgd2UgYXNzdW1lIHRoYXQgb3VyIHN1YnBvcHVsYXRpb24gaWRlbnRpZmljYXRpb24gYWRlcXVhdGVseSByZXByZXNlbnRzIHBvcHVsYXRpb24gc3RydWN0dXJlDQpoZWF0bWFwKFEsQ29sdiA9IE5BLCBSb3d2ID0gTkEsbGFiUm93ID0gY29sbmFtZXMobXBzbnBkb3NlKSkNCmBgYA0KDQojIyMgJEskIG1hdHJpeA0KDQpXZSBjYW4gYWxzbyBjYWxjdWxhdGUgYSBraW5zaGlwIG1hdHJpeCB1c2luZyBgY2FsYy5LKClgLiBPdXIgZm9ybXVsYXRpb24gaXMgYmFzZWQgb24gdGhlIFsicmVhbGl6ZWQgcmVsYXRpb25zaGlwIiBpbiBSb3N5YXJhICpldCBhbC4qIDIwMTZdKGh0dHBzOi8vZG9pLm9yZy8xMC4zODM1L3BsYW50Z2Vub21lMjAxNS4wOC4wMDczICJTb2Z0d2FyZSBmb3IgR2Vub21lLVdpZGUgQXNzb2NpYXRpb24gU3R1ZGllcyBpbiBBdXRvcG9seXBsb2lkcyBhbmQgSXRzIEFwcGxpY2F0aW9uIHRvIFBvdGF0byIpLiBUaGUgcmVzdWx0IGhhcyB2YWx1ZXMgdGhhdCBjYW4gZ28gYmVsb3cgMCBhbmQgb3ZlciAxLiBBbHRob3VnaCB0aGlzIHNjYWxlIGlzIHNvbWV3aGF0IHN0cmFuZ2UsIGl0IGFsbG93cyBmb3IgYSB2ZXJ5IGZhc3QgY2FsY3VsYXRpb24gb2Yga2luc2hpcC4gVGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBtZWFzdXJlIGlzIHRoYXQgMCBpcyB0aGUgYXZlcmFnZSByZWxhdGVkbmVzcyBiZXR3ZWVuIGluZGl2aWR1YWxzIGluIHRoZSBwb3B1bGF0aW9uLCBhbmQgMSBpcyB0aGUgYXZlcmFnZSByZWxhdGVkbmVzcyBvZiBhbiBpbmRpdmlkdWFsIHdpdGggaXRzZWxmLiANCg0KVGhlIHBhcmFtZXRlcnMgYXJlOg0KDQotICoqbWF0cml4Kio6IGVpdGhlciBhIGRvc2FnZSBtYXRyaXggKG1hcmtlcnMgaW4gY29sdW1ucyBhbmQgaW5kaXZpZHVhbHMgaW4gcm93cyksIG9yIGEgaGFwbG90eXBlIG1hdHJpeCAocA0KIHJvd3MgcGVyIGluZGl2aWR1YWwsIHdoZXJlIHANCiBpcyBwbG9pZHkpLg0KLSAqKmhhcGxvdHlwZXMqKjogbG9naWNhbCwgYXJlIGhhcGxvdHlwZXMgcHJlc2VudD8gRGVmYXVsdHMgdG8gRmFsc2UuDQotICoqcGxvaWR5Kio6IGludGVnZXIgaW5kaWNhdGluZyBwbG9pZHkuIE9ubHkgdXNlZCBpZiBoYXBsb3R5cGVzID0gVA0KDQpgYGB7cn0NCktkIDwtIGNhbGMuSyh0KG1wc25wZG9zZSkpDQpLaCA8LSBjYWxjLksodChtcGhhcGRvc2UpLCBoYXBsb3R5cGVzID0gVCwgcGxvaWR5ID0gNCkNCg0KI1dlIGNhbiB2aXN1YWxpemUgdGhlIG1hdHJpY2VzIHVzaW5nIGhlYXRtYXBzDQpoZWF0bWFwKEtkLCBDb2x2ID0gTkEsIFJvd3YgPSBOQSwgbWFpbiA9ICJEb3NhZ2UgbWF0cml4IikNCmhlYXRtYXAoS2gsIENvbHYgPSBOQSwgUm93diA9IE5BLCBtYWluID0gIkhhcGxvdHlwZSBtYXRyaXgiKQ0KDQojVGhpcyBoZWxwcyB1cyBpZGVudGlmeSBlYWNoIGZhbWlseSBvZiBpbmRpdmlkdWFscw0KcG9wIDwtIHN1YnN0cihyb3duYW1lcyhLZCksMSwyKQ0KI09yIGEgYml0IG1vcmUgY2xlYXJseSB1c2luZyBQQ29BIHBsb3RzDQpwY29hLnBsb3QoS2QsY29sID0gcG9wLCBtYWluID0gIkRvc2FnZSBtYXRyaXgiLCBoID0gMjQwKQ0KI09yIGEgYml0IG1vcmUgY2xlYXJseSB1c2luZyBQQ29BIHBsb3RzDQpwY29hLnBsb3QoS2gsY29sID0gcG9wLCBtYWluID0gIkhhcGxvdHlwZSBtYXRyaXgiLCBoID0gMjQwKQ0KYGBgDQoNCkFsdGhvdWdoIHRoZSB2YWx1ZXMgd2l0aGluIHRoZSBkb3NhZ2UgYW5kIGhhcGxvdHlwZSBtYXRyaXggYXJlIHNvbWV3aGF0IGRpZmZlcmVudCwgd2UgY2FuIHNlZSB1c2luZyBoZWF0bWFwcyBhbmQgUENvQSBwbG90cyB0aGF0IHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBlc3RpbWF0ZWQgd2l0aCB0aGUgbWF0cmljZXMgaXMgZXF1aXZhbGVudC4NCg0KSW4gdGhlIGhlYXRtYXBzLCB3aGVuIHR3byBpbmRpdmlkdWFscyBhcmUgbW9yZSBnZW5ldGljYWxseSBzaW1pbGFyLCB0aGVpciBjZWxscyBhcmUgZGFya2VyLCBhbmQgc2luY2Ugb3VyIGluZGl2aWR1YWxzIGFyZSBvcmRlcmVkIHBlciBmYW1pbHksIHRoaXMgY3JlYXRlcyBhIGNoZWNrYm9hcmQtbGlrZSBwYXR0ZXJuLiBUaGUgaGVhdG1hcHMgYWxzbyBzaG93IHVzIHdoaWNoIGZhbWlsaWVzIGFyZSBtb3JlIGNsb3NlciByZWxhdGVkIHRvIGVhY2ggb3RoZXIsIGR1ZSB0byBzaGFyaW5nIHNpbWlsYXIgcGFyZW50cy4NCg0KTGFzdGx5LCB3ZSBjYW4gdXNlIGEgc3BlY2lhbCB0eXBlIG9mICRRJCBtYXRyaXggYmFzZWQgb24ga2luc2hpcC4gSWYgd2UgYXBwbHkgUENvQSBvbiB0aGUgJEskIG1hdHJpeCAoYXMgd2UgZG8gZm9yIHZpc3VhbGl6YXRpb24pIHdlIGNhbiBvYnRhaW4gYSAkUSQgbWF0cml4IHRoYXQgaWRlbnRpZmllcyBzdWJwb3B1bGF0aW9ucyBpbiBhIHNpbWlsYXIgd2F5IHRvIHRoZSBLIG1hdHJpeC4gVGhpcyBpcyBub3QgYXMgYWNjdXJhdGUgYnV0IGNhbiByZWR1Y2UgY29tcHV0YXRpb25hbCB0aW1lIHN1YnN0YW50aWFsbHkgYW5kIHByb3ZpZGUgZXF1aXZhbGVudCByZXN1bHRzIGlmIHRoZSBzdWJwb3B1bGF0aW9uIHN0cnVjdHVyZSBpcyBjbGVhci4NCg0KDQpgYGB7cn0NCksgPC0gY2FsYy5LKHQobXBzbnBkb3NlKSkNClEgPC0gY21kc2NhbGUoMSAtIEssIGsgPSAyLCBlaWcgPSBGQUxTRSwgYWRkID0gRkFMU0UsIHgucmV0ID0gRkFMU0UpDQoNCiNUaGlzIGhlbHBzIHVzIGlkZW50aWZ5IGVhY2ggZmFtaWx5IG9mIGluZGl2aWR1YWxzDQpwb3AgPC0gc3Vic3RyKHJvd25hbWVzKEtkKSwxLDIpDQoNCnBjb2EucGxvdChRLCBjb2wgPSBwb3AsIG1haW4gPSAiUSBtYXRyaXgiLCBoID0gMjQwKQ0KYGBgDQoNCiMjIE1peGVkIG1vZGVsIChhbmQgUSBtb2RlbCkNCg0KVG8gdXNlIHRoZSBtaXhlZCBtb2RlbCB3ZSBkbyBub3QgbmVlZCB0byBwcm92aWRlIGRpcmVjdGx5IHRoZSBraW5zaGlwIG1hdHJpeCAkSyQsIGBtYXAuUVRMKClgIHdpbGwgZGlyZWN0bHkgY2FsY3VsYXRlIGl0IGlmIGBLID0gVFJVRWAuIFlvdSBjYW4gcHJvdmlkZSB5b3VyIG93biBkaXN0YW5jZSBtYXRyaXggdXNpbmcgYEsgPSB5b3VyX2Rpc3RhbmNlX21hdHJpeGAuDQoNCkltcG9ydGFudGx5LCBLIGlzIGNhbGN1bGF0ZWQgdXNpbmcgYSBzYW1wbGUgb2YgbWFya2VycyBhY3Jvc3MgdGhlIGdlbm9tZSwgcmF0aGVyIHRoYW4gYWxsIG1hcmtlcnMuIFRoaXMgcHJldmVudHMgdGhhdCByZWdpb25zIHdoZXJlIG1hcmtlciBkZW5zaXR5IGlzIGhpZ2hlciB0byBjb250cmlidXRlIG1vcmUgdG8gdGhlIGtpbnNoaXAgdGhhbiByZWdpb25zIHRoYXQgYXJlIGxlc3MgZGVuc2UuIEJ5IGRlZmF1bHQsIDEgbWFya2VyIHBlciBjTSwgd2hlbiBwb3NzaWJsZSwgaXMgdXNlZC4gVGhpcyBjYW4gYmUgY2hhbmdlZCB3aXRoIHRoZSBwYXJhbWV0ZXIgYGJpbnNpemVgLiBJZiB5b3UgdXNlIGEgYmFzZS1wYWlyIG1hcCB5b3UgbmVlZCB0byBhZGFwdCB0aGlzIHZhbHVlIHRvIGFuIGFkZXF1YXRlIGJhc2UtcGFpciAoZm9yIGV4YW1wbGUgb25lIG1hcmtlciBldmVyeSAxMEtiKS4NCg0KYGBge3J9DQpyZXN1bHRfbWl4IDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IG1wc25wZG9zZSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIEsgPSBULA0KICAgICAgICBiaW5zaXplID0gMSkNCg0Kc3RyKHJlc3VsdF9taXgsIG1heC5sZXZlbCA9IDIsIGdpdmUuYXR0ciA9IEYpDQpgYGANCg0KQmVzaWRlcyB0aGUgcmVzdWx0cyBvYnRhaW5lZCBiZWZvcmUsIHdlIHNlZSBhIGB3YWxkYCByZXN1bHQgYW5kIGEgYHJlYWwuZGZgLCB0aGVzZSByZWxhdGUgdG8gdGhlIGZhY3QgdGhhdCBwLXZhbHVlcyBpbiBtaXhlZCBtb2RlbHMgYXJlIG9idGFpbmVkIHZpYSBhbiBhcHByb3hpbWF0aW9uIHVzaW5nIFdhbGQgdGVzdHMgYW5kIHJlYWxpemVkIGRlZ3JlZXMgb2YgZnJlZWRvbS4gQWRkaXRpb25hbGx5LCB0aGUgcmVzaWR1YWxzIGhhdmUgYmVlbiBpbmNsdWRlZCBmb3IgZWFjaCB0ZXN0Lg0KDQpgYGB7cn0NCnJlc3VsdF9taXgkcGhlbm90eXBlMSRiZXRhWzE6M10NCmBgYA0KDQpOb3RlIGFsc28gdGhhdCBpbiB0aGUgbWl4ZWQgbW9kZWwgbm8gaW50ZXJjZXB0IGlzIGNhbGN1bGF0ZWQgYW5kIHRodXMgdGhlIGVmZmVjdHMgY2FuIGJlIGludGVycHJldGVkIGFzIGRpZmZlcmVuY2VzIHdpdGggdGhlIGdlbmVyYWwgbWVhbi4NCg0KTGFzdGx5LCBpZiB3ZSB2aXN1YWxpemUgdGhlIHAtdmFsdWUgZGlzdHJpYnV0aW9uIHdlIGNhbiBzZWUgdGhhdCB0aGlzIHRpbWUgaXQgZm9sbG93cyBhIG1vcmUgcmVhc29uYWJsZSBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KcHYgPC0gLWxvZzEwKHJlc3VsdF9taXgkcGhlbm90eXBlMSRwdmFsKQ0Kc2t5cGxvdChwdiwgbXBtYXAsIG1haW49IlFUTCBkZXRlY3Rpb24gd2l0aCBtaXhlZCBtb2RlbCIpDQpgYGANCg0KV2UgY2FuIHVzZSB0aGUgUXBjbyBtZXRob2QgdG8gb2J0YWluIGEgc2ltaWxhciByZXN1bHQuIElmIHdlIHNldCBgUSA9IFRSVUVgIHdpdGhvdXQgcHJvdmlkaW5nIGEgdmVjdG9yIG9mIHBvcHVsYXRpb24gaWRlbnRpZmllcnMsIGBtYXAuUVRMKClgIHdpbGwgYXV0b21hdGljYWxseSBjYWxjdWxhdGUgYSBRIG1hdHJpeCBiYXNlZCBvbiBraW5zaGlwLiBUaGUgZGlzdHJpYnV0aW9uIGlzIG5vdyBiZXR0ZXIsIGJ1dCBvdXIgcG9wdWxhdGlvbidzIHN0cnVjdHVyZSBpcyB0b28gY29tcGxleCB0byBvbmx5IHVzZSB0aGUgUSBjb3JyZWN0aW9uIG1ldGhvZC4NCg0KYGBge3J9DQpyZXN1bHRfcXBjbyA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBtcHBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcHNucGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwLA0KICAgICAgICBRID0gVCwNCiAgICAgICAgYmluc2l6ZSA9IDAuMSkNCg0KcHYgPC0gLWxvZzEwKHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsIG1wbWFwLCBtYWluPSJRVEwgZGV0ZWN0aW9uIHdpdGggbWl4ZWQgbW9kZWwiKQ0KYGBgDQoNCiMjIENvdmFyaWF0ZXMgYW5kIGNvZmFjdG9ycw0KDQpTb21ldGltZXMgZXh0cmEgdmFyaWFibGVzIG1pZ2h0IGhlbHAgdXMgZXhwbGFpbiB0aGUgcGhlbm90eXBpYyB2YXJpYXRpb24gaW4gb3VyIGRhdGFzZXRzLiBEZXBlbmRpbmcgb24gd2hldGhlciB0aGV5IGFyZSBudW1lcmljYWwgb3IgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB0aGV5IGNhbiBiZSBjYWxsZWQgY292YXJpYXRlcyBvciBjb2ZhY3RvcnMsIHJlc3BlY3RpdmVseS4gSW4gdGhlIGBtYXAuUVRMKClgIGZ1bmN0aW9uIHdlIGhhdmUgaW5jbHVkZWQgdGhlIHBvc3NpYmlsaXR5IG9mIGFkZGluZyBhZGRpdGlvbmFsIHZhcmlhYmxlcyB0byB0aGUgbW9kZWwgdGhyb3VnaCB0aGUgcGFyYW1ldGVyczoNCg0KKiBgY29mYWN0b3JgOiBhIHZlY3RvciBvciBtYXRyaXggd2hlcmUgZWFjaCBjb2x1bW4gaXMgYSBjb2ZhY3Rvci9jb3ZhcmlhdGUuDQoqIGBjb2ZhY3Rvci50eXBlYDogdmVjdG9yIHdpdGggb25lIGNoYXJhY3RlciBzdHJpbmcgcGVyIGNvbHVtbiBpbiB0aGUgY29mYWN0b3IgbWF0cml4LCBjb250YWluaW5nIGVpdGhlciDigJxudW1lcmljYWzigJ0gb3Ig4oCcY2F0ZWdvcmljYWzigJ0uIElmIG51bWVyaWNhbCwgdGhlIHZhcmlhYmxlIGlzIHVzZWQgYXMgYSByZWdyZXNzb3IgaW4gdGhlIG1vZGVsIGFuZCBhIHNpbmdsZSBleHRyYSBlZmZlY3Qgd2lsbCBiZSBhZGRlZCwgdGh1cyB0aGUgY292YXJpYXRlIG11c3QgYmUgbnVtZXJpY2FsLiBJZiBjYXRlZ29yaWNhbCwgYW4gaW5jaWRlbmNlIG1hdHJpeCBpcyBjcmVhdGVkIHVzaW5nIGVhY2ggdW5pcXVlIHZhbHVlIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSwgdGh1cyB0aGUgY29mYWN0b3IgY2FuIGJlIG51bWVyaWNhbC9jaGFyYWN0ZXIuIE9uZSBlZmZlY3QgcGVyIGNvZmFjdG9yIHdpbGwgYmUgY2FsY3VsYXRlZC4NCg0KV2UgY2FuIHNlZSB0aGUgdXNlZnVsbmVzcyBvZiBpbmNsdWRpbmcgc3VjaCBwYXJhbWV0ZXIgdG8gdGhlIFFUTCBhbmFseXNpcyBieSBsb29raW5nIGF0IHRoZSBkaWZmZXJlbmNlcyBpbiBwLXZhbHVlcy4gV2Ugd2lsbCBjcmVhdGUgYSBwaGVub3R5cGUgaW5mbHVlbmNlZCBieSBhIGNvZmFjdG9yIHRvIG9ic2VydmUgdGhlIGRpZmZlcmVuY2VzLg0KDQpgYGB7cn0NCmNvZiA8LSBzYW1wbGUoMTozLHNpemUgPSBucm93KG1wcGhlbm8pLCByZXBsYWNlID0gVFJVRSkNCnRhYmxlKGNvZikNCmNvZnBoZW5vIDwtIG1wcGhlbm8gKyBtcHBoZW5vKmNvZi8xMA0KDQpyZXN1bHRfbWl4IDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IGNvZnBoZW5vLA0KICAgICAgICBnZW5vdHlwZXMgPSBtcHNucGRvc2UsDQogICAgICAgIHBsb2lkeSA9IDQsDQogICAgICAgIG1hcCA9IG1wbWFwLA0KICAgICAgICBLID0gVCkNCg0KcmVzdWx0X2NvZiA8LSBtYXAuUVRMKHBoZW5vdHlwZXMgPSBjb2ZwaGVubywNCiAgICAgICAgZ2Vub3R5cGVzID0gbXBzbnBkb3NlLA0KICAgICAgICBwbG9pZHkgPSA0LA0KICAgICAgICBtYXAgPSBtcG1hcCwNCiAgICAgICAgSyA9IFQsDQogICAgICAgIGNvZmFjdG9yID0gY29mLA0KICAgICAgICBjb2ZhY3Rvci50eXBlID0gImNhdGVnb3JpY2FsIikNCg0Kd2l0aG91dF9jb2ZhY3RvciA8LSAtbG9nMTAocmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwpDQp3aXRoX2NvZmFjdG9yIDwtIC1sb2cxMChyZXN1bHRfY29mJHBoZW5vdHlwZTEkcHZhbCkNCmNvbXAuc2t5cGxvdChsaXN0KHdpdGhvdXRfY29mYWN0b3Isd2l0aF9jb2ZhY3RvciksDQogICAgICAgICAgICAgbXBtYXAsDQogICAgICAgICAgICAgbGVnbmFtZXMgPSBjKCJObyBjb2ZhY3RvciIsIkNvZmFjdG9yIiksDQogICAgICAgICAgICAgbWFpbiA9ICJRVEwgZGV0ZWN0aW9uIHdpdGggYW5kIHdpdGhvdXQgY29mYWN0b3IiKQ0KYGBgDQoNCiMjIFBlcm11dGF0aW9uIHRocmVzaG9sZA0KDQpJbiBvcmRlciB0byBjYWxjdWxhdGUgYSBzaWduaWZpY2FuY2UgdGhyZXNob2xkLCBhIHBlcm11dGF0aW9uIHRlc3QgY2FuIGJlIHBlcmZvcm1lZCB3aGljaCBhbGxvd3MgdXMgdG8gZmluZCB3aGF0IGlzIHRoZSDigJxhdmVyYWdl4oCdIGxldmVsIG9mIHNpZ25pZmljYW5jZSBpbiB0aGlzIGRhdGFzZXQgYnkgcGVybXV0aW5nIHBoZW5vdHlwZXMgYW5kIGdlbm90eXBlcy4gVG8gb2J0YWluIHRoZSB0aHJlc2hvbGQgdGhlIFFUTCBtYXBwaW5nIHByb2Nlc3MgaXMgcmVwZWF0ZWQgbXVsdGlwbGUgdGltZXMsIHdoaWNoIG1heSByZXF1aXJlIGEgbG9uZyBjb21wdXRhdGlvbiB0aW1lLiBGb3Igb3VyIGV4YW1wbGUgd2Ugd2lsbCB1c2Ugb25seSAxMCBwZXJtdXRhdGlvbnMsIGJ1dCBnZW5lcmFsbHksIHRoZSBtb3JlIHBlcm11dGF0aW9ucyB0aGUgaGlnaGVyIHRoZSBhY2N1cmFjeSAoZS5nLiAxMDAwIC0gMTAwMDApLg0KDQpUaGVyZSBhcmUgdGhyZWUgcGFyYW1ldGVycyBmb3IgdGhyZXNob2xkIGNhbGN1bGF0aW9uOg0KDQoqIGBucGVybWA6IGludGVnZXIsIHRoZSBudW1iZXIgb2YgcGVybXV0YXRpb25zLg0KKiBgcGVybXV0YXRpb25gOiBlaXRoZXIg4oCccG9w4oCdIG9yIOKAnGZhbeKAnSwgZGV0ZXJtaW5lcyB0aGUgcGVybXV0YXRpb24gc3RyYXRlZ3kuIElmIOKAnHBvcOKAnSwgaXQgcGVybXV0ZXMgb3ZlciB0aGUgd2hvbGUgcG9wdWxhdGlvbiwgYW5kIGlmIOKAnGZhbeKAnSwgb25seSB3aXRoaW4gZmFtaWxpZXMuIElmIHRoZXJlIGlzIGEgc3Ryb25nIGZhbWlseSBzdHJ1Y3R1cmUsIHRoZSDigJxmYW3igJ0gc3RyYXRlZ3kgd2lsbCBwcm9kdWNlIG1vcmUgYWNjdXJhdGUgdGhyZXNob2xkcy4gSG93ZXZlciwgaW4gYSBwb3B1bGF0aW9uIGNvbnNpc3Rpbmcgb2YgbWFueSBzbWFsbCBmYW1pbGllcyAoPCAzMCBzaWJzKSB0aGUg4oCccG9w4oCdIGFwcHJvYWNoIGlzIHN0aWxsIHByZWZlcmFibGUuDQoqIGBmYW1gOiBhIHZlY3RvciBvZiBmYW1pbHkgbWVtYmVyc2hpcCAob3IgcG9wdWxhdGlvbiBzdHJ1Y3R1cmUgbWVtYmVyc2hpcCksIHRvIGJlIHByb3ZpZGVkIGlmIGBwZXJtdXRhdGlvbiA9IOKAnGZhbeKAnWAuDQoqIGBhbHBoYWA6IG51bWJlciBiZXR3ZWVuIDAgYW5kIDEsIGluZGljYXRpbmcgZm9yIHdoaWNoIHByb2JhYmlsaXR5IHNob3VsZCB0aGUgdGhyZXNob2xkIGJlIGNhbGN1bGF0ZWQuIERlZmF1bHQgaXMgMC4wNSAoaS5lLiBhIG1heGltdW0gb2YgNSUgb2YgZmFsc2UgcG9zaXRpdmVzKS4NCg0KRm9yIGVhY2ggcGhlbm90eXBlIGEgbmV3IG91dHB1dCBjYW4gYmUgZm91bmQgbmFtZWQgYHBlcm0udGhyZXNoYA0KDQpgYGB7cn0NCnJlc3VsdF9wZXJtIDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IG1wc25wZG9zZSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIFEgPSBULA0KICAgICAgICBwZXJtdXRhdGlvbiA9ICJwb3AiLA0KICAgICAgICBucGVybSA9IDEwLA0KICAgICAgICBhbHBoYSA9IDAuMDUpDQoNCnB2IDwtIC1sb2cxMChyZXN1bHRfcGVybSRwaGVub3R5cGUxJHB2YWwpDQp0aHIgPC0gcmVzdWx0X3Blcm0kcGhlbm90eXBlMSRwZXJtLnRocg0KDQpza3lwbG90KHB2LG1wbWFwLHRocmVzaG9sZCA9IHRocixtYWluID0gIlFUTCBkZXRlY3Rpb24gd2l0aCBwZXJtdXRhdGlvbiB0ZXN0IikNCmBgYA0KDQojIyMgTGkgJiBKaSB0aHJlc2hvbGQNCg0KQWx0ZXJuYXRpdmVseSwgd2UgY2FuIGNvbXB1dGUgdGhlIG51bWJlciBvZiBleHBlY3RlZCBpbmRlcGVuZGVudCB0ZXN0cyBhcyBwcm9wb3NlZCBieSBbTGkgJiBKaSAyMDA1XShodHRwczovL2RvaS5vcmcvMTAuMTAzOC9zai5oZHkuNjgwMDcxNyAiQWRqdXN0aW5nIG11bHRpcGxlIHRlc3RpbmcgaW4gbXVsdGlsb2N1cyBhbmFseXNlcyB1c2luZyB0aGUgZWlnZW52YWx1ZXMgb2YgYSBjb3JyZWxhdGlvbiBtYXRyaXgiKS4NCg0KVGhlIGVmZmVjdGl2ZSBudW1iZXIgb2YgaW5kZXBlbmRlbnQgdGVzdCBpcyBleHBlY3RlZCB0byBiZSBsb3dlciB0aGFuIHRoZSB0b3RhbCBudW1iZXIgb2YgdGVzdHMgdXNlZCBpbiB0aGUgQm9uZmVycm9uaSBhZGp1c3RtZW50LiBGb3IgdGhpcyByZWFzb24sIHRoZSBMaSAmIEppIHRocmVzaG9sZCBpcyB1c3VhbGx5IGxlc3Mgc3RyaWN0IHRoYW4gdGhlIEJvbmZlcnJvbmkgdGhyZXNob2xkLiBOb3RpY2UgdGhhdCB0aGlzIHRocmVzaG9sZCBkZXBlbmRzIG9uIGdlbm90eXBlcyBvbmx5LCB0aGVyZWZvcmUgdGhlIHNhbWUgdGhyZXNob2xkIGNhbiBiZSB1c2VkIGZvciBRVEwgYW5hbHlzZXMgb2YgZGlmZmVyZW50IHRyYWl0cy4NCg0KYGBge3J9DQoNCnJlc3VsdF9saWppIDwtIHRoci5MaUppKG0gPSBtcHNucGRvc2UsDQogICAgICAgICAgICAgICAgICAgICAgICBjaHJvbSA9IG1wbWFwJGNocm9tb3NvbWUsDQogICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUsDQogICAgICAgICAgICAgICAgICAgICAgICBwbG9pZHkgPSA0KQ0KDQpyZXN1bHRfbGlqaQ0KLWxvZzEwKHJlc3VsdF9saWppJHRocmVzaG9sZCkNCmBgYA0KDQojIyBJbXB1dGF0aW9uIG9mIG1pc3NpbmcgdmFsdWVzDQoNCkluIGdlbmVyYWwsIGZldyBtaXNzaW5nIHZhbHVlcyBzaG91bGQgYmUgaW5jbHVkZWQgYm90aCBpbiBwaGVub3R5cGVzIGFuZCBnZW5vdHlwZXMuIFRoZSBmdW5jdGlvbnMsIGhvd2V2ZXIsIGFyZSB3ZWxsIGFkYXB0ZWQgdG8gaGFuZGxlIG1pc3NpbmcgdmFsdWVzLiBQYXJ0IG9mIHRoaXMgYWRhcHRpb24gaXMgdGhlIHBvc3NpYmlsaXR5IG9mIGltcHV0aW5nIGdlbm90eXBlcyB1c2luZyBhIGstbmVhcmVzdCBuZWlnaGJvdXJzIChrbm4pIGFwcHJvYWNoLiBUaGF0IGlzLCBmb3IgZWFjaCBtaXNzaW5nIHZhbHVlLCB0aGUgJGskIG1vc3QgY29ycmVsYXRlZCBpbmRpdmlkdWFscyBhdCB0aGF0IGdlbmV0aWMgcmVnaW9uIGFyZSB0YWtlbiwgYW5kIGEgY29uc2Vuc3VzIGdlbm90eXBlIGlzIG9idGFpbmVkIHRvIGZpbGwgaW4gdGhlIG1pc3NpbmcgZGF0YS4gSWYgbm90aGluZyBpcyBzcGVjaWZpZWQsIGBtYXAuUVRMKClgIHdpbGwgdHJ5IHRvIGltcHV0ZSB0aGUgbWlzc2luZyBnZW5vdHlwZXMsIGFsdGhvdWdoIGluIHNvbWUgY2FzZXMgaXQgbWlnaHQgZGVjaWRlIGl0IGRvZXMgbm90IGhhdmUgZW5vdWdoIGluZm9ybWF0aW9uIHRvIGFjY3VyYXRlbHkgaW1wdXRlIGEgZ2Vub3R5cGUuDQoNClRvIGNvbnRyb2wgdGhlIGJlaGF2aW91ciBvZiB0aGUgaW1wdXRhdG9yIHVzZToNCg0KKiBgaW1wdXRlYDogbG9naWNhbCwgc2hvdWxkIG1pc3NpbmcgZ2Vub3R5cGVzIGJlIGltcHV0ZWQ/DQoqIGBrYDogaW50ZWdlciwgaG93IG1hbnkgbmVpZ2hib3VycyBzaG91bGQgYmUgdXNlZCBmb3IgaW1wdXRhdGlvbj8gRGVmYXVsdHMgdG8gMjAuDQoNCmBgYHtyfQ0Kc25wX05BIDwtIG1wc25wZG9zZQ0Kc25wX05BW3NhbXBsZSgxOmxlbmd0aChtcHNucGRvc2UpLDIwMDApXSA8LSBOQQ0KcmVzdWx0X05BIDwtIG1hcC5RVEwocGhlbm90eXBlcyA9IG1wcGhlbm8sDQogICAgICAgIGdlbm90eXBlcyA9IHNucF9OQSwNCiAgICAgICAgcGxvaWR5ID0gNCwNCiAgICAgICAgbWFwID0gbXBtYXAsDQogICAgICAgIFEgPSBUKQ0KDQpwdiA8LSAtbG9nMTAocmVzdWx0X05BJHBoZW5vdHlwZTEkcHZhbCkNCnNreXBsb3QocHYsbXBtYXAsbWFpbiA9ICJRVEwgZGV0ZWN0aW9uIHdpdGggbWlzc2luZyBnZW5vdHlwZXMiLCANCiAgICAgICAgdGhyZXNob2xkID0gcmVzdWx0X3Blcm0kcGhlbm90eXBlMSRwZXJtLnRocikNCg0KYGBgDQoNCiMjIEFkdmFuY2VkIHBhcmFtZXRlcnMNClNvbWUgZXh0cmEgcGFyYW1ldGVycyBhbGxvdyB0byBtb2RpZnkgdGhlIGJlaGF2aW91ciBvZiBjZXJ0YWluIHBhcnRzIG9mIGBtYXAuUVRMKClgLiBUaGVzZSBwYXJhbWV0ZXJzIGluY2x1ZGU6DQoNCiogYG5vX2NvcmVzYDogaW50ZWdlciBzcGVjaWZ5aW5nIHRoZSBudW1iZXIgb2YgY29yZXMgdG8gdXNlIGZvciBwYXJhbGxlbCBjb21wdXRpbmcuIERlZmF1bHRzIHRvIG51bWJlciBvZiBjb3JlcyBpbiB0aGUgbWFjaGluZSAtIDEuIElmIHJ1bm5pbmcgb24gYSBzZXJ2ZXIgb3IgY29tcHV0ZXIgY2x1c3RlciwgdGhpcyBwYXJhbWV0ZXIgc2hvdWxkIGJlIHNwZWNpZmllZCB0byBhdm9pZCBhc2tpbmcgZm9yIHRvbyBtYW55IGNvcmVzLg0KKiBgbGluZWFyYDogbG9naWNhbCwgc3BlY2lmeWluZyB3aGV0aGVyIGEgbGluZWFyIG1vZGVsIHNob3VsZCBiZSB1c2VkLiBJZiBGIGEgbWl4ZWQgbW9kZWwgaXMgdXNlZC4gVGhpcyBhcmd1bWVudCB3aWxsIG92ZXJydWxlIHRoZSBhdXRvbWF0aWMgYmVoYXZpb3VyIG9mIG1hcC5RVEwoKSwgc28gaXQgaXMgdXNlZnVsIHRvIGZvcmNlIGxpbmVhciBtb2RlbCBiZWhhdmlvdXIgd2hlbiBhIGN1c3RvbSBLDQogbWF0cml4IHdhbnRzIHRvIGJlIHByb3ZpZGVkIChmb3IgaW1wdXRhdGlvbiwgb3IgUXBjbyBkZWZpbml0aW9uKS4NCiogYGFwcHJveGltYXRlYDogYnkgZGVmYXVsdCwgdGhlIG1peGVkIG1vZGVsIHNvbHV0aW9uIGlzIGFjaGlldmVkIHVzaW5nIGFuIGFsZ29yaXRoIHByZXNlbnRlZCBwYXJhbGVsbHkgYXMgUDNEIG9yIEVNTUFYLCB3aGljaCBlc3NlbnRpYWxseSBhcHByb3hpbWF0ZXMgcGFydCBvZiB0aGUgY2FsY3VsYXRpb24gYmV0d2VlbiBtYXJrZXJzIHdoaWNoIGlzIHZlcnkgc2ltaWxhciwgYnV0IG5vdCBpZGVudGljYWwuIEJ5IGRvaW5nIHRoYXQsIHRoZSBzcGVlZCBvZiBjb21wdXRhdGlvbiBpcyBncmVhdGx5IGluY3JlYXNlZCwgYXQgYSBwcmFjdGljYWxseSBudWxsIGNvc3Qgb2YgYWNjdXJhY3kuIElmIHRoaXMgYXBwcm94aW1hdGlvbiBuZWVkcyB0byBiZSBkZWFjdGl2YXRlZCwgb25lIGNhbiBzZXQgYXBwcm94aW1hdGUgPSBGLg0KKiBgS19pZGVudGl0eWA6IGxvZ2ljYWwuIElmIFRydWUsIGl0IGZvcmNlcyB0byB1c2UgYSBtaXhlZCBtb2RlbCB0aGF0IGRvZXMgbm90IGNvcnJlY3QgZm9yIGdlbmV0aWMgc3RydWN0dXJlLCBidXQgd2hlcmUgdGhlIEsgbWF0cml4IGlzIHVzZWQgZm9yIG90aGVyIHB1cnBvc2VzIChOQSBpbXB1dGF0aW9uLCBRcGNv4oCmKS4NCg0KIyBWaXN1YWxpemF0aW9uDQoNCkxldCB1cyBub3cgZGlzY3VzcyB0aGUgdmlzdWFsaXphdGlvbiBmdW5jdGlvbnMgaW5jbHVkZWQgaW4gYG1wUVRMYCBhcyB3ZWxsIGFzIHRoZWlyIGludGVycHJldGF0aW9ucy4gVGhlIHZpc3VhbGl6YXRpb25zIHByb3ZpZGVkIGluY2x1ZGU6DQoNCiogUHJpbmNpcGFsIENvb3JkaW5hdGUgQW5hbHlzaXMgKFBDb0EpIHBsb3RzDQoqIFF1YW50aWxlLXF1YW50aWxlIHBsb3RzIChRUS1wbG90cykNCiogTWFuaGF0dGFuIHBsb3RzICh3ZSBjYWxsIHRoZW0gc2t5bGluZSBwbG90cykNCiogUGhlbm90eXBlIGJveHBsb3RzIGJhc2VkIG9uIFNOUCBvciBoYXBsb3R5cGUgZG9zYWdlDQoNCldlIGNhbiBkaXN0aW5ndWlzaCBiZXR3ZW4gdGhvc2UgdmlzdWFsaXphdGlvbnMgdXNlZnVsIGJlZm9yZSB0aGUgUVRMIGFuYWx5c2lzICgxKSwgdGhvc2UgdXNlZnVsIHRvIHVuZGVyc3RhbmQgdGhlIFFUTCBhbmFseXNpcyAoMiBhbmQgMyksIGFuZCBmaW5hbGx5IHRob3NlIHRoYXQgaGVscCB1cyBkaXNzZWN0IHRoZSByZXN1bHRzIG9mIHRoZSBRVEwgYW5hbHlzaXMgKDQpLg0KDQojIyBQcmluY2lwYWwgQ29vcmRpbmF0ZSBBbmFseXNpcw0KDQpXZSBoYXZlIGluY2x1ZGVkIGEgZnVuY3Rpb24sIHBjb2EucGxvdCgpIHRoYXQgY2FsY3VsYXRlcyB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudHMgYW5kIHZhcmlhbmNlIHBlcmNlbnRhZ2VzICh1c2luZyBS4oCZcyBvd24gcHJjb21wKCkgZnVuY3Rpb24pIGFuZCB1c2VzIHRoZW0gdG8gcGxvdCB3aGF0ZXZlciBjb21wb25lbnRzIGFyZSBkZXNpcmVkICgxIGFuZCAyIGJ5IGRlZmF1bHQpLiBJdCBhbHNvIGluY2x1ZGVzIGEgbGl0dGxlIGZ1bmN0aW9uIHRvIGFkZCBjb2xvdXIgdG8gdGhlc2UgcGxvdHMgdG8gaGVscCBkaXNzZWN0IHRoZSBzdHJ1Y3R1cmUuIFRoZSBhcmd1bWVudHMgb2YgcGNvYS5wbG90KCkgaW5jbHVkZToNCg0KKiBgS2A6IGFueSBkaXN0YW5jZS9zaW1pbGFyaXR5IG1hdHJpeC4NCiogYGNvbXBgOiBudW1lcmljIHZlY3RvciBvZiBsZW5ndGggMi4gVGhlIHByaW5jaXBsZSBjb21wb25lbnRzIHRvIGJlIHVzZWQgZm9yIHBsb3R0aW5nLiBBIFBDb0EgZ2VuZXJhdGVzIGFzIG1hbnkgY29tcG9uZW50cyBhcyBkaW1lbnNpb25zIGhhZCB0aGUgb3JpZ2luYWwgZGlzdGFuY2UgbWF0cml4LiBUaGV5IGFyZSBvcmRlcmVkIGJ5IHRoZWlyIGV4cGxhbmF0b3J5IHZhbHVlIChjb21wb25lbnRzIDEgYW5kIDIgd2lsbCBhbHdheXMgYmUgdGhlIG1vc3QgZXhwbGFuYXRvcnkpLiBUaGUgYW1vdW50IG9mIGV4cGxhaW5lZCB2YXJpYW5jZSBvZiBlYWNoIGlzIGFsd2F5cyBwcmVzZW50IGluIHRoZSB4bGFiIGFuZCB5bGFiIG9mIHRoZSBwbG90Lg0KKiBgcGxvdF9sZWdlbmRgOiBsb2dpY2FsLCB3aGV0aGVyIHRoZSBsZWdlbmQgc2hvdWxkIGJlIHBsb3R0ZWQuDQoqIGBsZWdzcGFjZWA6IG51bWVyaWMsIGluZGljYXRpbmcgdGhlIGFtb3VudCBvZiBzcGFjZSB0byBsZWF2ZSBmb3IgdGhlIGxlZ2VuZC4gSW4gY2FzZSBhIGdyb3VwaW5nIGlzIHByb3ZpZGVkLCBhIGxlZ2VuZCB3aWxsIGJlIHBsb3R0ZWQgdG8gdGhlIHJpZ2h0IG9mIHRoZSBwbG90LiBJbiBvcmRlciBub3QgdG8gb3ZlcmxhcCB3aXRoIHRoZSBwbG90LCBhZGRpdGlvbmFsIHNwYWNlIGlzIGFkZGVkIHRvIHRoZSByaWdodCBvZiB0aGUgcGxvdC4gVGhlIHBhcmFtZXRlciBsZWdzcGFjZSBjb250cm9scyB0aGUgYW1vdW50IG9mIHNwYWNlIGxlZnQgYXMgYSBwcm9wb3J0aW9uIHRvIHRoZSByYW5nZSBvZiB4IHZhbHVlcy4gQnkgZGVmYXVsdCwgaXQgdGFrZXMgdGhlIHZhbHVlIG9mIDAuMSAoMTAlIG9mIGV4dHJhIHNwYWNlKS4NCiogYGxlZ25hbWVgOiB0aGUgbGVnZW5kIG5hbWVzIGZvciB0aGUgY29sb3VycyBjaG9zZW4sIGF1dG9tYXRpY2FsbHkgdGFrZW4gZnJvbSBjb2wgaWYgbm90IHNwZWNpZmllZC4NCg0KRm9yIFBDb0EgcGxvdHMsIHRoZSBtb3N0IHVzZWZ1bCBpcyB0byBiZSBhYmxlIHRvIG1hcCBjYXRlZ29yaWVzIG9udG8gdGhlIHBsb3QsIHRvIGFzc2VzcyB3aGV0aGVyIHRoZSBnZW5ldGljIHN0cnVjdHVyZSBjb3JyZWxhdGVzIHdpdGggdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aG9zZSBjYXRlZ29yaWVzLiBUbyBhY2hpZXZlIHRoYXQgZnVuY3Rpb25hbGl0eSwgdHdvIGNvbW1vbiBwbG90dGluZyBwYXJhbWV0ZXJzIGhhdmUgYmVlbiBtb2RpZmllZDoNCg0KKiBgY29sYDogaW5zdGVhZCBvZiBleHBlY3RpbmcgYSBjb2xvdXIgZGVmaW5pdGlvbiwgY29sIGV4cGVjdHMgYSB2ZWN0b3Igb2YgdmFsdWVzIChlaXRoZXIgY2F0ZWdvcmljYWwgb3IgbnVtZXJpY2FsKS4gSXQgd2lsbCB0aGVuIGFzc2lnbiBhIGNvbG91ciB0byBlYWNoIHVuaXF1ZSB2YWx1ZSBhbmQgYXBwbHkgaXQgdG8gdGhlIFBDb0EgcGxvdCAoZS5nLiBjKOKAnHBvcDHigJ0s4oCccG9wMeKAnSzigJxwb3Ay4oCdLOKAnHBvcDLigJ0s4oCccG9wM+KAnSkgd2lsbCBnZW5lcmF0ZSB0aHJlZSBjb2xvdXIgY2F0ZWdvcmllcykuIFdpdGggY29udGludW91cyB2YWx1ZXMsIHRoaXMgc3lzdGVtIHdpbGwgbm90IHByb2R1Y2UgaWRlYWwgcmVzdWx0cyBpZiBub3QgYWxsIHZhbHVlcyBpbiBhIHNlcmllcyBhcmUgcHJlc2VudCAoaS5lLiB3b3JrcyB3ZWxsIGZvciBjb250aWd1b3VzIHZhbHVlcyBsaWtlIDEsMiwzLDQsNTsgZG9lcyBub3Qgd29yayB3ZWxsIGZvciBub24tY29udGlndW91cyB2YWx1ZXMgbGlrZSAxLDUsMTAsMTEsNDYpLg0KKiBgcGNoYDogdGhpcyBwYXJhbWV0ZXIgaXMgdXNlZCBtb3N0bHkgYXMgdGhlIGNsYXNzaWNhbCBwY2ggcGFyYW1ldGVyLCBidXQgaWYgbXVsdGlwbGUgcGNoIHZhbHVlcyBhcmUgcHJvdmlkZWQsIHRoZSBmaXJzdCB3aWxsIGJlIGFwcGxpZWQgdG8gdGhlIGZpcnN0IGNhdGVnb3J5LCB0aGUgc2Vjb25kIHRvIHRoZSBzZWNvbmQgY2F0ZWdvcnksIGV0Yy4gVGhpcyBtaWdodCBiZSBoZWxwZnVsIHdoZW4gdGhlcmUgYXJlIG1hbnkgY2F0ZWdvcmllcyBhbmQgb25seSBjb2xvdXIgZGlmZmVyZW5jZXMgbWlnaHQgbm90IGJlIGVub3VnaC4NCg0KTGV0IHVzIHNlZSBob3cgdGhpcyBjb2xvdXIgYW5kIHBvaW50IHR5cGUgbWFwcGluZyB3b3JrLg0KDQpgYGB7cn0NCiNjYWxjdWxhdGUgdGhlIHNpbWlsYXJpdHkgbWF0cml4IEsNCksgPC0gY2FsYy5LKHQobXBzbnBkb3NlKSkNCg0KcG9wIDwtIHN1YnN0cihjb2xuYW1lcyhLKSwxLDIpDQpsYXlvdXQobWF0cml4KDE6NCxieXJvdz1ULG5jb2w9MikpDQpwYXIobWFyPWMoNCw0LDMsMSkpDQpwY29hLnBsb3QoSyxtYWluID0gIkRlZmF1bHQgUENvQSBwbG90IiwgaCA9IGMoMCwzNjApKQ0KDQpwY29hLnBsb3QoSyxjb2wgPSBwb3AsDQogICAgICAgICAgcGNoPTE5LCBtYWluID0gIkNvbG91ciBtYXBwaW5nIiwgaCA9IGMoMCwzNjApKQ0KDQpwY29hLnBsb3QoSyxjb2wgPSBwb3AscGNoID0gYygxNSwxNywxOSksDQogICAgICAgICAgbWFpbiA9ICJEaWZmZXJlbnQgcG9pbnQgdHlwZXMiLCBoID0gYygwLDM2MCkpDQoNCiNJbiB0aGlzIGNhc2UgdGhlIGxlZ2VuZCBkb2VzIG5vdCB3b3JrIHByb3Blcmx5LCBpdCdzIGJldHRlciB0byB0dXJuIGl0IG9mZg0KcGNvYS5wbG90KEssY29sID0gMTpucm93KEspLCBwbG90X2xlZ2VuZCA9IEYsDQogICAgICAgICAgcGNoPTE5LCBtYWluID0iIE9uZSBjb2xvdXIgcGVyIGluZGl2aWR1YWwiLCBoID0gYygwLDM2MCkpDQpgYGANCg0KIyMgUXVhbnRpbGUtUXVhbnRpbGUgcGxvdA0KDQpRUS1wbG90cyBhcmUgdXNlZnVsIGZvciBkZXRlcm1pbmluZyB3aGV0aGVyIHRoZSBwLXZhbHVlIGRpc3RyaWJ1dGlvbiBvZiBhIFFUTCBhbmFseXNpcyAoeSBheGlzKSBmb2xsb3dzIHRoZSBleHBlY3RlZCBkaXN0cmlidXRpb24gKHggYXhpcykuIEluIHRoaXMgY2FzZSwgdGhlIGV4cGVjdGVkIGRpc3RyaWJ1dGlvbiBjb3JyZXNwb25kcyB0byBhIG51bGwgaHlwb3RoZXNpcyB3aGVyZSB0aGVyZSBhcmUgbm8gdHJ1bHkgc2lnbmlmaWNhbnQgbWFya2VycyAodGhlcmUgYXJlIG9ubHkgYSBmZXcgZmFsc2UgcG9zaXRpdmVzKS4gVGhpcyBleHBlY3RhdGlvbiBjYW4gYmUgc2VlbiBpbiB0aGUgcGxvdCB3aXRoIGEgcmVkIGRpYWdvbmFsIGxpbmUuDQoNClRoZSBmdW5jdGlvbiBgUVEucGxvdCgpYCBjYW4gYmUgdXNlZCB0byBnZW5lcmF0ZSBRUS1wbG90cyBvZiBvbmUgb3IgbXVsdGlwbGUgdmVjdG9ycyBvZiBwLXZhbHVlcy4gRWFjaCB2ZWN0b3Igb2YgcC12YWx1ZXMgY2FuIGJlIGdpdmVuIGFzIGEgY29sdW1uIGluIGEgbWF0cml4IG9yIGFzIGFuIGVsZW1lbnQgb2YgYSBsaXN0LiBUaGUgbnVtYmVyIG9mIHAtdmFsdWVzIGluIGVhY2ggdmVjdG9yIGRvZXMgbm90IG1hdHRlci4gQSBzaW5nbGUgc2V0IG9mIHAtdmFsdWVzIGNhbiBhbHNvIGJlIHByb3ZpZGVkLg0KDQpgYGB7cn0NCiNUaGUgcC12YWx1ZXMgYXJlIGZyb20gNTAwIHJhbmRvbSBub3JtYWwgdmFsdWVzDQpub3Rfc2lnIDwtIHBub3JtKHJub3JtKDUwMCksbG93ZXIudGFpbCA9IEYpDQoNCiNUaGUgcC12YWx1ZXMgYXJlIGZyb20gc29tZSByYW5kb20gbm9ybWFsIHZhbHVlcyBhbmQgc29tZSBub24tcmFuZG9tDQpzb21lX3NpZyA8LSBwbm9ybShjKHJub3JtKDQ1MCkscm5vcm0oNTAsbWVhbiA9IDMpKSxsb3dlci50YWlsID0gRikNCg0KI1RoZSBwLXZhbHVlcyBhcmUgYWxsIHRvbyBzaWduaWZpY2FudA0KaGlnaF9zaWcgPC0gcG5vcm0ocm5vcm0oNTAwLG1lYW49MyksbG93ZXIudGFpbCA9IEYpDQoNCiNUaGUgcC12YWx1ZXMgYXJlIGFsbCB0b28gbm9uLXNpZ25pZmljYW50DQpsb3dfc2lnIDwtIHBub3JtKHJub3JtKDUwMCksc2QgPSAzLGxvd2VyLnRhaWwgPSBGKQ0KDQoNCmV4YW1wbGVzIDwtIGxpc3Qobm90X3NpZyxzb21lX3NpZyxoaWdoX3NpZyxsb3dfc2lnKQ0KUVEucGxvdChleGFtcGxlcyxtYWluPSJFeGFtcGxlIFFRLXBsb3QiLA0KICAgICAgICBsZWduYW1lcyA9IGMoIk5vdCBzaWduaWZpY2FudCIsDQogICAgICAgICAgICAgICAgICAgICAiR29vZCBzaWduaWZpY2FudCIsDQogICAgICAgICAgICAgICAgICAgICAiT3ZlcmVzdGltYXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAiVW5kZXJlc3RpbWF0ZWQiKSkNCmBgYA0KDQpBIGxlZ2VuZCBpcyBhdXRvbWF0aWNhbGx5IGFkZGVkIHdoZW4gbXVsdGlwbGUgcHZhbHVlIHNldHMgYXJlIHByb3ZpZGVkLiBUaGVyZSBhcmUgc29tZSBwYXJhbWV0ZXJzIHJlbGF0ZWQgd2l0aCB0aGUgbGVnZW5kOiANCg0KKiBgcGxvdF9sZWdlbmRgOiBsb2dpY2FsLCBjYW4gYmUgdXNlZCB0byBhdm9pZCB0aGUgZ2VuZXJhdGlvbiBvZiB0aGUgbGVnZW5kLiANCiogYGxlZ25hbWVzYDogYSB2ZWN0b3IgY2FuIGJlIHByb3ZpZGVkIGZvciB0aGUgbGVnZW5kIG5hbWVzLiANCiogYGxlZ3NwYWNlYDogbnVtZXJpYywgYSBudW1iZXIgaW5kaWNhdGluZyBob3cgbXVjaCBleHRyYSBzcGFjZSAoaW4gcHJvcG9ydGlvbikgbXVzdCBiZSBhZGRlZCB0byB0aGUgbGVmdCBvZiB0aGUgcGxvdC4gQ2FuIGJlIHVzZWZ1bCB0byB0d2VhayBpdCB3aGVuIGxlZ2VuZCBuYW1lcyBhcmUgdG9vIGxvbmcgYW5kIHRoZXkgb3ZlcmxhcCB3aXRoIHBvaW50cyBvbiB0aGUgUVEtcGxvdC4NCg0KYGBge3J9DQpwdmFscyA8LSBsaXN0KGxpbl9kb3NhZ2UgPSByZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCwNCiAgICAgICAgICAgICAgbGluX2hhcCA9IHJlc3VsdF9oYXAkcGhlbm90eXBlMSRwdmFsLA0KICAgICAgICAgICAgICBsaW5fcXBjbyA9IHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCwNCiAgICAgICAgICAgICAgbWl4ID0gcmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwsDQogICAgICAgICAgICAgIGxpbl9RX05BID0gcmVzdWx0X05BJHBoZW5vdHlwZTEkcHZhbCkNCg0KUVEucGxvdChwdmFscywgbWFpbiA9ICJNb2RlbCBjb21wYXJpc29uIGZvciBwLXZhbHVlcyBvZiBwaGVubzEiLGxlZ3NwYWNlID0gMC4yMikNCmBgYA0KYGBge3J9DQojVGhlIG5vbi1jb3JyZWN0ZWQgbGluZWFyIG1vZGVscyBoYXZlIGdyb3NzbHkgb3ZlcmVzdGltYXRlZCBwLXZhbHVlcw0KI0JldHRlciB0YWtlIHRoZW0gb3V0IHRvIGNvbXBhcmUgdGhlIG90aGVyIG1vZGVscyBtb3JlIGNsZWFybHkNClFRLnBsb3QocHZhbHNbLTE6LTJdLCBtYWluID0gIk1vZGVsIGNvbXBhcmlzb24gZm9yIHB2YWx1ZXMgb2YgcGhlbm8xIikNCmBgYA0KV2UgY2xlYXJseSBzZWUgaG93IHRoZSBsYWNrIG9mIHN0cnVjdHVyZSBjb3JyZWN0aW9uIGluIHRoZSBsaW5lYXIgbW9kZWxzIGhhcyBjYXVzZWQgYSBncmVhdCBpbmZsYXRpb24gb2YgdGhlIHAtdmFsdWVzLg0KDQojIyBTa3lsaW5lIHBsb3QNCg0KUHJvYmFibHkgdGhlIG1vc3QgcmVsZXZhbnQgcGxvdCBmb3IgUVRMIG1hcHBpbmcgaXMgdGhlIOKAnE1hbmhhdHRhbiBwbG904oCdLCBuYW1lZCBhcyBzdWNoIGR1ZSB0byBpdHMgc3RydWN0dXJlLCB3aGljaCBjYW4gcmVtaW5kIG9mIHRoZSBza3lsaW5lIG9mIG9mIHRoZSBmYW1vdXMgc2t5c2NyYXBlciBkaXN0cmljdCBvZiBOZXcgWW9yay4gSW4gdGhpcyBwYWNrYWdlLCB3ZSBoYXZlIG9wdGVkIHRvIG5hbWUgdGhlIGZ1bmN0aW9uIGBza3lwbG90KClgLCBmb2xsb3dpbmcgdGhlIHZpc3VhbCBtZXRhcGhvci4NCg0KQSBNYW5oYXR0YW4gcGxvdCBpcyBhIGZvcm0gb2YgbWFya2VyIHNpZ25pZmljYW5jZSB2aXN1YWxpemF0aW9uIHRoYXQgaGVscHMgdXMgcmVjb2duaXNlIHJlZ2lvbnMgd2hlcmUgbXVsdGlwbGUgc2lnbmlmaWNhbnQgbWFya2VycyBjby1sb2NhdGUgYXJvdW5kIGEgcmVnaW9uLCBkZWZpbmluZyBhIFFUTCBsb2NhdGlvbi4gT24gdGhlIHgtYXhpcyB0aGUgbWFya2Vy4oCZcyBwb3NpdGlvbiBpbiB0aGUgZ2Vub21lIChwaHlzaWNhbCBvciBnZW5ldGljKSBhbmQgb24gdGhlIHktYXhpcywgdGhlIG1hcmtlcuKAmXMgc2lnbmlmaWNhbmNlLiBHZW5lcmFsbHksIHNpZ25pZmljYW5jZSBpcyBleHByZXNzZWQgYXMgJOKIkmxvZzEwKHB2YWwpJCwgYnV0IGFueSBvdGhlciBzY29yZSB3aWxsIGRvLCBhcyBsb25nIGFzIGl0IGZvbGxvd3MgdGhhdCBtb3JlIHNpZ25pZmljYW50IG1hcmtlcnMgaGF2ZSBoaWdoZXIgc2NvcmVzLCBhbmQgbGVzcyBzaWduaWZpY2FudCBtYXJrZXJzIGhhdmUgbG93ZXIgc2NvcmVzIChmb3IgaW5zdGFuY2UgYSBXYWxkIHNjb3JlIG9yIEYtdGVzdCkuDQoNClRvIGdlbmVyYXRlIHRoZSB2aXN1YWxpemF0aW9uIG9uZSBtdXN0IHByb3ZpZGUgYm90aCBwLXZhbHVlcyBhbmQgYSBnZW5ldGljL3BoeXNpY2FsIG1hcC4gVGhlIGBza3lwbG90KClgIGZ1bmN0aW9uIGhhcyB0aGUgZm9sbG93aW5nIGFyZ3VtZW50czoNCg0KKiBgcHZhbGA6IHZlY3RvciBvZiBwdmFsdWVzLiBJbXBvcnRhbnRseSwgdGhlIGZ1bmN0aW9uIC1sb2cxMCgpIG11c3QgYmUgcGVyZm9ybWVkIGJ5IHRoZSB1c2VyLiBUaGlzIGhhcyBiZWVuIGRvbmUgb24gcHVycG9zZSwgYXMgd2UgbWlnaHQgd2FudCB0byBwbG90IG90aGVyIGtpbmQgb2Ygc2NvcmUgdmFsdWVzIHdpdGggdGhpcyBmdW5jdGlvbiB0aGF0IGRvIG5vdCByZXF1aXJlIHRoZSAtbG9nMTAgdHJhbnNmb3JtYXRpb24uDQoqIGBtYXBgOiBhIGdlbmV0aWMgbWFwIGRhdGEuZnJhbWUgd2l0aCBhdCBsZWFzdCBjb2x1bW5zIOKAnHBvc2l0aW9u4oCdIGFuZCDigJxjaHJvbW9zb21l4oCdLg0KKiBgdGhyZXNob2xkYDogbnVtZXJpYywgb3B0aW9uYWwgdmFsdWUgdG8gZHJhdyBhIHRocmVzaG9sZCBsaW5lLg0KKiBgY2hyb21gOiBudW1lcmljIG9yIGNoYXJhY3RlciB2ZWN0b3IuIElmIHByb3ZpZGVkLCBpdCBpcyB1c2VkIHRvIHNlbGVjdCB0aGUgcC12YWx1ZXMgYmFzZWQgb24gdGhlIOKAnGNocm9tb3NvbWXigJ0gY29sdW1uIG9mIG1hcC4NCiogYHNtYWxsYDogbG9naWNhbCwgc2hvdWxkIHRoZSBjTSBzY2FsZSBiZSBkcmF3bj8gQnkgZGVmYXVsdCBpdCB3aWxsIG9ubHkgYmUgZHJhd24gaWYgb25seSB0d28gb3IgYSBzaW5nbGUgY2hyb21vc29tZSBhcmUgcGxvdHRlZCwgb3RoZXJ3aXNlIGl0IGlzIHRvbyBjcm93ZGVkIGFuZCBiYXJlbHkgbGVnaWJsZS4NCi4uLjogb3RoZXIgcGFyYW1ldGVycyBjYW4gYmUgcGFzc2VkIHRvIHRoZSBwbG90KCkgZnVuY3Rpb24uIFRoZSBtb3N0IHJlbGV2YW50IGlzIHByb2JhYmx5IG1haW4gZm9yIHRoZSBwbG90IHRpdGxlLg0KDQpgYGB7cn0NCnB2MSA8LSAtbG9nMTAocmVzdWx0X21peCRwaGVub3R5cGUxJHB2YWwpDQojSnVzdCBhIHNreWxpbmUgcGxvdA0Kc2t5cGxvdChwdjEsbWFwID0gbXBtYXAsbWFpbj0iRXhhbXBsZSBTa3lsaW5lIHBsb3QiKQ0KDQojV2Ugd2FudCB0byBmb2N1cyBvbiBjaHJvbW9zb21lIDINCnNreXBsb3QocHYxLG1wbWFwLGNocm9tID0gMixtYWluID0iU2t5bGluZSBsb3Qgb2YgY2hyb21vc29tZSAyIix0aHJlc2hvbGQgPSAzLjk1KQ0KDQojVGhlICJjaHJvbW9zb21lcyIgY2FuIGFsc28gYmUgZXhwcmVzc2VkIGFzIGNoYXJhY3RlcnMNCm1hcF9sZXR0ZXJzIDwtIG1wbWFwDQptYXBfbGV0dGVycyRjaHJvbW9zb21lIDwtIExFVFRFUlNbbWFwX2xldHRlcnMkY2hyb21vc29tZSsxXQ0KDQpza3lwbG90KHB2MSxtYXBfbGV0dGVycywNCiAgICAgICAgbWFpbj0iU2t5bGluZSBwbG90IHdoZXJlIGNocm9tb3NvbWVzIGFyZSBjaGFyYWN0ZXJzIiwNCiAgICAgICAgY2hyb20gPSBjKCJDIiwiRSIpLHNtYWxsID0gVCx0aHJlc2hvbGQgPSAzLjk1KQ0KYGBgDQoNCiMjIyBDb21wYXJhdGl2ZSBza3lsaW5lIHBsb3QNCg0KRm9yIG91ciByZXNlYXJjaCwgd2Ugd2FudGVkIHRvIGNvbXBhcmUgdGhlIHNreWxpbmUgcGxvdHMgb2YgbXVsdGlwbGUgbW9kZWxzLCBuYW1lbHkgdGhlIGhhcGxvdHlwZS1iYXNlZCBhbmQgZG9zYWdlLWJhc2VkIG1vZGVscy4gRm9yIHRoYXQgcmVhc29uLCBhbm90aGVyIHNreWxpbmUgcGxvdCBmdW5jdGlvbiB3YXMgZGV2ZWxvcGVkOiBgY29tcC5za3Bsb3QoKWAuIFRoZSBmdW5jdGlvbiBpcyB1c2VkIGluIGEgdmVyeSBzaW1pbGFyIGZhc2hpb24sIHdpdGggc29tZSBkaWZmZXJlbmNlczoNCg0KKiBgcHZhbGBgOiBlYWNoIHAtdmFsdWUgdmVjdG9yIG11c3QgYmUgcHJvdmlkZWQgYXMgYSBjb2x1bW4gaW4gYSBtYXRyaXggb3IgYSB2ZWN0b3IgaW4gYSBsaXN0Lg0KKiBtYXA6IGEgbWFwIG11c3QgYmUgcHJvdmlkZWQgZm9yIGVhY2ggc2V0IG9mIHAtdmFsdWVzLiBJZiBhIHNpbmdsZSBtYXAgaXMgcHJvdmlkZWQsIGl0IHdpbGwgYmUgYXNzdW1lZCB0aGF0IGFsbCBwLXZhbHVlIHZlY3RvcnMgY29ycmVzcG9uZCB0byB0aGUgc2FtZSBtYXAuIEZvciB0aGUgbW9tZW50LCB3ZSBoYXZlIG5vdCB0ZXN0ZWQgd2hhdCBoYXBwZW5zIGlmIGVhY2ggbWFwIGhhcyBhIGRpZmZlcmVudCBzZXQgb2YgY2hyb21vc29tZXMgKGkuZS4gbWFwMSBoYXMgY2hyb21vc29tZXMgMSwgMiBhbmQgMyBhbmQgbWFwMiBoYXMgY2hyb21vc29tZXMgMSwgMiBhbmQgNCksIHRoYXQgbWlnaHQgY2F1c2UgY29uZmxpY3RzLg0KKiBgY2hyb21gOiBzaW1pbGFybHksIHdlIGhhdmUgbm90IHRlc3RlZCB3aGF0IHdvdWxkIGhhcHBlbiBpZiBjaHJvbW9zb21lcyBhcmUgc2VsZWN0ZWQgdGhhdCBhcmUgIHByZXNlbnQgb25seSBpbiBvbmUgb2YgdGhlIHR3byBtYXBzLg0KKiBgbGVnbmFtZXNgOiBhIHZlY3RvciBvZiBuYW1lcyBmb3IgdGhlIGxlZ2VuZCBlbGVtZW50cy4gSWYgbm90IHByb3ZpZGVkLCBpdCB3aWxsIGJlIHJlYWQgZnJvbSB0aGUgcHZhbHVlIGxpc3QsIGFuZCBpZiB0aGUgbGlzdCBoYXMgbm8gbmFtZXMsIGl0IHdpbGwgYmUgc2ltcGx5IGxhYmVsbGVkIHB2YWwgMSwgcHZhbCAyLCBldGMuDQoqIGBsZWdzcGFjZWA6IG51bWVyaWMsIHRoZSBwcm9wb3J0aW9uIG9mIHNwYWNlIHRvIGFkZCB0byB0aGUgbGVmdCBvZiB0aGUgcGxvdCBmb3IgdGhlIGxlZ2VuZC4gSXQgaXMgdXNlZnVsIHdoZW4gdGhlIGxlZ2VuZCBpcyB0b28gY2xvc2UgdG8gcG9pbnRzLCBhbmQgZGVmYXVsdHMgdG8gMC4xLg0KKiBgcGNoYDogdGhlIHVzdWFsIG51bWVyaWMgcGNoIGZvciBwbG90KCksIGJ1dCBlYWNoIHZhbHVlIGdpdmVuIHdpbGwgYmUgYXNzaWduZWQgdG8gZWFjaCBzZXQgb2YgcC12YWx1ZXMuIENhbiBoZWxwIGRpc3Rpbmd1aXNoIHBvaW50cyB3aGVuIHRoZXJlIGFyZSBtYW55IGNvbG91cnMuIFdpbGwgYmUgcmVjeWNsZWQgaWYgbm90IGVub3VnaCBwY2ggdmFsdWVzIGFyZSBwcm92aWRlZC4NCiogYGFscGhhYDogbnVtZXJpYyBiZXR3ZWVuIDAgKHRyYW5zcGFyZW50KSBhbmQgMSAob3BhcXVlKS4gQnkgZGVmYXVsdCwgdGhlIHBvaW50cyBhcmUgcGxvdHRlZCB3aXRoIHNvbWUgdHJhbnNwYXJlbmN5IHRvIGJlIGFibGUgdG8gc2VlIHRoZSBtdWx0aXBsZSBkaXN0cmlidXRpb25zLCBjaGFuZ2UgdGhpcyBwYXJhbWV0ZXIgdG8gbWFrZSBpdCBtb3JlL2xlc3MgdHJhbnNwYXJlbnQuDQoNCmBgYHtyfQ0KcHYyIDwtIC1sb2cxMChyZXN1bHRfZG9zJHBoZW5vdHlwZTEkcHZhbCkNCmNvbXAuc2t5cGxvdChsaXN0KHB2MSxwdjIpLG1wbWFwLA0KICAgICAgICAgICAgIG1haW4gPSAiQ29tcGFyaXNvbiBvZiB0d28gcC12YWx1ZSBkaXN0cmlidXRpb25zIiwNCiAgICAgICAgICAgICB0aHJlc2hvbGQgPSAzLjk1LHBjaCA9IGMoMTksMTcpKQ0KYGBgDQoNCldlIGNhbiBhbHNvIHVzZSBpdCB0byBjb21wYXJlIHRoZSByZXN1bHRzIHdlIGhhdmUgYmVlbiBnZW5lcmF0aW5nLg0KDQpgYGB7cn0NCiNSZW1lbWJlciB3ZSBtdXN0IGFwcGx5IHRoZSAtbG9nMTAgdHJhbnNmb3JtYXRpb24NCnB2YWxzIDwtIGxhcHBseShwdmFscyxmdW5jdGlvbihpKSAtbG9nMTAoaSkpDQpjb21wLnNreXBsb3QocHZhbHMsbWFwID0gbXBtYXAsDQogICAgICAgICAgICAgbGVnc3BhY2UgPSAwLjIyKQ0KYGBgDQpgYGB7cn0NCiNBZ2FpbiB0aGUgbm9uLWNvcnJlY3RlZCBtb2RlbHMgYXJlIHRvbyBvdXRseWluZw0KY29tcC5za3lwbG90KHB2YWxzWy0xOi0yXSwNCiAgICAgICAgICAgICBtYXA9IG1wbWFwLA0KICAgICAgICAgICAgIHBjaCA9IGMoMTUsMTcsMTkpLGxlZ3NwYWNlID0gMC4yMiwNCiAgICAgICAgICAgICBtYWluPSAiQ29tcGFyaXNvbiBvZiBtb2RlbHMgb24gZXhhbXBsZSBkYXRhIikNCmBgYA0KDQojIyBQaGVub3R5cGUgYm94cGxvdA0KDQpJdCBpcyB1c2VmdWwgdG8gY29ycmVsYXRlIHRoZSBkb3NhZ2Ugb2YgYSBzaW5nbGUgbWFya2VyIHdpdGggdGhlIHZhbHVlIG9mIGEgcGhlbm90eXBlLCBhcyBzb21ldGltZXMgdGhhdCBjYW4gcmV2ZWFsIOKAnGRvc2FnZSBlZmZlY3Rz4oCdLiBJbiBvcmRlciB0byBzaW1wbGlmeSB0aGUgcHJvY2VzcyBvZiBnZW5lcmF0aW5nIHN1Y2ggYm94cGxvdHMsIHdlIGhhdmUgY3JlYXRlZCBhIHdyYXBwZXIgdGhhdCBtYWtlcyB0aGVtIHVzaW5nIGRvc2FnZXMgb3IgaGFwbG90eXBlcy4NCg0KVGhlcmUgaXMgYSBzaW5nbGUgZnVuY3Rpb24sIGBwaGVub19ib3goKWAgdGhhdCBjYW4gdXNlIHR3byBkaWZmZXJlbnQgbWV0aG9kcywgZWl0aGVyIGZvciBkb3NhZ2VzIG9yIGZvciBoYXBsb3R5cGVzLCBieSBjaGFuZ2luZyB0aGUgcGFyYW1ldGVyIGhhcGxvdHlwZSAoRmFsc2UgYnkgZGVmYXVsdCkuIFNvbWUgcGFyYW1ldGVycyBiZWhhdmUgaWRlbnRpY2FsbHkgbm8gbWF0dGVyIHdoYXQgdGhlIHZhbHVlIG9mIGhhcGxvdHlwZSBpczoNCg0KKiBgcGhlYDogaXMgYSBudW1lcmljYWwgdmVjdG9yIG9mIHBoZW5vdHlwZXMNCiogYGdlbmA6IGlmIGhhcGxvdHlwZSA9IEYsIGdlbiBzaG91bGQgYmUgYSBudW1lcmljIHZlY3RvciBvZiBkb3NhZ2VzIHdpdGggYSBzaW5nbGUgb2JzZXJ2YXRpb24gcGVyIGluZGl2aWR1YWwuIElmIGhhcGxvdHlwZSA9IFQsIGdlbiBzaG91bGQgYmUgYSB2ZWN0b3Igd2l0aCBwDQogbnVtZXJpYy9jaGFyYWN0ZXIgb2JzZXJ2YXRpb25zIHBlciBpbmRpdmlkdWFsIHdoZXJlIHANCiBpcyB0aGUgcGxvaWR5LCBhbmQgZWFjaCBvYnNlcnZhdGlvbiBpcyBhIGhhcGxvdHlwZSBjbGFzcyAoZS5nLiAxMjAsIDE0MCwgMTI44oCmO0EsIEIsIEPigKY7IGhhcDEsIGhhcDIsIGhhcDPigKYpLg0KKiBgZHJhdy5wb2ludHNgOiBpcyBhIGxvZ2ljYWwgdGhhdCBpbmRpY2F0ZXMgd2hldGhlciBwb2ludHMgc2hvdWxkIGJlIGRyYXduLg0KKiBgLi4uYDogZnVydGhlciBhcmd1bWVudHMgdG8gYmUgcGFzc2VkIHRvIHBsb3QoKQ0KDQpXaGVuIGhhcGxvdHlwZSA9IFQgb3RoZXIgcGFyYW1ldGVycyBjYW4gYmUgdXNlZDogDQoNCiogYHBsb2lkeWA6IGlzIGFuIGludGVnZXIgaW5kaWNhdGluZyB0aGUgcGxvaWR5LiANCiogYGhhcC5zZWxlY3RgOiBpcyBhIHZlY3RvciBvZiBudW1lcmljL2NoYXJhY3RlciBpbmRpY2F0aW5nIHdoaWNoIGhhcGxvdHlwZXMgc2hvdWxkIGJlIHBsb3R0ZWQuDQoNCmBgYHtyfQ0KI1dlIGNob29zZSB0aGUgbW9zdCBzaWduaWZpY2FudCBtYXJrZXIgaW4gb3VyIFFUTCBhbmFseXNpcw0KYmVzdF9zbnAgPC0gd2hpY2gubWluKHJlc3VsdF9taXgkcGhlbm90eXBlMSRwdmFsKQ0KYmVzdF9oYXAgPC0gd2hpY2gubWluKHJlc3VsdF9xcGNvJHBoZW5vdHlwZTEkcHZhbCkNCmdlbl9zbnAgPC0gdW5saXN0KG1wc25wZG9zZVtiZXN0X3NucCxdKQ0KZ2VuX2hhcCA8LSB1bmxpc3QobXBoYXBkb3NlW2Jlc3RfaGFwLF0pDQoNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5fc25wLA0KICAgICAgICAgIHhsYWI9IkRvc2FnZSIseWxhYj0icGhlbm90eXBlIixtYWluPSJBIGJveHBsb3Qgb2YgZG9zYWdlcyIpDQoNCiNCdXQgbm93IHRoZXJlIGFyZSB0b28gbWFueSB0aGluZ3MgcGxvdHRlZCBhbmQgSSBjYW4ndCBzZWUgYW55dGhpbmcNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCwNCiAgICAgICAgICB4bGFiPSJIYXBsb3R5cGVzIix5bGFiPSJwaGVub3R5cGUiLG1haW49IkEgYm94cGxvdCBvZiBoYXBsb3R5cGUgZG9zYWdlcyIpDQoNCiNUaGlzIGlzIGJldHRlciBidXQgc3RpbGwgdG9vIG1hbnkgYm94ZXMNCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCxkcmF3LnBvaW50cyA9IEYsDQogICAgICAgICAgeGxhYj0iSGFwbG90eXBlcyIseWxhYj0icGhlbm90eXBlIixtYWluPSJBIGJveHBsb3Qgb2YgaGFwbG90eXBlIGRvc2FnZXMgKG5vIHBvaW50cykiKQ0KDQojVGhpcyBpcyBiZXR0ZXINCnBoZW5vX2JveChtcHBoZW5vWywxXSxnZW5faGFwLGhhcGxvdHlwZSA9IFQscGxvaWR5ID0gNCwgDQogICAgICAgICAgaGFwLnNlbGVjdCA9IGMoIjU3IiwiNDYiLCAiMzciLCI2OSIpLA0KICAgICAgICAgIHhsYWI9IkhhcGxvdHlwZXMiLHlsYWI9InBoZW5vdHlwZSIsDQogICAgICAgICAgbWFpbj0iQSBib3hwbG90IG9mIHNvbWUgaGFwbG90eXBlIGRvc2FnZXMiKQ0KYGBgDQoNCiMjIENvbG91ciBjaG9pY2UNCg0KVGhlIGNvbG91ciBzeXN0ZW0gb2YgdGhlIHZpc3VhbGl6YXRpb24gZnVuY3Rpb25zIGluIHRoZSBtcFFUTCBwYWNrYWdlIGlzIGEgYml0IGRpZmZlcmVudCB0aGFuIHRoZSBkZWZhdWx0IG1ldGhvZHMgdGhhdCBtb3N0IFIgcGxvdHMgaW5jbHVkZS4gVG8gcGVyZm9ybSBjb2xvdXIgY2hvaWNlIHdlIHVzZSB0aGUgcGFja2FnZSBjb2xvcnNwYWNlLCB3aGljaCB1c2VzIHRoZSBIQ0wgKGh1ZSwgY29sb3VyIHRvbmU7IGNocm9tYSwgY29sb3VyIGludGVuc2l0eTsgYW5kIGx1bWluYW5jZSwgY29sb3VyIGxpZ2h0bmVzcykgc3lzdGVtIHRvIGRlZmluZSBjb2xvdXIuIFRoaXMgcGFja2FnZSBoYXMgYmVlbiBkZXNpZ25lZCB3aXRoIGRhdGEgdmlzdWFsaXphdGlvbiBpbiBtaW5kIGFuZCBvZmZlcnMgZ3JlYXQgZnVuY3Rpb25hbGl0aWVzIGZvciBpbnRlbGxpZ2VudCBhbmQgZWZmZWN0aXZlIGNvbG91ciBjaG9pY2UuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCwgSSBoaWdobHkgcmVjb21tZW5kIHZpc2l0aW5nIHRoZWlyIFt3ZWIgcGFnZV0oaHR0cDovL2NvbG9yc3BhY2Uuci1mb3JnZS5yLXByb2plY3Qub3JnLyAiQSBUb29sYm94IGZvciBNYW5pcHVsYXRpbmcgYW5kIEFzc2Vzc2luZyBDb2xvcnMgYW5kIFBhbGV0dGVzIiksIHdoZXJlIHRoZXkgZXhwbGFpbiB0aGUgcGFja2FnZSBhbmQgbWFueSBpbXBvcnRhbnQgY29uY2VwdHMgb2YgY29sb3VyIHRoZW9yeSBhbmQgZGVzaWduLg0KDQpJbiBlc3NlbmNlLCB3ZSBjYW4gY2hvb3NlIGJldHdlZW4gKipxdWFsaXRhdGl2ZSoqLCAqKnNlcXVlbnRpYWwqKiBvciAqKmRpdmVyZ2VudCoqIGNvbG9yIHBhbGV0dGVzLiBTZXR0aW5nIGBjb2x0eXBlYCBpbiBtb3N0IGZ1bmN0aW9ucyB0byBhbnkgb2YgdGhlc2UgdGhyZWUgc3lzdGVtcyB3aWxsIGNoYW5nZSB0aGUgdHlwZSBvZiBwYWxldHRlIHVzZWQuIA0KDQpBIHF1YWxpdGF0aXZlIHBhbGV0dGUgaGFzIGRpZmZlcmVudCBjb2xvdXJzIChodWVzKSwgYW5kIGlzIHVzZWZ1bCBmb3IgKipjYXRlZ29yaWNhbCoqIHZhcmlhYmxlcy4NCg0KQSBzZXF1ZW50aWFsIHBhbGV0dGUgaGFzIHNldmVyYWwgdG9uZXMgb2YgdGhlIHNhbWUgY29sb3IgKGx1bWluYW5jZXMpLCBhbmQgaXMgdXNlZnVsIGZvciAqKm51bWVyaWNhbCoqIHZhcmlhYmxlcy4NCg0KQSBkaXZlcmdlbnQgcGFsZXR0ZSBoYXMgdHdvIG9wcG9zaW5nIGNvbG9ycyBhbmQgYW4gaW4tYmV0d2VlbiBuZXV0cmFsIGNvbG9yLiBGb3IgZXhhbXBsZSByZWQsIHdoaXRlIGFuZCBibHVlLiBUaGVzZSBwYWxldHRlcyBhcmUgdXNlZnVsIGZvciAqKm51bWVyaWNhbCoqIHZhcmlhYmxlcyB0aGF0IGhhdmUgZWl0aGVyIHZlcnkgbG93IG9yIHZlcnkgaGlnaCB2YWx1ZXMuDQoNCkluIGFsbCBwbG90dGluZyBmdW5jdGlvbnMgeW91IGNhbiB1c2UgdGhlIHBhcmFtZXRlcnM6DQoNCiogYGNvbHR5cGVgOiBzdGFuZGluZyBmb3IgY29sb3VyIHBhbGV0dGUgdHlwZSwgd2UgY2FuIGNob29zZSBiZXR3ZWVuIOKAnHNlcXVlbnRpYWzigJ0sIOKAnHF1YWxpdGF0aXZl4oCdLCDigJxkaXZlcmdlbnTigJ0gb3Ig4oCccmFpbmJvd+KAnS4NCiogYGhgOiBvbmUgb3IgdHdvIG51bWVyaWNhbCB2YWx1ZXMsIHN0YW5kaW5nIGZvciBodWUuIFRoZSB2YWx1ZXMgYXJlIGRlZ3JlZXMgd2l0aGluIHRoZSBjb2xvdXIgd2hlZWwsIGFuZCB0aHVzIHZhbHVlcyBiZXR3ZWVuIDAgYW5kIDM2MCBhcmUgcmVjb21tZW5kZWQsIHdoZXJlIDAgYW5kIDM2MCBhcmUgdGhlIHNhbWUuIEZvciBhIGNvbG91ciByZWZlcmVuY2UgeW91IGNhbiB1c2UgYGh1ZV93aGVlbCgpYCB3aGljaCBwcm9kdWNlcyB0aGUgcGxvdCBiZWxvdy4NCiogYGxgOiBvbmUgKGZvciBxdWFsaXRhdGl2ZSkgb3IgdHdvIChmb3Igc2VxdWVudGlhbCkgbnVtZXJpY2FsIHZhbHVlcy4gVGhpcyBjb250cm9scyB0aGUgbGlnaHRuZXNzIG9mIHRoZSBjb2xvdXJzLCBieSBkZWZhdWx0IHNldCB0byA2MC4gVmFsdWVzIHNob3VsZCBiZSBiZXR3ZWVuIDIwIGFuZCAxMDAsIGJlbG93IG9yIGFib3ZlIHRoZSByZXN1bHRzIHdpbGwgYmUgdW5leHBlY3RlZC4NCg0KYGBge3IsIG91dC5oZWlnaHQ9NTAwLCBvdXQud2lkdGg9ODAwfQ0KcGFyKG1mcm93ID0gYygyLDIpLA0KICAgIG1hciA9IGMoMSwwLDMsMCkpDQpmb3IobCBpbiBjKDQwLDYwLDgwLDEwMCkpew0KICBodWVfd2hlZWwobCA9IGwpDQp9DQpgYGANCg0K