1 Independent filtering

Independent filtering, where genes/transcripts/proteins are filtered out prior to statistical analysis, is a common practice in ‘omics experiments. Typically, lowly expressed features are filtered out, and one can argue that these features’ expression is too low to be deemed biologically relevant. In addition, low-count features are also associated with a low statistical power for differential expression (remember relative uncertainty of counts, and edgeR’s BCV plot), and will increase the number of tests performed, and therefore lead to a more severe multiple testing correction.

Figure 1 from Bourgon *et al.* (2010).

Figure 1 from Bourgon et al. (2010).


Independent filtering has been formalized by Bourgon et al. (2010), and the concept can be summarized as follows.

  • For each feature we calculate two statistics, \(S_F\) and \(S_T\), respectively used for two stages: filtering and testing (e.g., differential expression).
  • In order for a feature to be deemed significant, both of its statistics must be greater than some cut-off.
  • We want to control the type I error rate of the second stage (testing). But note that the second stage is conditional on the first stage, as we only test features passing the filter, and basically ignore the fact that filtering was performed. Indeed, one criticism is that computing and correcting the \(p\)-values as if filtering had not been performed may lead to overoptimistic adjusted \(p\)-values.
  • Bourgon et al. (2010) show that filtering is only appropriate (i.e., does not inflate type I error rate) if the conditional null distribution of test statistics for features passing the filter is the same as the unconditional null distribution. Therefore, filtering is appropriate if the statistic used for filtering is independent of the statistic used for testing under the null hypothesis. A good filtering statistic, however, is also informative under the alternative hypothesis. Indeed, a filtering statistic that is independent of the test statistic under both the null- and alternative hypothesis will amount to a random filter, hence deteriorate the quality of the analysis.
Figure 2 from Bourgon *et al.* (2010).

Figure 2 from Bourgon et al. (2010).


Let’s try a couple of examples to get some intuition using simulated data.

suppressPackageStartupMessages(library(DESeq2))
set.seed(24)
dds <- DESeq2::makeExampleDESeqDataSet()
simCounts <- counts(dds)
group <- dds$condition

1.1 A dependent test statistic

## filter based on difference in means
filterStatEffectSize <- abs(rowMeans(simCounts[,group == "A"]) - rowMeans(simCounts[,group == "B"]))
## calculate t-test results for each gene
testStat <- genefilter::rowttests(simCounts, group)

## unconditional distribution of test statistics prior to filtering
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     main = "Unconditional distribution")

# filter out ~20% of genes with lowest effect sizes
mean(filterStatEffectSize > 1)
## [1] 0.792
hist(filterStatEffectSize, breaks=40)
abline(v=1, col="red")

keepEffectSize <- filterStatEffectSize > 1
## conditional distribution: very different!
plot(density(testStat$statistic[keepEffectSize], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution")

## in same plot
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     col = "orange",
     main = "Test statistics before and after filtering on mean difference",
     lwd = 2)
lines(density(testStat$statistic[keepEffectSize], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution",
     col = "steelblue",
     lwd = 2)
legend("topright", c("Unconditional", "Conditional"),
       col=c("orange", "steelblue"), lwd=2, bty='n')

1.2 An independent test statistic

filterStatGlobalMean <- rowMeans(simCounts)
# we remove a similar fraction of ~20% genes
mean(filterStatGlobalMean > 5) 
## [1] 0.771
keepGlobalMean <- filterStatGlobalMean > 5

## unconditional distribution
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     main = "Unconditional distribution")

## conditional distribution: the same.
plot(density(testStat$statistic[keepGlobalMean], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution")

## in same plot
plot(density(testStat$statistic, na.rm=TRUE),
     xlab = "Test statistic",
     col = "orange",
     main = "Test statistics before and after filtering on global mean",
     lwd = 2)
lines(density(testStat$statistic[keepGlobalMean], na.rm=TRUE),
     xlab = "Test statistic",
     main = "Conditional distribution",
     col = "steelblue",
     lwd = 2)
legend("topright", c("Unconditional", "Conditional"),
       col=c("orange", "steelblue"), lwd=2, bty='n')

2 Aliasing

Suppose we are working with the following experimental design on colon cancer. Studying the effect of a drug on gene expression, researchers gather RNA-seq data from four colon cancer patients and four healthy individuals. For each individual, they obtain RNA-seq data from a blood sample before as well as two weeks after taking a daily dose of the drug. The research question relates to differential expression after vs. before taking the drug, in particular whether this is different for the diseased versus healthy group (i.e., the interaction between time (before/after taking the drug) and disease status (healthy/colon cancer)).

In terms of the model matrix, we could imagine a design such as ~ patient + disease*time, where

  • disease is a binary indicator referring to colon cancer versus control sample.
  • time defines if the sample is taken before or after taking the drug.
  • patient defines the individual donor the sample comes from.

The research question could then amount to testing the disease * time interaction.

Let’s try this, by simulating random data for one gene.

set.seed(2)
# 2 samples per patient for 8 patients
patient <- factor(rep(letters[1:8], each=2)) 
# first four are healthy, next four are diseased
disease <- factor(c(rep("healthy",8), rep("cancer",8)), levels=c("healthy", "cancer")) 
# one before and one after sample for each
time <- factor(rep(c("before", "after"), 8), levels=c("before", "after")) 

table(patient, disease, time)
## , , time = before
## 
##        disease
## patient healthy cancer
##       a       1      0
##       b       1      0
##       c       1      0
##       d       1      0
##       e       0      1
##       f       0      1
##       g       0      1
##       h       0      1
## 
## , , time = after
## 
##        disease
## patient healthy cancer
##       a       1      0
##       b       1      0
##       c       1      0
##       d       1      0
##       e       0      1
##       f       0      1
##       g       0      1
##       h       0      1
## simulate data for one gene
n <- 16
y <- rpois(n = n, lambda = 50)

## fit a Poisson model
m <- glm(y ~ patient + disease*time,
         family = "poisson")
summary(m)
## 
## Call:
## glm(formula = y ~ patient + disease * time, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients: (1 not defined because of singularities)
##                         Estimate Std. Error z value Pr(>|z|)    
## (Intercept)              3.76900    0.11916  31.631   <2e-16 ***
## patientb                 0.06744    0.14999   0.450   0.6530    
## patientc                 0.06744    0.14999   0.450   0.6530    
## patientd                 0.27304    0.14310   1.908   0.0564 .  
## patiente                 0.16449    0.16224   1.014   0.3107    
## patientf                 0.02565    0.16644   0.154   0.8775    
## patientg                -0.01784    0.16785  -0.106   0.9154    
## patienth                 0.05706    0.16544   0.345   0.7302    
## diseasecancer                 NA         NA      NA       NA    
## timeafter               -0.01567    0.10220  -0.153   0.8782    
## diseasecancer:timeafter  0.12374    0.14407   0.859   0.3904    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 16.1200  on 15  degrees of freedom
## Residual deviance:  8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4

We find that one of the coefficients is NA! This is obviously not because we’re dealing with NA values in the data as we’ve just simulated the response variable ourselves. What’s going on?

One of the parameters, in this case the parameter distinguishing cancer from healthy patients cannot be estimated as it is a linear combination of other parameters. In our case, estimating the diseased effect would use information that is already used to estimate the patient-level intercepts. In other words, once you know the patient, you immediately also know the disease status, so estimating the diseased vs healthy effect on top of the patient effect provides no additional information if we have already estimated the patient-level effects. This concept is called aliasing, and is a common technical issue in ’omics experiments with complex experimental designs.


While to understand the origin of the aliasing it is crucial to understand the relationship between the variables in the experimental design, we can also investigate it in detail using the alias function, to give us an idea.

alias(m)
## Model :
## y ~ patient + disease * time
## 
## Complete :
##               (Intercept) patientb patientc patientd patiente patientf patientg
## diseasecancer 0           0        0        0        1        1        1       
##               patienth timeafter diseasecancer:timeafter
## diseasecancer 1        0         0

We see that the effect diseasecancer is a linear combination of the patient-specific effects of the cancer patients. This makes sense!


For clarity, let’s reproduce this using our design matrix.

X <- model.matrix(~ patient + disease*time) # this is the design used in glm()

## these are indeed identical.
X[,"diseasecancer"]
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 
##  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1
X[,"patiente"] + X[,"patientf"] + X[,"patientg"] + X[,"patienth"]
##  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 
##  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1

Since one of our parameters is a linear combination of other parameters, it cannot be estimated simultaneously with the other parameters. In this case, we can actually drop the disease main effect from the model, since we know that it is already included in the patient effect.


We will have to carefully construct our design matrix in order to account for all important sources of variation while still allowing us to answer the research question of interest. The aliasing exploration above has made it clear we may drop the disease main effect, so let’s start by constructing this design matrix.

X <- model.matrix(~ patient + time + disease:time)

m2 <- glm(y ~ -1 + X,
         family = "poisson")
summary(m2)
## 
## Call:
## glm(formula = y ~ -1 + X, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients: (1 not defined because of singularities)
##                           Estimate Std. Error z value Pr(>|z|)    
## X(Intercept)               3.76900    0.11916  31.631   <2e-16 ***
## Xpatientb                  0.06744    0.14999   0.450   0.6530    
## Xpatientc                  0.06744    0.14999   0.450   0.6530    
## Xpatientd                  0.27304    0.14310   1.908   0.0564 .  
## Xpatiente                  0.28823    0.16077   1.793   0.0730 .  
## Xpatientf                  0.14939    0.16500   0.905   0.3653    
## Xpatientg                  0.10590    0.16643   0.636   0.5246    
## Xpatienth                  0.18081    0.16400   1.102   0.2703    
## Xtimeafter                -0.01567    0.10220  -0.153   0.8782    
## Xtimebefore:diseasecancer -0.12374    0.14407  -0.859   0.3904    
## Xtimeafter:diseasecancer        NA         NA      NA       NA    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 4489.2752  on 16  degrees of freedom
## Residual deviance:    8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4
alias(m2)
## Model :
## y ~ -1 + X
## 
## Complete :
##                          X(Intercept) Xpatientb Xpatientc Xpatientd Xpatiente
## Xtimeafter:diseasecancer  0            0         0         0         1       
##                          Xpatientf Xpatientg Xpatienth Xtimeafter
## Xtimeafter:diseasecancer  1         1         1         0        
##                          Xtimebefore:diseasecancer
## Xtimeafter:diseasecancer -1

We are still confronted with aliasing as the model matrix contains an interaction effect timebefore:diseasecancer as well as timeafter:diseasecancer, while only the latter is relevant. Indeed, we know that we can derive the timebefore:diseasecancer effect by averaging the patient effects of the cancer patients.


X <- X[,!colnames(X) %in% "timebefore:diseasecancer"]


## fit a Poisson model
m2 <- glm(y ~ -1 + X,
         family = "poisson")
summary(m2)
## 
## Call:
## glm(formula = y ~ -1 + X, family = "poisson")
## 
## Deviance Residuals: 
##      Min        1Q    Median        3Q       Max  
## -1.52772  -0.43544   0.00013   0.44162   1.34650  
## 
## Coefficients:
##                          Estimate Std. Error z value Pr(>|z|)    
## X(Intercept)              3.76900    0.11916  31.631   <2e-16 ***
## Xpatientb                 0.06744    0.14999   0.450   0.6530    
## Xpatientc                 0.06744    0.14999   0.450   0.6530    
## Xpatientd                 0.27304    0.14310   1.908   0.0564 .  
## Xpatiente                 0.16449    0.16224   1.014   0.3107    
## Xpatientf                 0.02565    0.16644   0.154   0.8775    
## Xpatientg                -0.01784    0.16785  -0.106   0.9154    
## Xpatienth                 0.05706    0.16544   0.345   0.7302    
## Xtimeafter               -0.01567    0.10220  -0.153   0.8782    
## Xtimeafter:diseasecancer  0.12374    0.14407   0.859   0.3904    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 4489.2752  on 16  degrees of freedom
## Residual deviance:    8.8417  on  6  degrees of freedom
## AIC: 120.16
## 
## Number of Fisher Scoring iterations: 4

We see that all coefficients can now be estimated. The timeafter effect may be interpreted as the time effect for healthy patients, while the timeafter:diseasecancer effect may be interpreted as the difference in the time effect for cancer patients as compared to healthy patients, i.e., it is the relevant interaction effect we are interested in.

Question. Taking this further, suppose that we can safely assume that there is no interaction effect between disease status and time. How would you now test for differential expression between healthy and cancer patients at the first timepoint? Specify the experimental design and contrast used.

Answer.

Assuming no interaction, we can specify the design as follows:

XMain <- model.matrix(~ patient + time)
head(XMain)
##   (Intercept) patientb patientc patientd patiente patientf patientg patienth
## 1           1        0        0        0        0        0        0        0
## 2           1        0        0        0        0        0        0        0
## 3           1        1        0        0        0        0        0        0
## 4           1        1        0        0        0        0        0        0
## 5           1        0        1        0        0        0        0        0
## 6           1        0        1        0        0        0        0        0
##   timeafter
## 1         0
## 2         1
## 3         0
## 4         1
## 5         0
## 6         1
In order to set up the contrast testing for healthy versus diseased patients at the first timepoint, we need to take the average of the appropriate patient-level intercepts. The average expression for healthy patients is \[ \log \mu_{healthy} = \frac{1}{4} \left\{ \beta_0 + (\beta_0 + \beta_1) + (\beta_0 + \beta_2) + (\beta_0 + \beta_3) \right\}. \] Similar, for the diseased patients it equals \[ \log \mu_{diseased} = \frac{1}{4} \left\{ (\beta_0 + \beta_4) + (\beta_0 + \beta_5) + (\beta_0 + \beta_6) + (\beta_0 + \beta_7) \right\}. \] And thus the relevant contrast \[ \log \frac{\mu_{diseased}}{\mu_{healthy}} = \frac{1}{4} (\beta_4 + \beta_5 + \beta_6 + \beta_7 ) - \frac{1}{4} (\beta_1 + \beta_2 + \beta_3).\]

3 limma-voom as an alternative approach to modeling counts

limma is a powerful linear model based framework for modeling microarray gene expression data and inferring differential expression results, and has been introduced in the proteomics module of this course. With the inception of RNA-seq, the limma developers got creative and extended their framework to also model count data, hence creating limma-voom.

3.1 The limma framework for the analysis of microarrays

In the proteomics module, we have previously introduced the powerful linear model based framework limma, and how it uses an empirical Bayes strategy to borrow information across proteins to derive a posterior variance estimate. In its default implementation, limma cannot be used to model count data, as it can not account for their mean-variance relationship. The developers, however, came up with a creative approach to use the limma framework to model count data.

3.2 limma-voom: extending limma for RNA-seq data

  • Count models such as edgeR and DESeq2 automatically account for the mean-variance relationship of the data by assuming a proper count distribution, given that the observed mean-variance relationship is close to the one assumed by the distribution. However, they are also more complex, both computationally as well as statistically and conceptually.
  • limma-voom (Law et al. (2014)) is a method that unlocks Gaussian linear models to analyze count data in the context of RNA-seq, by first estimating the mean-variance relationship of the dataset at hand, and subsequently incorporating it in the analysis through observation-level weights in a linear regression model.
Figure 1 from Law *et al.* (2014).

Figure 1 from Law et al. (2014).


  • The mean-variance relationship is dataset-specific and needs to be estimated separately for each dataset.
  • The mean-variance trend is estimated nonparametrically across all genes, using a global mean and variance for each gene. Using this trend, observation-level variances are estimated for each individual observation.
  • These observation-level variances are then used as inverse weights in the linear modeling framework, to account for heteroscedasticity.
Figure 1 from Law *et al.* (2014).

Figure 1 from Law et al. (2014).

3.3 limma-voom analysis of parathyroid data

limma has an extensive user’s guide which can be accessed via limma::limmaUsersGuide().

library(limma)
## 
## Attaching package: 'limma'
## The following object is masked from 'package:DESeq2':
## 
##     plotMA
## The following object is masked from 'package:BiocGenerics':
## 
##     plotMA
library(edgeR)
se <- readRDS("data/seParathyroid.rds")
se
## class: SummarizedExperiment 
## dim: 63193 23 
## metadata(1): MIAME
## assays(1): counts
## rownames(63193): ENSG00000000003 ENSG00000000005 ... LRG_98 LRG_99
## rowData names(0):
## colnames: NULL
## colData names(8): run experiment ... study sample
design <- model.matrix(~treatment*time+patient, 
                       data=colData(se))

keep <- filterByExpr(se, design)
table(keep)
## keep
## FALSE  TRUE 
## 46629 16564
filtCounts <- assays(se)$counts[keep,]

dge <- DGEList(counts=filtCounts)

# normalize just as in edgeR
dge <- calcNormFactors(dge)
# fit the mean-variance trend used to calculate weights
v <- voom(dge, design, plot=TRUE)

# this mean-variance trend is then automatically incorporated into the usual limma pipeline
fit <- lmFit(v, design)
fit <- eBayes(fit)
tt <- topTable(fit, coef=(ncol(design-1):ncol(design)), number=nrow(dge)) # test interaction effects
head(tt) #similar as in edgeR, we find no DE

3.3.1 Testing contrasts using limma-voom

When working with limma-voom, we cannot immediately test a contrast using a contrast matrix just like we did in edgeR. Instead, we will reparametrize our model according to the contrasts, such that each parameter corresponds to a specific contrast, using the contrasts.fit function.

# contrast matrix we used before
L <- matrix(0, nrow = ncol(fit$coefficients), ncol = 7)
rownames(L) <- colnames(fit$coefficients)
colnames(L) <- c("DPNvsCON24", "DPNvsCON48",
                 "OHTvsCON24", "OHTvsCON48",
                 "DPNvsCONInt", "OHTvsCONInt",
                 "OHTvsDPNInt")
# DPN vs control at 24h
L[2,"DPNvsCON24"] <- 1
# DPN vs control at 48h
L[c(2,8),"DPNvsCON48"] <- 1
# OHT vs control at 24h
L[3,"OHTvsCON24"] <- 1
# OHT vs control at 48h
L[c(3,9),"OHTvsCON48"] <- 1
# DPN control interaction
L[8,"DPNvsCONInt"] <- 1
# OHT control interaction
L[9,"OHTvsCONInt"] <- 1
# OHT DPN interaction
L[c(9,8),"OHTvsDPNInt"] <- c(1, -1)
L
##                      DPNvsCON24 DPNvsCON48 OHTvsCON24 OHTvsCON48 DPNvsCONInt
## (Intercept)                   0          0          0          0           0
## treatmentDPN                  1          1          0          0           0
## treatmentOHT                  0          0          1          1           0
## time48h                       0          0          0          0           0
## patient2                      0          0          0          0           0
## patient3                      0          0          0          0           0
## patient4                      0          0          0          0           0
## treatmentDPN:time48h          0          1          0          0           1
## treatmentOHT:time48h          0          0          0          1           0
##                      OHTvsCONInt OHTvsDPNInt
## (Intercept)                    0           0
## treatmentDPN                   0           0
## treatmentOHT                   0           0
## time48h                        0           0
## patient2                       0           0
## patient3                       0           0
## patient4                       0           0
## treatmentDPN:time48h           0          -1
## treatmentOHT:time48h           1           1
fit <- lmFit(v, design)
fit2 <- contrasts.fit(fit, L)
fit2 <- eBayes(fit2)
head(fit2$coefficients) # one coefficient is one contrast
##                   DPNvsCON24   DPNvsCON48  OHTvsCON24   OHTvsCON48 DPNvsCONInt
## ENSG00000000003  0.160189232 -0.015153215  0.11818144 -0.053716201 -0.17534245
## ENSG00000000419 -0.081114061 -0.006840437 -0.05767507  0.116075037  0.07427362
## ENSG00000000457 -0.196673615 -0.092219304 -0.19581292 -0.007131573  0.10445431
## ENSG00000000460  0.101878901  0.346346109  0.16977808  0.407107755  0.24446721
## ENSG00000000938  0.001144654 -0.027973867 -0.23689618 -0.270319390 -0.02911852
## ENSG00000000971  0.558406693 -0.362972697  0.45660599 -0.594255351 -0.92137939
##                 OHTvsCONInt  OHTvsDPNInt
## ENSG00000000003 -0.17189764  0.003444803
## ENSG00000000419  0.17375011  0.099476483
## ENSG00000000457  0.18868134  0.084227034
## ENSG00000000460  0.23732967 -0.007137533
## ENSG00000000938 -0.03342321 -0.004304688
## ENSG00000000971 -1.05086134 -0.129481951
# loop over all contrasts of interest
ttList <- list()
for(cc in 1:ncol(L)){
  ttList[[cc]] <- topTable(fit2, coef=cc, number=nrow(dge))
}
names(ttList) <- colnames(L)
nDE <- unlist(lapply(ttList, function(x) sum(x$adj.P.Val <= 0.05)))
nDE
##  DPNvsCON24  DPNvsCON48  OHTvsCON24  OHTvsCON48 DPNvsCONInt OHTvsCONInt 
##           0           0           0           7           0           0 
## OHTvsDPNInt 
##           0
LS0tCnRpdGxlOiAnU2VxdWVuY2luZzogU2VsZWN0ZWQgdGVjaG5pY2FsIHRvcGljcycKYXV0aG9yOiAiS29lbiBWYW4gZGVuIEJlcmdlIgpkYXRlOiAiTGFzdCBjb21waWxlZCBvbiBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCLCAlWScpYCIKb3V0cHV0OiAKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3IgZnVuY3Rpb25zLCBpbmNsdWRlPUZBTFNFfQojIEEgZnVuY3Rpb24gZm9yIGNhcHRpb25pbmcgYW5kIHJlZmVyZW5jaW5nIGltYWdlcwpmaWcgPC0gbG9jYWwoewogICAgaSA8LSAwCiAgICByZWYgPC0gbGlzdCgpCiAgICBsaXN0KAogICAgICAgIGNhcD1mdW5jdGlvbihyZWZOYW1lLCB0ZXh0KSB7CiAgICAgICAgICAgIGkgPDwtIGkgKyAxCiAgICAgICAgICAgIHJlZltbcmVmTmFtZV1dIDw8LSBpCiAgICAgICAgICAgIHBhc3RlKCJGaWd1cmUgIiwgaSwgIjogIiwgdGV4dCwgc2VwPSIiKQogICAgICAgIH0sCiAgICAgICAgcmVmPWZ1bmN0aW9uKHJlZk5hbWUpIHsKICAgICAgICAgICAgcmVmW1tyZWZOYW1lXV0KICAgICAgICB9KQp9KQpgYGAgCgpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1UUlVFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoa25pdHIpCiAgbGlicmFyeShybWFya2Rvd24pCiAgbGlicmFyeShnZ3Bsb3QyKQp9KQpgYGAKCiMgSW5kZXBlbmRlbnQgZmlsdGVyaW5nCgpJbmRlcGVuZGVudCBmaWx0ZXJpbmcsIHdoZXJlIGdlbmVzL3RyYW5zY3JpcHRzL3Byb3RlaW5zIGFyZSBmaWx0ZXJlZCBvdXQgcHJpb3IgdG8gc3RhdGlzdGljYWwgYW5hbHlzaXMsIGlzIGEgY29tbW9uIHByYWN0aWNlIGluICdvbWljcyBleHBlcmltZW50cy4gVHlwaWNhbGx5LCBsb3dseSBleHByZXNzZWQgZmVhdHVyZXMgYXJlIGZpbHRlcmVkIG91dCwgYW5kIG9uZSBjYW4gYXJndWUgdGhhdCB0aGVzZSBmZWF0dXJlcycgZXhwcmVzc2lvbiBpcyB0b28gbG93IHRvIGJlIGRlZW1lZCBiaW9sb2dpY2FsbHkgcmVsZXZhbnQuIEluIGFkZGl0aW9uLCBsb3ctY291bnQgZmVhdHVyZXMgYXJlIGFsc28gYXNzb2NpYXRlZCB3aXRoIGEgbG93IHN0YXRpc3RpY2FsIHBvd2VyIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAocmVtZW1iZXIgcmVsYXRpdmUgdW5jZXJ0YWludHkgb2YgY291bnRzLCBhbmQgYGVkZ2VSYCdzIEJDViBwbG90KSwgYW5kIHdpbGwgaW5jcmVhc2UgdGhlIG51bWJlciBvZiB0ZXN0cyBwZXJmb3JtZWQsIGFuZCB0aGVyZWZvcmUgbGVhZCB0byBhIG1vcmUgc2V2ZXJlIG11bHRpcGxlIHRlc3RpbmcgY29ycmVjdGlvbi4KCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPXBhc3RlKCJGaWd1cmUgMSBmcm9tIEJvdXJnb24gKmV0IGFsLiogKDIwMTApLiIpfQojIEFsbCBkZWZhdWx0cwppbmNsdWRlX2dyYXBoaWNzKCIuL2ltYWdlc19zZXF1ZW5jaW5nL2luZGVwZW5kZW50RmlsdGVyaW5nLnBuZyIpCmBgYAoKIC0tLQoKSW5kZXBlbmRlbnQgZmlsdGVyaW5nIGhhcyBiZWVuIGZvcm1hbGl6ZWQgYnkgW0JvdXJnb24gKmV0IGFsLiogKDIwMTApXShodHRwczovL3d3dy5wbmFzLm9yZy9jb250ZW50LzEwNy8yMS85NTQ2KSwgYW5kIHRoZSBjb25jZXB0IGNhbiBiZSBzdW1tYXJpemVkIGFzIGZvbGxvd3MuCgogLSBGb3IgZWFjaCBmZWF0dXJlIHdlIGNhbGN1bGF0ZSB0d28gc3RhdGlzdGljcywgJFNfRiQgYW5kICRTX1QkLCByZXNwZWN0aXZlbHkgdXNlZCBmb3IgdHdvIHN0YWdlczogZmlsdGVyaW5nIGFuZCB0ZXN0aW5nIChlLmcuLCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbikuCiAtIEluIG9yZGVyIGZvciBhIGZlYXR1cmUgdG8gYmUgZGVlbWVkIHNpZ25pZmljYW50LCBib3RoIG9mIGl0cyBzdGF0aXN0aWNzIG11c3QgYmUgZ3JlYXRlciB0aGFuIHNvbWUgY3V0LW9mZi4KIC0gV2Ugd2FudCB0byBjb250cm9sIHRoZSB0eXBlIEkgZXJyb3IgcmF0ZSBvZiB0aGUgc2Vjb25kIHN0YWdlICh0ZXN0aW5nKS4gQnV0IG5vdGUgdGhhdCAqKnRoZSBzZWNvbmQgc3RhZ2UgaXMgY29uZGl0aW9uYWwgb24gdGhlIGZpcnN0IHN0YWdlKiosIGFzIHdlIG9ubHkgdGVzdCBmZWF0dXJlcyBwYXNzaW5nIHRoZSBmaWx0ZXIsIGFuZCBiYXNpY2FsbHkgaWdub3JlIHRoZSBmYWN0IHRoYXQgZmlsdGVyaW5nIHdhcyBwZXJmb3JtZWQuIEluZGVlZCwgb25lIGNyaXRpY2lzbSBpcyB0aGF0IGNvbXB1dGluZyBhbmQgY29ycmVjdGluZyB0aGUgJHAkLXZhbHVlcyBhcyBpZiBmaWx0ZXJpbmcgaGFkIG5vdCBiZWVuIHBlcmZvcm1lZCBtYXkgbGVhZCB0byBvdmVyb3B0aW1pc3RpYyBhZGp1c3RlZCAkcCQtdmFsdWVzLgogLSBbQm91cmdvbiAqZXQgYWwuKiAoMjAxMCldKGh0dHBzOi8vd3d3LnBuYXMub3JnL2NvbnRlbnQvMTA3LzIxLzk1NDYpIHNob3cgdGhhdCBmaWx0ZXJpbmcgaXMgb25seSBhcHByb3ByaWF0ZSAoaS5lLiwgZG9lcyBub3QgaW5mbGF0ZSB0eXBlIEkgZXJyb3IgcmF0ZSkgaWYgdGhlIGNvbmRpdGlvbmFsIG51bGwgZGlzdHJpYnV0aW9uIG9mIHRlc3Qgc3RhdGlzdGljcyBmb3IgZmVhdHVyZXMgcGFzc2luZyB0aGUgZmlsdGVyIGlzIHRoZSBzYW1lIGFzIHRoZSB1bmNvbmRpdGlvbmFsIG51bGwgZGlzdHJpYnV0aW9uLiBUaGVyZWZvcmUsICoqZmlsdGVyaW5nIGlzIGFwcHJvcHJpYXRlIGlmIHRoZSBzdGF0aXN0aWMgdXNlZCBmb3IgZmlsdGVyaW5nIGlzIGluZGVwZW5kZW50IG9mIHRoZSBzdGF0aXN0aWMgdXNlZCBmb3IgdGVzdGluZyB1bmRlciB0aGUgbnVsbCBoeXBvdGhlc2lzKiouIEEgZ29vZCBmaWx0ZXJpbmcgc3RhdGlzdGljLCBob3dldmVyLCBpcyBhbHNvIGluZm9ybWF0aXZlIHVuZGVyIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLiBJbmRlZWQsIGEgZmlsdGVyaW5nIHN0YXRpc3RpYyB0aGF0IGlzIGluZGVwZW5kZW50IG9mIHRoZSB0ZXN0IHN0YXRpc3RpYyB1bmRlciBib3RoIHRoZSBudWxsLSBhbmQgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB3aWxsIGFtb3VudCB0byBhIHJhbmRvbSBmaWx0ZXIsIGhlbmNlIGRldGVyaW9yYXRlIHRoZSBxdWFsaXR5IG9mIHRoZSBhbmFseXNpcy4KIApgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmNhcD1wYXN0ZSgiRmlndXJlIDIgZnJvbSBCb3VyZ29uICpldCBhbC4qICgyMDEwKS4iKX0KIyBBbGwgZGVmYXVsdHMKaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9pbmRlcGVuZGVudEZpbHRlcmluZzIucG5nIikKYGBgCiAgLS0tCiAgCkxldCdzIHRyeSBhIGNvdXBsZSBvZiBleGFtcGxlcyB0byBnZXQgc29tZSBpbnR1aXRpb24gdXNpbmcgc2ltdWxhdGVkIGRhdGEuCgpgYGB7cn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoREVTZXEyKSkKc2V0LnNlZWQoMjQpCmRkcyA8LSBERVNlcTI6Om1ha2VFeGFtcGxlREVTZXFEYXRhU2V0KCkKc2ltQ291bnRzIDwtIGNvdW50cyhkZHMpCmdyb3VwIDwtIGRkcyRjb25kaXRpb24KYGBgCgojIyBBIGRlcGVuZGVudCB0ZXN0IHN0YXRpc3RpYwoKYGBge3J9CiMjIGZpbHRlciBiYXNlZCBvbiBkaWZmZXJlbmNlIGluIG1lYW5zCmZpbHRlclN0YXRFZmZlY3RTaXplIDwtIGFicyhyb3dNZWFucyhzaW1Db3VudHNbLGdyb3VwID09ICJBIl0pIC0gcm93TWVhbnMoc2ltQ291bnRzWyxncm91cCA9PSAiQiJdKSkKIyMgY2FsY3VsYXRlIHQtdGVzdCByZXN1bHRzIGZvciBlYWNoIGdlbmUKdGVzdFN0YXQgPC0gZ2VuZWZpbHRlcjo6cm93dHRlc3RzKHNpbUNvdW50cywgZ3JvdXApCgojIyB1bmNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiBvZiB0ZXN0IHN0YXRpc3RpY3MgcHJpb3IgdG8gZmlsdGVyaW5nCnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWMsIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiVW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24iKQoKIyBmaWx0ZXIgb3V0IH4yMCUgb2YgZ2VuZXMgd2l0aCBsb3dlc3QgZWZmZWN0IHNpemVzCm1lYW4oZmlsdGVyU3RhdEVmZmVjdFNpemUgPiAxKQpoaXN0KGZpbHRlclN0YXRFZmZlY3RTaXplLCBicmVha3M9NDApCmFibGluZSh2PTEsIGNvbD0icmVkIikKa2VlcEVmZmVjdFNpemUgPC0gZmlsdGVyU3RhdEVmZmVjdFNpemUgPiAxCiMjIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbjogdmVyeSBkaWZmZXJlbnQhCnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWNba2VlcEVmZmVjdFNpemVdLCBuYS5ybT1UUlVFKSwKICAgICB4bGFiID0gIlRlc3Qgc3RhdGlzdGljIiwKICAgICBtYWluID0gIkNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiIpCgoKIyMgaW4gc2FtZSBwbG90CnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWMsIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIGNvbCA9ICJvcmFuZ2UiLAogICAgIG1haW4gPSAiVGVzdCBzdGF0aXN0aWNzIGJlZm9yZSBhbmQgYWZ0ZXIgZmlsdGVyaW5nIG9uIG1lYW4gZGlmZmVyZW5jZSIsCiAgICAgbHdkID0gMikKbGluZXMoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWNba2VlcEVmZmVjdFNpemVdLCBuYS5ybT1UUlVFKSwKICAgICB4bGFiID0gIlRlc3Qgc3RhdGlzdGljIiwKICAgICBtYWluID0gIkNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiIsCiAgICAgY29sID0gInN0ZWVsYmx1ZSIsCiAgICAgbHdkID0gMikKbGVnZW5kKCJ0b3ByaWdodCIsIGMoIlVuY29uZGl0aW9uYWwiLCAiQ29uZGl0aW9uYWwiKSwKICAgICAgIGNvbD1jKCJvcmFuZ2UiLCAic3RlZWxibHVlIiksIGx3ZD0yLCBidHk9J24nKQpgYGAKCgojIyBBbiBpbmRlcGVuZGVudCB0ZXN0IHN0YXRpc3RpYwoKYGBge3J9CmZpbHRlclN0YXRHbG9iYWxNZWFuIDwtIHJvd01lYW5zKHNpbUNvdW50cykKIyB3ZSByZW1vdmUgYSBzaW1pbGFyIGZyYWN0aW9uIG9mIH4yMCUgZ2VuZXMKbWVhbihmaWx0ZXJTdGF0R2xvYmFsTWVhbiA+IDUpIAprZWVwR2xvYmFsTWVhbiA8LSBmaWx0ZXJTdGF0R2xvYmFsTWVhbiA+IDUKCiMjIHVuY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uCnBsb3QoZGVuc2l0eSh0ZXN0U3RhdCRzdGF0aXN0aWMsIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiVW5jb25kaXRpb25hbCBkaXN0cmlidXRpb24iKQoKIyMgY29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uOiB0aGUgc2FtZS4KcGxvdChkZW5zaXR5KHRlc3RTdGF0JHN0YXRpc3RpY1trZWVwR2xvYmFsTWVhbl0sIG5hLnJtPVRSVUUpLAogICAgIHhsYWIgPSAiVGVzdCBzdGF0aXN0aWMiLAogICAgIG1haW4gPSAiQ29uZGl0aW9uYWwgZGlzdHJpYnV0aW9uIikKCgojIyBpbiBzYW1lIHBsb3QKcGxvdChkZW5zaXR5KHRlc3RTdGF0JHN0YXRpc3RpYywgbmEucm09VFJVRSksCiAgICAgeGxhYiA9ICJUZXN0IHN0YXRpc3RpYyIsCiAgICAgY29sID0gIm9yYW5nZSIsCiAgICAgbWFpbiA9ICJUZXN0IHN0YXRpc3RpY3MgYmVmb3JlIGFuZCBhZnRlciBmaWx0ZXJpbmcgb24gZ2xvYmFsIG1lYW4iLAogICAgIGx3ZCA9IDIpCmxpbmVzKGRlbnNpdHkodGVzdFN0YXQkc3RhdGlzdGljW2tlZXBHbG9iYWxNZWFuXSwgbmEucm09VFJVRSksCiAgICAgeGxhYiA9ICJUZXN0IHN0YXRpc3RpYyIsCiAgICAgbWFpbiA9ICJDb25kaXRpb25hbCBkaXN0cmlidXRpb24iLAogICAgIGNvbCA9ICJzdGVlbGJsdWUiLAogICAgIGx3ZCA9IDIpCmxlZ2VuZCgidG9wcmlnaHQiLCBjKCJVbmNvbmRpdGlvbmFsIiwgIkNvbmRpdGlvbmFsIiksCiAgICAgICBjb2w9Yygib3JhbmdlIiwgInN0ZWVsYmx1ZSIpLCBsd2Q9MiwgYnR5PSduJykKYGBgCgoKIyBBbGlhc2luZwoKU3VwcG9zZSB3ZSBhcmUgd29ya2luZyB3aXRoIHRoZSBmb2xsb3dpbmcgZXhwZXJpbWVudGFsIGRlc2lnbiBvbiBjb2xvbiBjYW5jZXIuIFN0dWR5aW5nIHRoZSBlZmZlY3Qgb2YgYSBkcnVnIG9uIGdlbmUgZXhwcmVzc2lvbiwgcmVzZWFyY2hlcnMgZ2F0aGVyIFJOQS1zZXEgZGF0YSBmcm9tIGZvdXIgY29sb24gY2FuY2VyIHBhdGllbnRzIGFuZCBmb3VyIGhlYWx0aHkgaW5kaXZpZHVhbHMuIEZvciBlYWNoIGluZGl2aWR1YWwsIHRoZXkgb2J0YWluIFJOQS1zZXEgZGF0YSBmcm9tIGEgYmxvb2Qgc2FtcGxlIGJlZm9yZSBhcyB3ZWxsIGFzIHR3byB3ZWVrcyBhZnRlciB0YWtpbmcgYSBkYWlseSBkb3NlIG9mIHRoZSBkcnVnLiBUaGUgcmVzZWFyY2ggcXVlc3Rpb24gcmVsYXRlcyB0byBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhZnRlciB2cy4gYmVmb3JlIHRha2luZyB0aGUgZHJ1ZywgaW4gcGFydGljdWxhciB3aGV0aGVyIHRoaXMgaXMgZGlmZmVyZW50IGZvciB0aGUgZGlzZWFzZWQgdmVyc3VzIGhlYWx0aHkgZ3JvdXAgKGkuZS4sIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRpbWUgKGJlZm9yZS9hZnRlciB0YWtpbmcgdGhlIGRydWcpIGFuZCBkaXNlYXNlIHN0YXR1cyAoaGVhbHRoeS9jb2xvbiBjYW5jZXIpKS4KCkluIHRlcm1zIG9mIHRoZSBtb2RlbCBtYXRyaXgsIHdlIGNvdWxkIGltYWdpbmUgYSBkZXNpZ24gc3VjaCBhcyBgIH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZWAsIHdoZXJlIAoKIC0gYGRpc2Vhc2VgIGlzIGEgYmluYXJ5IGluZGljYXRvciByZWZlcnJpbmcgdG8gY29sb24gY2FuY2VyIHZlcnN1cyBjb250cm9sIHNhbXBsZS4KIC0gYHRpbWVgIGRlZmluZXMgaWYgdGhlIHNhbXBsZSBpcyB0YWtlbiBiZWZvcmUgb3IgYWZ0ZXIgdGFraW5nIHRoZSBkcnVnLgogLSBgcGF0aWVudGAgZGVmaW5lcyB0aGUgaW5kaXZpZHVhbCBkb25vciB0aGUgc2FtcGxlIGNvbWVzIGZyb20uCgpUaGUgcmVzZWFyY2ggcXVlc3Rpb24gY291bGQgdGhlbiBhbW91bnQgdG8gdGVzdGluZyB0aGUgYGRpc2Vhc2UgKiB0aW1lYCBpbnRlcmFjdGlvbi4KCkxldCdzIHRyeSB0aGlzLCBieSBzaW11bGF0aW5nIHJhbmRvbSBkYXRhIGZvciBvbmUgZ2VuZS4KCmBgYHtyfQpzZXQuc2VlZCgyKQojIDIgc2FtcGxlcyBwZXIgcGF0aWVudCBmb3IgOCBwYXRpZW50cwpwYXRpZW50IDwtIGZhY3RvcihyZXAobGV0dGVyc1sxOjhdLCBlYWNoPTIpKSAKIyBmaXJzdCBmb3VyIGFyZSBoZWFsdGh5LCBuZXh0IGZvdXIgYXJlIGRpc2Vhc2VkCmRpc2Vhc2UgPC0gZmFjdG9yKGMocmVwKCJoZWFsdGh5Iiw4KSwgcmVwKCJjYW5jZXIiLDgpKSwgbGV2ZWxzPWMoImhlYWx0aHkiLCAiY2FuY2VyIikpIAojIG9uZSBiZWZvcmUgYW5kIG9uZSBhZnRlciBzYW1wbGUgZm9yIGVhY2gKdGltZSA8LSBmYWN0b3IocmVwKGMoImJlZm9yZSIsICJhZnRlciIpLCA4KSwgbGV2ZWxzPWMoImJlZm9yZSIsICJhZnRlciIpKSAKCnRhYmxlKHBhdGllbnQsIGRpc2Vhc2UsIHRpbWUpCgojIyBzaW11bGF0ZSBkYXRhIGZvciBvbmUgZ2VuZQpuIDwtIDE2CnkgPC0gcnBvaXMobiA9IG4sIGxhbWJkYSA9IDUwKQoKIyMgZml0IGEgUG9pc3NvbiBtb2RlbAptIDwtIGdsbSh5IH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZSwKICAgICAgICAgZmFtaWx5ID0gInBvaXNzb24iKQpzdW1tYXJ5KG0pCmBgYAoKIC0tLQogCldlIGZpbmQgdGhhdCBvbmUgb2YgdGhlIGNvZWZmaWNpZW50cyBpcyBgTkFgISBUaGlzIGlzIG9idmlvdXNseSBub3QgYmVjYXVzZSB3ZSdyZSBkZWFsaW5nIHdpdGggYE5BYCB2YWx1ZXMgaW4gdGhlIGRhdGEgYXMgd2UndmUganVzdCBzaW11bGF0ZWQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIG91cnNlbHZlcy4gV2hhdCdzIGdvaW5nIG9uPwoKT25lIG9mIHRoZSBwYXJhbWV0ZXJzLCBpbiB0aGlzIGNhc2UgdGhlIHBhcmFtZXRlciBkaXN0aW5ndWlzaGluZyBjYW5jZXIgZnJvbSBoZWFsdGh5IHBhdGllbnRzICoqY2Fubm90IGJlIGVzdGltYXRlZCBhcyBpdCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiBvdGhlciBwYXJhbWV0ZXJzKiouIEluIG91ciBjYXNlLCBlc3RpbWF0aW5nIHRoZSBkaXNlYXNlZCBlZmZlY3Qgd291bGQgdXNlIGluZm9ybWF0aW9uIHRoYXQgaXMgYWxyZWFkeSB1c2VkIHRvIGVzdGltYXRlIHRoZSBwYXRpZW50LWxldmVsIGludGVyY2VwdHMuIEluIG90aGVyIHdvcmRzLCAqKm9uY2UgeW91IGtub3cgdGhlIHBhdGllbnQsIHlvdSBpbW1lZGlhdGVseSBhbHNvIGtub3cgdGhlIGRpc2Vhc2Ugc3RhdHVzKiosIHNvIGVzdGltYXRpbmcgdGhlIGRpc2Vhc2VkIHZzIGhlYWx0aHkgZWZmZWN0IG9uIHRvcCBvZiB0aGUgcGF0aWVudCBlZmZlY3QgcHJvdmlkZXMgbm8gYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBpZiB3ZSBoYXZlIGFscmVhZHkgZXN0aW1hdGVkIHRoZSBwYXRpZW50LWxldmVsIGVmZmVjdHMuIFRoaXMgY29uY2VwdCBpcyBjYWxsZWQgYWxpYXNpbmcsIGFuZCBpcyBhIGNvbW1vbiB0ZWNobmljYWwgaXNzdWUgaW4gJ29taWNzIGV4cGVyaW1lbnRzIHdpdGggY29tcGxleCBleHBlcmltZW50YWwgZGVzaWducy4gCgogLS0tCgpXaGlsZSB0byB1bmRlcnN0YW5kIHRoZSBvcmlnaW4gb2YgdGhlIGFsaWFzaW5nIGl0IGlzIGNydWNpYWwgdG8gdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHZhcmlhYmxlcyBpbiB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiwgd2UgY2FuIGFsc28gaW52ZXN0aWdhdGUgaXQgaW4gZGV0YWlsIHVzaW5nIHRoZSBgYWxpYXNgIGZ1bmN0aW9uLCB0byBnaXZlIHVzIGFuIGlkZWEuCgpgYGB7cn0KYWxpYXMobSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgZWZmZWN0IGBkaXNlYXNlY2FuY2VyYCBpcyBhIGxpbmVhciBjb21iaW5hdGlvbiBvZiB0aGUgcGF0aWVudC1zcGVjaWZpYyBlZmZlY3RzIG9mIHRoZSBjYW5jZXIgcGF0aWVudHMuIFRoaXMgbWFrZXMgc2Vuc2UhCgogLS0tIAoKRm9yIGNsYXJpdHksIGxldCdzIHJlcHJvZHVjZSB0aGlzIHVzaW5nIG91ciBkZXNpZ24gbWF0cml4LgoKYGBge3J9ClggPC0gbW9kZWwubWF0cml4KH4gcGF0aWVudCArIGRpc2Vhc2UqdGltZSkgIyB0aGlzIGlzIHRoZSBkZXNpZ24gdXNlZCBpbiBnbG0oKQoKIyMgdGhlc2UgYXJlIGluZGVlZCBpZGVudGljYWwuClhbLCJkaXNlYXNlY2FuY2VyIl0KWFssInBhdGllbnRlIl0gKyBYWywicGF0aWVudGYiXSArIFhbLCJwYXRpZW50ZyJdICsgWFssInBhdGllbnRoIl0KYGBgCgpTaW5jZSBvbmUgb2Ygb3VyIHBhcmFtZXRlcnMgaXMgYSBsaW5lYXIgY29tYmluYXRpb24gb2Ygb3RoZXIgcGFyYW1ldGVycywgaXQgY2Fubm90IGJlIGVzdGltYXRlZCBzaW11bHRhbmVvdXNseSB3aXRoIHRoZSBvdGhlciBwYXJhbWV0ZXJzLiBJbiB0aGlzIGNhc2UsIHdlIGNhbiBhY3R1YWxseSBkcm9wIHRoZSBgZGlzZWFzZWAgbWFpbiBlZmZlY3QgZnJvbSB0aGUgbW9kZWwsIHNpbmNlIHdlIGtub3cgdGhhdCBpdCBpcyBhbHJlYWR5IGluY2x1ZGVkIGluIHRoZSBgcGF0aWVudGAgZWZmZWN0LgoKIC0tLQoKV2Ugd2lsbCBoYXZlIHRvIGNhcmVmdWxseSBjb25zdHJ1Y3Qgb3VyIGRlc2lnbiBtYXRyaXggaW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgYWxsIGltcG9ydGFudCBzb3VyY2VzIG9mIHZhcmlhdGlvbiB3aGlsZSBzdGlsbCBhbGxvd2luZyB1cyB0byBhbnN3ZXIgdGhlIHJlc2VhcmNoIHF1ZXN0aW9uIG9mIGludGVyZXN0LiBUaGUgYWxpYXNpbmcgZXhwbG9yYXRpb24gYWJvdmUgaGFzIG1hZGUgaXQgY2xlYXIgd2UgbWF5IGRyb3AgdGhlIGBkaXNlYXNlYCBtYWluIGVmZmVjdCwgc28gbGV0J3Mgc3RhcnQgYnkgY29uc3RydWN0aW5nIHRoaXMgZGVzaWduIG1hdHJpeC4KCmBgYHtyfQpYIDwtIG1vZGVsLm1hdHJpeCh+IHBhdGllbnQgKyB0aW1lICsgZGlzZWFzZTp0aW1lKQoKbTIgPC0gZ2xtKHkgfiAtMSArIFgsCiAgICAgICAgIGZhbWlseSA9ICJwb2lzc29uIikKc3VtbWFyeShtMikKYWxpYXMobTIpCmBgYAoKV2UgYXJlIHN0aWxsIGNvbmZyb250ZWQgd2l0aCBhbGlhc2luZyBhcyB0aGUgbW9kZWwgbWF0cml4IGNvbnRhaW5zIGFuIGludGVyYWN0aW9uIGVmZmVjdCBgdGltZWJlZm9yZTpkaXNlYXNlY2FuY2VyYCBhcyB3ZWxsIGFzIGB0aW1lYWZ0ZXI6ZGlzZWFzZWNhbmNlcmAsIHdoaWxlIG9ubHkgdGhlIGxhdHRlciBpcyByZWxldmFudC4gSW5kZWVkLCB3ZSBrbm93IHRoYXQgd2UgY2FuIGRlcml2ZSB0aGUgYHRpbWViZWZvcmU6ZGlzZWFzZWNhbmNlcmAgZWZmZWN0IGJ5IGF2ZXJhZ2luZyB0aGUgcGF0aWVudCBlZmZlY3RzIG9mIHRoZSBjYW5jZXIgcGF0aWVudHMuCgogLS0tCgpgYGB7cn0KWCA8LSBYWywhY29sbmFtZXMoWCkgJWluJSAidGltZWJlZm9yZTpkaXNlYXNlY2FuY2VyIl0KCgojIyBmaXQgYSBQb2lzc29uIG1vZGVsCm0yIDwtIGdsbSh5IH4gLTEgKyBYLAogICAgICAgICBmYW1pbHkgPSAicG9pc3NvbiIpCnN1bW1hcnkobTIpCmBgYAoKV2Ugc2VlIHRoYXQgYWxsIGNvZWZmaWNpZW50cyBjYW4gbm93IGJlIGVzdGltYXRlZC4gVGhlIGB0aW1lYWZ0ZXJgIGVmZmVjdCBtYXkgYmUgaW50ZXJwcmV0ZWQgYXMgdGhlIHRpbWUgZWZmZWN0IGZvciBoZWFsdGh5IHBhdGllbnRzLCB3aGlsZSB0aGUgYHRpbWVhZnRlcjpkaXNlYXNlY2FuY2VyYCBlZmZlY3QgbWF5IGJlIGludGVycHJldGVkIGFzIHRoZSBkaWZmZXJlbmNlIGluIHRoZSB0aW1lIGVmZmVjdCBmb3IgY2FuY2VyIHBhdGllbnRzIGFzIGNvbXBhcmVkIHRvIGhlYWx0aHkgcGF0aWVudHMsIGkuZS4sIGl0IGlzIHRoZSByZWxldmFudCBpbnRlcmFjdGlvbiBlZmZlY3Qgd2UgYXJlIGludGVyZXN0ZWQgaW4uIAoKKipRdWVzdGlvbioqLiBUYWtpbmcgdGhpcyBmdXJ0aGVyLCBzdXBwb3NlIHRoYXQgd2UgY2FuIHNhZmVseSBhc3N1bWUgdGhhdCB0aGVyZSBpcyBubyBpbnRlcmFjdGlvbiBlZmZlY3QgYmV0d2VlbiBkaXNlYXNlIHN0YXR1cyBhbmQgdGltZS4gSG93IHdvdWxkIHlvdSBub3cgdGVzdCBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYmV0d2VlbiBoZWFsdGh5IGFuZCBjYW5jZXIgcGF0aWVudHMgYXQgdGhlIGZpcnN0IHRpbWVwb2ludD8gU3BlY2lmeSB0aGUgZXhwZXJpbWVudGFsIGRlc2lnbiBhbmQgY29udHJhc3QgdXNlZC4KCjxkZXRhaWxzPjxzdW1tYXJ5PiBBbnN3ZXIuIDwvc3VtbWFyeT48cD4KQXNzdW1pbmcgbm8gaW50ZXJhY3Rpb24sIHdlIGNhbiBzcGVjaWZ5IHRoZSBkZXNpZ24gYXMgZm9sbG93czoKCmBgYHtyfQpYTWFpbiA8LSBtb2RlbC5tYXRyaXgofiBwYXRpZW50ICsgdGltZSkKaGVhZChYTWFpbikKYGBgCgpJbiBvcmRlciB0byBzZXQgdXAgdGhlIGNvbnRyYXN0IHRlc3RpbmcgZm9yIGhlYWx0aHkgdmVyc3VzIGRpc2Vhc2VkIHBhdGllbnRzIGF0IHRoZSBmaXJzdCB0aW1lcG9pbnQsIHdlIG5lZWQgdG8gdGFrZSB0aGUgYXZlcmFnZSBvZiB0aGUgYXBwcm9wcmlhdGUgcGF0aWVudC1sZXZlbCBpbnRlcmNlcHRzLgpUaGUgYXZlcmFnZSBleHByZXNzaW9uIGZvciBoZWFsdGh5IHBhdGllbnRzIGlzCiQkIFxsb2cgXG11X3toZWFsdGh5fSA9IFxmcmFjezF9ezR9IFxsZWZ0XHsgXGJldGFfMCArIChcYmV0YV8wICsgXGJldGFfMSkgKyAoXGJldGFfMCArIFxiZXRhXzIpICsgKFxiZXRhXzAgKyBcYmV0YV8zKSBccmlnaHRcfS4gJCQKU2ltaWxhciwgZm9yIHRoZSBkaXNlYXNlZCBwYXRpZW50cyBpdCBlcXVhbHMKJCQgXGxvZyBcbXVfe2Rpc2Vhc2VkfSA9IFxmcmFjezF9ezR9IFxsZWZ0XHsgKFxiZXRhXzAgKyBcYmV0YV80KSArIChcYmV0YV8wICsgXGJldGFfNSkgKyAoXGJldGFfMCArIFxiZXRhXzYpICsgKFxiZXRhXzAgKyBcYmV0YV83KSBccmlnaHRcfS4gJCQKQW5kIHRodXMgdGhlIHJlbGV2YW50IGNvbnRyYXN0CiQkIFxsb2cgXGZyYWN7XG11X3tkaXNlYXNlZH19e1xtdV97aGVhbHRoeX19ID0gXGZyYWN7MX17NH0gKFxiZXRhXzQgKyBcYmV0YV81ICsgXGJldGFfNiArIFxiZXRhXzcgKSAtIFxmcmFjezF9ezR9IChcYmV0YV8xICsgXGJldGFfMiArIFxiZXRhXzMpLiQkCjwvcD48L2RldGFpbHM+CgojIGxpbW1hLXZvb20gYXMgYW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggdG8gbW9kZWxpbmcgY291bnRzCgpgbGltbWFgIGlzIGEgcG93ZXJmdWwgbGluZWFyIG1vZGVsIGJhc2VkIGZyYW1ld29yayBmb3IgbW9kZWxpbmcgbWljcm9hcnJheSBnZW5lIGV4cHJlc3Npb24gZGF0YSBhbmQgaW5mZXJyaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMsIGFuZCBoYXMgYmVlbiBpbnRyb2R1Y2VkIGluIHRoZSBwcm90ZW9taWNzIG1vZHVsZSBvZiB0aGlzIGNvdXJzZS4gV2l0aCB0aGUgaW5jZXB0aW9uIG9mIFJOQS1zZXEsIHRoZSBgbGltbWFgIGRldmVsb3BlcnMgZ290IGNyZWF0aXZlIGFuZCBleHRlbmRlZCB0aGVpciBmcmFtZXdvcmsgdG8gYWxzbyBtb2RlbCBjb3VudCBkYXRhLCBoZW5jZSBjcmVhdGluZyBsaW1tYS12b29tLgoKIyMgVGhlIGxpbW1hIGZyYW1ld29yayBmb3IgdGhlIGFuYWx5c2lzIG9mIG1pY3JvYXJyYXlzCgpJbiB0aGUgcHJvdGVvbWljcyBtb2R1bGUsIHdlIGhhdmUgcHJldmlvdXNseSBpbnRyb2R1Y2VkIHRoZSBwb3dlcmZ1bCBsaW5lYXIgbW9kZWwgYmFzZWQgZnJhbWV3b3JrIGBsaW1tYWAsIGFuZCBob3cgaXQgdXNlcyBhbiBlbXBpcmljYWwgQmF5ZXMgc3RyYXRlZ3kgdG8gYm9ycm93IGluZm9ybWF0aW9uIGFjcm9zcyBwcm90ZWlucyB0byBkZXJpdmUgYSBwb3N0ZXJpb3IgdmFyaWFuY2UgZXN0aW1hdGUuIEluIGl0cyBkZWZhdWx0IGltcGxlbWVudGF0aW9uLCBgbGltbWFgIGNhbm5vdCBiZSB1c2VkIHRvIG1vZGVsIGNvdW50IGRhdGEsIGFzIGl0IGNhbiBub3QgYWNjb3VudCBmb3IgdGhlaXIgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAuIFRoZSBkZXZlbG9wZXJzLCBob3dldmVyLCBjYW1lIHVwIHdpdGggYSBjcmVhdGl2ZSBhcHByb2FjaCB0byB1c2UgdGhlIGBsaW1tYWAgZnJhbWV3b3JrIHRvIG1vZGVsIGNvdW50IGRhdGEuCgojIyBsaW1tYS12b29tOiBleHRlbmRpbmcgbGltbWEgZm9yIFJOQS1zZXEgZGF0YQoKIC0gQ291bnQgbW9kZWxzIHN1Y2ggYXMgYGVkZ2VSYCBhbmQgYERFU2VxMmAgYXV0b21hdGljYWxseSBhY2NvdW50IGZvciB0aGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAgb2YgdGhlIGRhdGEgYnkgYXNzdW1pbmcgYSBwcm9wZXIgY291bnQgZGlzdHJpYnV0aW9uLCBnaXZlbiB0aGF0IHRoZSBvYnNlcnZlZCBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcCBpcyBjbG9zZSB0byB0aGUgb25lIGFzc3VtZWQgYnkgdGhlIGRpc3RyaWJ1dGlvbi4gSG93ZXZlciwgdGhleSBhcmUgYWxzbyBtb3JlIGNvbXBsZXgsIGJvdGggY29tcHV0YXRpb25hbGx5IGFzIHdlbGwgYXMgc3RhdGlzdGljYWxseSBhbmQgY29uY2VwdHVhbGx5LgogLSBsaW1tYS12b29tIChbTGF3ICpldCBhbC4qICgyMDE0KV0oaHR0cHM6Ly9nZW5vbWViaW9sb2d5LmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvZ2ItMjAxNC0xNS0yLXIyOSkpIGlzIGEgbWV0aG9kIHRoYXQgdW5sb2NrcyBHYXVzc2lhbiBsaW5lYXIgbW9kZWxzIHRvIGFuYWx5emUgY291bnQgZGF0YSBpbiB0aGUgY29udGV4dCBvZiBSTkEtc2VxLCBieSBmaXJzdCBlc3RpbWF0aW5nIHRoZSBtZWFuLXZhcmlhbmNlIHJlbGF0aW9uc2hpcCBvZiB0aGUgZGF0YXNldCBhdCBoYW5kLCBhbmQgc3Vic2VxdWVudGx5IGluY29ycG9yYXRpbmcgaXQgaW4gdGhlIGFuYWx5c2lzIHRocm91Z2ggb2JzZXJ2YXRpb24tbGV2ZWwgd2VpZ2h0cyBpbiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLgogCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPXBhc3RlKCJGaWd1cmUgMSBmcm9tIExhdyAqZXQgYWwuKiAoMjAxNCkuIil9CiMgQWxsIGRlZmF1bHRzCmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzX3NlcXVlbmNpbmcvbGltbWFWb29tTWVhblZhcmlhbmNlLnBuZyIpCmBgYAogCiAtLS0KIAogLSBUaGUgbWVhbi12YXJpYW5jZSByZWxhdGlvbnNoaXAgaXMgZGF0YXNldC1zcGVjaWZpYyBhbmQgbmVlZHMgdG8gYmUgZXN0aW1hdGVkIHNlcGFyYXRlbHkgZm9yIGVhY2ggZGF0YXNldC4KIC0gVGhlIG1lYW4tdmFyaWFuY2UgdHJlbmQgaXMgZXN0aW1hdGVkIG5vbnBhcmFtZXRyaWNhbGx5IGFjcm9zcyBhbGwgZ2VuZXMsIHVzaW5nIGEgZ2xvYmFsIG1lYW4gYW5kIHZhcmlhbmNlIGZvciBlYWNoIGdlbmUuIFVzaW5nIHRoaXMgdHJlbmQsIG9ic2VydmF0aW9uLWxldmVsIHZhcmlhbmNlcyBhcmUgZXN0aW1hdGVkIGZvciBlYWNoIGluZGl2aWR1YWwgb2JzZXJ2YXRpb24uCiAtIFRoZXNlIG9ic2VydmF0aW9uLWxldmVsIHZhcmlhbmNlcyBhcmUgdGhlbiB1c2VkIGFzIGludmVyc2Ugd2VpZ2h0cyBpbiB0aGUgbGluZWFyIG1vZGVsaW5nIGZyYW1ld29yaywgdG8gYWNjb3VudCBmb3IgaGV0ZXJvc2NlZGFzdGljaXR5LgoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5jYXA9cGFzdGUoIkZpZ3VyZSAxIGZyb20gTGF3ICpldCBhbC4qICgyMDE0KS4iKX0KIyBBbGwgZGVmYXVsdHMKaW5jbHVkZV9ncmFwaGljcygiLi9pbWFnZXNfc2VxdWVuY2luZy9saW1tYVZvb21XZWlnaHRzLnBuZyIpCmBgYAoKIyMgbGltbWEtdm9vbSBhbmFseXNpcyBvZiBwYXJhdGh5cm9pZCBkYXRhCgpgbGltbWFgIGhhcyBhbiBleHRlbnNpdmUgdXNlcidzIGd1aWRlIHdoaWNoIGNhbiBiZSBhY2Nlc3NlZCB2aWEgYGxpbW1hOjpsaW1tYVVzZXJzR3VpZGUoKWAuCgpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShlZGdlUikKc2UgPC0gcmVhZFJEUygiZGF0YS9zZVBhcmF0aHlyb2lkLnJkcyIpCnNlCmRlc2lnbiA8LSBtb2RlbC5tYXRyaXgofnRyZWF0bWVudCp0aW1lK3BhdGllbnQsIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGE9Y29sRGF0YShzZSkpCgprZWVwIDwtIGZpbHRlckJ5RXhwcihzZSwgZGVzaWduKQp0YWJsZShrZWVwKQpmaWx0Q291bnRzIDwtIGFzc2F5cyhzZSkkY291bnRzW2tlZXAsXQoKZGdlIDwtIERHRUxpc3QoY291bnRzPWZpbHRDb3VudHMpCgojIG5vcm1hbGl6ZSBqdXN0IGFzIGluIGVkZ2VSCmRnZSA8LSBjYWxjTm9ybUZhY3RvcnMoZGdlKQojIGZpdCB0aGUgbWVhbi12YXJpYW5jZSB0cmVuZCB1c2VkIHRvIGNhbGN1bGF0ZSB3ZWlnaHRzCnYgPC0gdm9vbShkZ2UsIGRlc2lnbiwgcGxvdD1UUlVFKQojIHRoaXMgbWVhbi12YXJpYW5jZSB0cmVuZCBpcyB0aGVuIGF1dG9tYXRpY2FsbHkgaW5jb3Jwb3JhdGVkIGludG8gdGhlIHVzdWFsIGxpbW1hIHBpcGVsaW5lCmZpdCA8LSBsbUZpdCh2LCBkZXNpZ24pCmZpdCA8LSBlQmF5ZXMoZml0KQp0dCA8LSB0b3BUYWJsZShmaXQsIGNvZWY9KG5jb2woZGVzaWduLTEpOm5jb2woZGVzaWduKSksIG51bWJlcj1ucm93KGRnZSkpICMgdGVzdCBpbnRlcmFjdGlvbiBlZmZlY3RzCmhlYWQodHQpICNzaW1pbGFyIGFzIGluIGVkZ2VSLCB3ZSBmaW5kIG5vIERFCmBgYAoKIyMjIFRlc3RpbmcgY29udHJhc3RzIHVzaW5nIGxpbW1hLXZvb20KCldoZW4gd29ya2luZyB3aXRoIGxpbW1hLXZvb20sIHdlIGNhbm5vdCBpbW1lZGlhdGVseSB0ZXN0IGEgY29udHJhc3QgdXNpbmcgYSBjb250cmFzdCBtYXRyaXgganVzdCBsaWtlIHdlIGRpZCBpbiBgZWRnZVJgLgpJbnN0ZWFkLCB3ZSB3aWxsIHJlcGFyYW1ldHJpemUgb3VyIG1vZGVsIGFjY29yZGluZyB0byB0aGUgY29udHJhc3RzLCBzdWNoIHRoYXQgZWFjaCBwYXJhbWV0ZXIgY29ycmVzcG9uZHMgdG8gYSBzcGVjaWZpYyBjb250cmFzdCwgdXNpbmcgdGhlIGBjb250cmFzdHMuZml0YCBmdW5jdGlvbi4KCmBgYHtyfQojIGNvbnRyYXN0IG1hdHJpeCB3ZSB1c2VkIGJlZm9yZQpMIDwtIG1hdHJpeCgwLCBucm93ID0gbmNvbChmaXQkY29lZmZpY2llbnRzKSwgbmNvbCA9IDcpCnJvd25hbWVzKEwpIDwtIGNvbG5hbWVzKGZpdCRjb2VmZmljaWVudHMpCmNvbG5hbWVzKEwpIDwtIGMoIkRQTnZzQ09OMjQiLCAiRFBOdnNDT040OCIsCiAgICAgICAgICAgICAgICAgIk9IVHZzQ09OMjQiLCAiT0hUdnNDT040OCIsCiAgICAgICAgICAgICAgICAgIkRQTnZzQ09OSW50IiwgIk9IVHZzQ09OSW50IiwKICAgICAgICAgICAgICAgICAiT0hUdnNEUE5JbnQiKQojIERQTiB2cyBjb250cm9sIGF0IDI0aApMWzIsIkRQTnZzQ09OMjQiXSA8LSAxCiMgRFBOIHZzIGNvbnRyb2wgYXQgNDhoCkxbYygyLDgpLCJEUE52c0NPTjQ4Il0gPC0gMQojIE9IVCB2cyBjb250cm9sIGF0IDI0aApMWzMsIk9IVHZzQ09OMjQiXSA8LSAxCiMgT0hUIHZzIGNvbnRyb2wgYXQgNDhoCkxbYygzLDkpLCJPSFR2c0NPTjQ4Il0gPC0gMQojIERQTiBjb250cm9sIGludGVyYWN0aW9uCkxbOCwiRFBOdnNDT05JbnQiXSA8LSAxCiMgT0hUIGNvbnRyb2wgaW50ZXJhY3Rpb24KTFs5LCJPSFR2c0NPTkludCJdIDwtIDEKIyBPSFQgRFBOIGludGVyYWN0aW9uCkxbYyg5LDgpLCJPSFR2c0RQTkludCJdIDwtIGMoMSwgLTEpCkwKCmZpdCA8LSBsbUZpdCh2LCBkZXNpZ24pCmZpdDIgPC0gY29udHJhc3RzLmZpdChmaXQsIEwpCmZpdDIgPC0gZUJheWVzKGZpdDIpCmhlYWQoZml0MiRjb2VmZmljaWVudHMpICMgb25lIGNvZWZmaWNpZW50IGlzIG9uZSBjb250cmFzdAojIGxvb3Agb3ZlciBhbGwgY29udHJhc3RzIG9mIGludGVyZXN0CnR0TGlzdCA8LSBsaXN0KCkKZm9yKGNjIGluIDE6bmNvbChMKSl7CiAgdHRMaXN0W1tjY11dIDwtIHRvcFRhYmxlKGZpdDIsIGNvZWY9Y2MsIG51bWJlcj1ucm93KGRnZSkpCn0KbmFtZXModHRMaXN0KSA8LSBjb2xuYW1lcyhMKQpuREUgPC0gdW5saXN0KGxhcHBseSh0dExpc3QsIGZ1bmN0aW9uKHgpIHN1bSh4JGFkai5QLlZhbCA8PSAwLjA1KSkpCm5ERQpgYGA=