Creative Commons License

1 Background

Histologic grade in breast cancer provides clinically important prognostic information. Researchers examined whether histologic grade was associated with gene expression profiles of breast cancers and whether such profiles could be used to improve histologic grading. In this tutorial we will assess the association between histologic grade and the expression of the KPNA2 gene that is known to be associated with poor BC prognosis. The patients, however, do not only differ in the histologic grade, but also on their lymph node status. The lymph nodes were not affected (0) or surgically removed (1).

Load libraries

library(tidyverse)

2 Data analysis

2.1 Import KPNA2 data in R

kpna2 <- read.table("https://raw.githubusercontent.com/statOmics/SGA21/master/data/kpna2.txt",header=TRUE)
kpna2

2.2 Transform the variable grade and node to a factor

kpna2$grade <- as.factor(kpna2$grade)
kpna2$node <- as.factor(kpna2$node)

2.3 Data exploration

Histologic grade and lymph node status can be associated with the kpna2 gene expression. Moreover, it is also possible that the differential expression associated with histological grade is different in patients that have unaffected lymph nodes and patients for which the lymph nodes had to be removed.

kpna2 %>% 
  ggplot(aes(x=node:grade,y=gene,fill=node:grade)) +
  geom_boxplot(outlier.shape = NA) +
  geom_jitter()

The plot suggests

  • An effect of the histological grade
  • An effect of node status
  • The differential expression associated to grade seems to differ according to the lymph node status (interaction)
  • Mean variance relation?

2.4 Model

Histologic grade and lymph node status can be associated with the kpna2 gene expression. Moreover, it is also possible that the differential expression associated with histological grade is different in patients that have unaffected lymph nodes and patients for which the lymph nodes had to be removed. Hence, we will have to model the gene expression by using main effects for grade, node and a grade x node interaction.

#Model with main effects for histological grade and node and grade x node interaction
fit <- lm(gene~grade*node,data=kpna2)
plot(fit)

The variance seems to increase with the mean. The QQ-plot of the residuals shows deviations from normality or some outliers.

We will first log transform the data.

fit <- lm(gene %>% log2~grade*node,data=kpna2)
plot(fit)

  • The variance is now more or less equal for every treatment x node combination.
  • The QQ-plot of the residuals shows no deviations from normality.
library(car)
Anova(fit,type="III")

The output shows that there is a very significant interaction (\(p=\) 0.0071). Hence, the association of the histological grade on the gene expression differs according to the lymph node status and vice versa.

The researchers are therefore interested in studying and reporting on the following hypotheses:

  • Is the KPNA2 expression on average different between grade 3 and grade 1 tumors from patients with unaffected lymph nodes (by testing \(H_0: \log_2{FC}_{g3n0-g1n0}=0\text{ vs }H1: \log_2{FC}_{g3n0-g1n0}\neq 0\))

  • Is the KPNA2 expression on average different between grade 3 and grade 1 tumors from patients with affected lymph nodes (by testing \(H_0: \log_2{FC}_{g3n1-g1n1}=0\text{ vs }H1: \log_2{FC}_{g3n1-g1n1}\neq 0\))

  • Is the KPNA2 expression on average different in grade 1 tumors of patients with affected and patients with unaffected lymph nodes (by testing \(H_0: \log_2{FC}_{g1n1-g1n0}=0\text{ vs }H1: \log_2{FC}_{g1n1-g1n0}\neq 0\))

  • Is the KPNA2 expression on average different in grade 3 tumors of patients with affected and patients with unaffected lymph nodes (by testing \(H_0: \log_2{FC}_{g3n1-g3n0}=0\text{ vs }H1: \log_2{FC}_{g3n1-g3n0}\neq 0\))

  • Is the fold change of the KPNA2 gene between grade 3 and grade 1 different according to the lymph node status and vice versa (tested already by assessing the interaction: \(H_0: \log_2{FC}_{g3n0-g1n0}=\log_2{FC}_{g3n1-g1n1} \text{ vs }H1:\log_2{FC}_{g3n0-g1n0}\neq\log_2{FC}_{g3n1-g1n1}\)).

3 Interpretation of model parameters and statistical tests

ExploreModelMatrix::VisualizeDesign(kpna2,~grade*node)$plotlist
[[1]]

summary(fit)

Call:
lm(formula = gene %>% log2 ~ grade * node, data = kpna2)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.57694 -0.19857 -0.04079  0.20807  0.64557 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)    7.4796     0.1292  57.893  < 2e-16 ***
grade3         1.6675     0.1827   9.127 1.44e-08 ***
node1          0.6577     0.1827   3.600  0.00179 ** 
grade3:node1  -0.7748     0.2584  -2.998  0.00710 ** 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3165 on 20 degrees of freedom
Multiple R-squared:  0.848, Adjusted R-squared:  0.8252 
F-statistic: 37.18 on 3 and 20 DF,  p-value: 2.266e-08
#Calculate confidence intervals for parameters of model
CIfit <- confint(fit)
#log_2 FC between g3n0-g1n0, g1n1-g1n0
#and log_2 difference in FC g3n1-g1n1 and FC g3n0-g1n0
CIfit
                  2.5 %     97.5 %
(Intercept)   7.2101125  7.7491128
grade3        1.2864076  2.0486691
node1         0.2766005  1.0388620
grade3:node1 -1.3137511 -0.2357505
#Transform parameters and the CI back to the original scale
2^fit$coef
 (Intercept)       grade3        node1 grade3:node1 
 178.4792627    3.1767209    1.5775997    0.5844896 
2^CIfit
                   2.5 %      97.5 %
(Intercept)  148.0676336 215.1371400
grade3         2.4391992   4.1372414
node1          1.2113372   2.0546063
grade3:node1   0.4022736   0.8492431
2^-fit$coef["grade3:node1"]
grade3:node1 
    1.710895 
2^-CIfit["grade3:node1",]
   2.5 %   97.5 % 
2.485870 1.177519 

We model the log\(_2\)-transformed intensities with the following model: \[ y=\beta_0+\beta_{g3}x_{g3}+\beta_{n1}x_{n1}+\beta_{g3n1}x_{g3}x_{n1}, \]

with \(\beta_0\) the intercept, \(\beta_{g3}\) the main effect for grade, \(x_{g3}\) a dummy variable for grade which is 0 for the control treatment in the absence of grade and 1 for the treatment with grade, \(\beta_{n1}\) the main effect for node, \(x_{n1}\) a dummy variable that is 0 for the measurements of patients with unaffected lymph nodes and 1 for patients for which the lymph nodes were removed and \(\beta_{g3n1}\) the interaction effect between grade and node. To ease the interpretation of the parameters, \(\log_2\) transformed geometric mean intensities are given for each treatment group as well as corresponding contrasts between treatments, which have an interpretation in terms of \(\log_2\) transformed fold changes (FC).

  • \(\log_2\hat{\mu}_{g1n0}=\hat\beta_0\), \(\log_2 \hat{\mu}_{g3n0}=\hat\beta_0+\hat\beta_{g3}\) –> \(\log_2 \widehat{FC}_{g3n0-g1n0}=\hat\beta_{g3}\)

  • \(\log_2 \hat{\mu}_{g1n1}=\hat\beta_0+\hat\beta_{n1}\), \(\log_2 \hat {\mu}_{g3n1}=\hat\beta_0+\hat\beta_{g3}+\hat\beta_{n1}+\hat\beta_{g3n1}\) –> \(\log_2 \widehat{FC}_{g3n1-g1n1}=\hat \beta_{g3} +\hat\beta_{g3n1}\)

  • Similarly, \(\log_2 \widehat{FC}_{g1n1-g1n0}=\hat\beta_{n1}\), \(\log_2 \widehat{FC}_{g3n1-g3n0}=\hat\beta_{n1}+\hat\beta_{g3n1}\)

  • \(\log_2\frac{\widehat{FC}_{g3n1-g1n1}}{\widehat{FC}_{g3n0-g1n0}}=\log_2\frac{\widehat{FC}_{g3n1-g3n0}}{\widehat{FC}_{g1n1-g1n0}}=\hat\beta_{g3n1}\)

with \(\log_2\hat{\mu}_{g1n0}\), \(\log_2\hat{\mu}_{g3n0}\), \(\log_2\hat {\mu}_{g1n1}\) and \(\log_2\hat{\mu}_{g3n1}\) the estimated mean \(\log_2\) transformed intensity for patients with grade 1 and node 0 status, grade 3 and node 0 status, grade 1 and node 1 status and grade 3 and node 1 status, respectively. With \(\log_2 \widehat{FC}_{b-a}\) we indicate \(\log_2\) transformed fold change estimates between treatment b and treatment a, i.e. \(\log_2 \widehat{FC}_{b-a}=\log_2 \hat{\mu}_{b}-\log_2 \hat{\mu}_a=\log_2 \frac{\hat{\mu}_{b}}{\hat{\mu}_{a}}\).

The model immediately provides statistical tests for assessing the significance of fold changes between grade 3 and grade 1 for patients with unaffected lymph nodes (n=0) \(\log_2 {FC}_{g3n0-g1n0}\), fold changes between the grade 1-node 1 patients and grade 1- node 0 patients \(\log_2 {FC}_{g1n1-g3n0}\) and for differences in fold change related to histological grade for node 1 patients and node 0 patients. \(\log_2\frac{{FC}_{g3n1-g1n1}}{{FC}_{g3n0-g1n0}}\), the interaction term.

Interpretation of the model parameters in the model output:

  • The geometric mean intensity for grade 1 patients with unaffected lymph nodes equals \(\exp(\hat \beta_0)\)= 178.48.
    • When lymph nodes are unaffected, the expression is on average 3.18 times higher for patients with histological grade 3 than patients with histological grade 1.
    • The gene expression in histological grade 1 patients with affected lymph nodes is on average 1.58 times higher than for grade 1 patients with unaffected lymph nodes.
  • The fold change corresponding to histological grade is on average 1.71 times lower in patients with affected lymph nodes as compared to patients with unaffected lymph node.

For the remaining hypothesis of interest we will have to define contrasts: linear combinations of the model parameters and evaluate the contrasts with the multcomp package.

The F-test showed an extremely significant association of the node status, hystological grade and/or the interaction between the node status and the grade (p<<0.001).

4 Assessing the significance of all hypothesis of interest

We can assess all contrasts of interest using the multcomp package. This will also allow us to correct for multiple testing, since we assess multiple hypotheses to answer the relevant research question.

  • \(H_0: \log_2{FC}_{g3n0-g1n0}= \beta_{g3}=0\) \(\rightarrow\) “grade3 = 0”
  • \(H_0: \log_2{FC}_{g3n1-g1n1}= \beta_{g3} + \hat\beta_{g3n1}=0\) \(\rightarrow\) “grade3+grade3:node1 = 0”
  • \(H_0: \log_2{FC}_{g1n1-g1n0}= \beta_{n1}\) \(\rightarrow\) “node1 = 0”
  • \(H_0: \log_2{FC}_{g3n1-g3n0}= \beta_{n1} + \hat\beta_{g3n1}=0\) \(\rightarrow\) “node1+grade3:node1 = 0”
  • \(H_0: \log_2{FC}_{g3n1-g1n1} - \log_2{FC}_{g3n0-g1n0} = \hat\beta_{g3n1}=0\), note that the latter hypothesis is also equivalent to \(H_0: \log_2{FC}_{g3n1-g3n0} - \log_2{FC}_{g1n1-g1n0} = \hat\beta_{g3n1}=0\) \(\rightarrow\) “grade3:node1 = 0”
library(multcomp)
fitGlht<- glht(fit, linfct = c("grade3 = 0","grade3+grade3:node1 = 0","node1 = 0","node1+grade3:node1 = 0","grade3:node1 = 0"))
summary(fitGlht)

     Simultaneous Tests for General Linear Hypotheses

Fit: lm(formula = gene %>% log2 ~ grade * node, data = kpna2)

Linear Hypotheses:
                           Estimate Std. Error t value Pr(>|t|)    
grade3 == 0                  1.6675     0.1827   9.127   <0.001 ***
grade3 + grade3:node1 == 0   0.8928     0.1827   4.886   <0.001 ***
node1 == 0                   0.6577     0.1827   3.600   0.0069 ** 
node1 + grade3:node1 == 0   -0.1170     0.1827  -0.640   0.8982    
grade3:node1 == 0           -0.7748     0.2584  -2.998   0.0266 *  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Adjusted p values reported -- single-step method)
confint(fitGlht)

     Simultaneous Confidence Intervals

Fit: lm(formula = gene %>% log2 ~ grade * node, data = kpna2)

Quantile = 2.6974
95% family-wise confidence level
 

Linear Hypotheses:
                           Estimate lwr      upr     
grade3 == 0                 1.66754  1.17469  2.16038
grade3 + grade3:node1 == 0  0.89279  0.39994  1.38563
node1 == 0                  0.65773  0.16489  1.15058
node1 + grade3:node1 == 0  -0.11702 -0.60986  0.37583
grade3:node1 == 0          -0.77475 -1.47174 -0.07776
2^confint(fitGlht)$confint
                       Estimate       lwr       upr
grade3                3.1767209 2.2569120 4.4713998
grade3 + grade3:node1 1.8567602 1.3191415 2.6134865
node1                 1.5775997 1.1208110 2.2205536
node1 + grade3:node1  0.9220906 0.6551023 1.2978904
grade3:node1          0.5844896 0.3604262 0.9478447
attr(,"conf.level")
[1] 0.95
attr(,"calpha")
[1] 2.699264
2^-confint(fitGlht)$confint["grade3:node1",]
Estimate      lwr      upr 
1.710895 2.773403 1.055440 

5 Conclusion

  • There is an extremely significant association between the KPNA2 expression and hystological grade in patients with unaffected as well as in patients with affected lymph nodes (both p<<0.001). When lymph nodes are unaffected, the expression is on average 3.18 times higher for patients with histological grade 3 than patients with histological grade 1 (95% CI [2.26, 4.47]). For patients with affected lymph nodes the expression is on average 1.86 times higher for patients with histological grade 3 tumors than patients with histological grade 1 tumors (95% CI [1.32, 2.61]).

  • The association between the KPNA2 expression with the lymph node status in grade 1 patients is very significant (\(p=\) 0.0071).
    The KPNA2 expression in histological grade 1 patients with affected lymph nodes is on average 1.58 times higher than for grade 1 patients with unaffected lymph nodes (95% CI [1.12, 2.22]). In grade 3 patients, however, this association is not significant (\(p=\) 0.9, 95% CI [0.66, 1.3] ).

  • There is also a significant interaction between the hystological grade and the lymph node status. So the association between the KPNA2 expression and the histological grade depends on the lymph node status and vice versa (\(p=\) 0.027). The fold change corresponding to histological grade is on average 1.71 times lower in patients with affected lymph nodes as compared to patients with unaffected lymph node (95% CI [1.06, 2.77]). (Similarly, the fold change corresponding to the node status is on average 1.71 times lower in patients with grade 3 tumors as compared to patients with grade 1 tumors, 95% CI [1.06, 2.77])

LS0tCnRpdGxlOiAiOC40LiBNdWx0aXBsZSBSZWdyZXNzaW9uOiBLUE5BMiBleGFtcGxlIC0gc29sdXRpb24iCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgICBodG1sX2RvY3VtZW50OgogICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICAgIAogICAgICB0aGVtZTogY29zbW8KICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCjxhIHJlbD0ibGljZW5zZSIgaHJlZj0iaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMCI+PGltZyBhbHQ9IkNyZWF0aXZlIENvbW1vbnMgTGljZW5zZSIgc3R5bGU9ImJvcmRlci13aWR0aDowIiBzcmM9Imh0dHBzOi8vaS5jcmVhdGl2ZWNvbW1vbnMub3JnL2wvYnktbmMtc2EvNC4wLzg4eDMxLnBuZyIgLz48L2E+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlID0gVFJVRSwgY29tbWVudCA9IE5BLCBlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFKQpgYGAKCiMgQmFja2dyb3VuZAoKSGlzdG9sb2dpYyBncmFkZSBpbiBicmVhc3QgY2FuY2VyIHByb3ZpZGVzIGNsaW5pY2FsbHkgaW1wb3J0YW50IHByb2dub3N0aWMgaW5mb3JtYXRpb24uIFJlc2VhcmNoZXJzIGV4YW1pbmVkIHdoZXRoZXIgaGlzdG9sb2dpYyBncmFkZSB3YXMgYXNzb2NpYXRlZCB3aXRoIGdlbmUgZXhwcmVzc2lvbiBwcm9maWxlcyBvZiBicmVhc3QgY2FuY2VycyBhbmQgd2hldGhlciBzdWNoIHByb2ZpbGVzIGNvdWxkIGJlIHVzZWQgdG8gaW1wcm92ZSBoaXN0b2xvZ2ljIGdyYWRpbmcuIEluIHRoaXMgdHV0b3JpYWwgd2Ugd2lsbCBhc3Nlc3MgdGhlIGFzc29jaWF0aW9uIGJldHdlZW4gaGlzdG9sb2dpYyBncmFkZSBhbmQgdGhlIGV4cHJlc3Npb24gb2YgdGhlIEtQTkEyIGdlbmUgdGhhdCBpcyBrbm93biB0byBiZSBhc3NvY2lhdGVkIHdpdGggcG9vciBCQyBwcm9nbm9zaXMuClRoZSBwYXRpZW50cywgaG93ZXZlciwgZG8gbm90IG9ubHkgZGlmZmVyIGluIHRoZSBoaXN0b2xvZ2ljIGdyYWRlLCBidXQgYWxzbyBvbiB0aGVpciBseW1waCBub2RlIHN0YXR1cy4gClRoZSBseW1waCBub2RlcyB3ZXJlIG5vdCBhZmZlY3RlZCAoMCkgb3Igc3VyZ2ljYWxseSByZW1vdmVkICgxKS4KCkxvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBEYXRhIGFuYWx5c2lzCiMjIEltcG9ydCBLUE5BMiBkYXRhIGluIFIKYGBge3J9CmtwbmEyIDwtIHJlYWQudGFibGUoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zdGF0T21pY3MvU0dBMjEvbWFzdGVyL2RhdGEva3BuYTIudHh0IixoZWFkZXI9VFJVRSkKa3BuYTIKYGBgCgojIyBUcmFuc2Zvcm0gdGhlIHZhcmlhYmxlIGdyYWRlIGFuZCBub2RlIHRvIGEgZmFjdG9yCmBgYHtyfQprcG5hMiRncmFkZSA8LSBhcy5mYWN0b3Ioa3BuYTIkZ3JhZGUpCmtwbmEyJG5vZGUgPC0gYXMuZmFjdG9yKGtwbmEyJG5vZGUpCmBgYAoKIyMgRGF0YSBleHBsb3JhdGlvbgpIaXN0b2xvZ2ljIGdyYWRlIGFuZCBseW1waCBub2RlIHN0YXR1cyBjYW4gYmUgYXNzb2NpYXRlZCB3aXRoIHRoZSBrcG5hMiBnZW5lIGV4cHJlc3Npb24uIE1vcmVvdmVyLCBpdCBpcyBhbHNvIHBvc3NpYmxlIHRoYXQgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFzc29jaWF0ZWQgd2l0aCBoaXN0b2xvZ2ljYWwgZ3JhZGUgaXMgZGlmZmVyZW50IGluIHBhdGllbnRzIHRoYXQgaGF2ZSB1bmFmZmVjdGVkIGx5bXBoIG5vZGVzIGFuZCBwYXRpZW50cyBmb3Igd2hpY2ggdGhlIGx5bXBoIG5vZGVzIGhhZCB0byBiZSByZW1vdmVkLiAKCmBgYHtyfQprcG5hMiAlPiUgCiAgZ2dwbG90KGFlcyh4PW5vZGU6Z3JhZGUseT1nZW5lLGZpbGw9bm9kZTpncmFkZSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9qaXR0ZXIoKQpgYGAKClRoZSBwbG90IHN1Z2dlc3RzCgotIEFuIGVmZmVjdCBvZiB0aGUgaGlzdG9sb2dpY2FsIGdyYWRlCi0gQW4gZWZmZWN0IG9mIG5vZGUgc3RhdHVzCi0gVGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFzc29jaWF0ZWQgdG8gZ3JhZGUgc2VlbXMgdG8gZGlmZmVyIGFjY29yZGluZyB0byB0aGUgbHltcGggbm9kZSBzdGF0dXMgKGludGVyYWN0aW9uKSAKLSBNZWFuIHZhcmlhbmNlIHJlbGF0aW9uPwoKIyMgTW9kZWwKCkhpc3RvbG9naWMgZ3JhZGUgYW5kIGx5bXBoIG5vZGUgc3RhdHVzIGNhbiBiZSBhc3NvY2lhdGVkIHdpdGggdGhlIGtwbmEyIGdlbmUgZXhwcmVzc2lvbi4gTW9yZW92ZXIsIGl0IGlzIGFsc28gcG9zc2libGUgdGhhdCB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYXNzb2NpYXRlZCB3aXRoIGhpc3RvbG9naWNhbCBncmFkZSBpcyBkaWZmZXJlbnQgaW4gcGF0aWVudHMgdGhhdCBoYXZlIHVuYWZmZWN0ZWQgbHltcGggbm9kZXMgYW5kIHBhdGllbnRzIGZvciB3aGljaCB0aGUgbHltcGggbm9kZXMgaGFkIHRvIGJlIHJlbW92ZWQuIEhlbmNlLCB3ZSB3aWxsIGhhdmUgdG8gbW9kZWwgdGhlIGdlbmUgZXhwcmVzc2lvbiBieSB1c2luZyBtYWluIGVmZmVjdHMgZm9yIGdyYWRlLCBub2RlIGFuZCBhIGdyYWRlIHggbm9kZSBpbnRlcmFjdGlvbi4gCiAKYGBge3J9CiNNb2RlbCB3aXRoIG1haW4gZWZmZWN0cyBmb3IgaGlzdG9sb2dpY2FsIGdyYWRlIGFuZCBub2RlIGFuZCBncmFkZSB4IG5vZGUgaW50ZXJhY3Rpb24KZml0IDwtIGxtKGdlbmV+Z3JhZGUqbm9kZSxkYXRhPWtwbmEyKQpwbG90KGZpdCkKYGBgCgpUaGUgdmFyaWFuY2Ugc2VlbXMgdG8gaW5jcmVhc2Ugd2l0aCB0aGUgbWVhbi4gClRoZSBRUS1wbG90IG9mIHRoZSByZXNpZHVhbHMgc2hvd3MgZGV2aWF0aW9ucyBmcm9tIG5vcm1hbGl0eSBvciBzb21lIG91dGxpZXJzLgoKV2Ugd2lsbCBmaXJzdCBsb2cgdHJhbnNmb3JtIHRoZSBkYXRhLgoKCmBgYHtyfQpmaXQgPC0gbG0oZ2VuZSAlPiUgbG9nMn5ncmFkZSpub2RlLGRhdGE9a3BuYTIpCnBsb3QoZml0KQpgYGAKCi0gVGhlIHZhcmlhbmNlIGlzIG5vdyBtb3JlIG9yIGxlc3MgZXF1YWwgZm9yIGV2ZXJ5IHRyZWF0bWVudCB4IG5vZGUgY29tYmluYXRpb24uIAotIFRoZSBRUS1wbG90IG9mIHRoZSByZXNpZHVhbHMgc2hvd3Mgbm8gZGV2aWF0aW9ucyBmcm9tIG5vcm1hbGl0eS4KCgpgYGB7cn0KbGlicmFyeShjYXIpCkFub3ZhKGZpdCx0eXBlPSJJSUkiKQpgYGAKClRoZSBvdXRwdXQgc2hvd3MgdGhhdCB0aGVyZSBpcyBhIHZlcnkgc2lnbmlmaWNhbnQgaW50ZXJhY3Rpb24gKCRwPSQgYHIgZm9ybWF0KEFub3ZhKGZpdCx0eXBlPSJJSUkiKVsiZ3JhZGU6bm9kZSIsNF0sZGlnaXRzPTIpYCkuIEhlbmNlLCB0aGUgYXNzb2NpYXRpb24gb2YgdGhlIGhpc3RvbG9naWNhbCBncmFkZSBvbiB0aGUgZ2VuZSBleHByZXNzaW9uIGRpZmZlcnMgYWNjb3JkaW5nIHRvIHRoZSBseW1waCBub2RlIHN0YXR1cyBhbmQgdmljZSB2ZXJzYS4KCgpUaGUgcmVzZWFyY2hlcnMgYXJlIHRoZXJlZm9yZSBpbnRlcmVzdGVkIGluIHN0dWR5aW5nIGFuZCByZXBvcnRpbmcgb24gdGhlIGZvbGxvd2luZyBoeXBvdGhlc2VzOgoKLSBJcyB0aGUgS1BOQTIgZXhwcmVzc2lvbiBvbiBhdmVyYWdlIGRpZmZlcmVudCBiZXR3ZWVuIGdyYWRlIDMgYW5kIGdyYWRlIDEgdHVtb3JzIGZyb20gcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGx5bXBoIG5vZGVzIChieSB0ZXN0aW5nICRIXzA6IFxsb2dfMntGQ31fe2czbjAtZzFuMH09MFx0ZXh0eyB2cyB9SDE6IFxsb2dfMntGQ31fe2czbjAtZzFuMH1cbmVxIDAkKQotIElzIHRoZSBLUE5BMiBleHByZXNzaW9uIG9uIGF2ZXJhZ2UgZGlmZmVyZW50IGJldHdlZW4gZ3JhZGUgMyBhbmQgZ3JhZGUgMSB0dW1vcnMgZnJvbSBwYXRpZW50cyB3aXRoIGFmZmVjdGVkIGx5bXBoIG5vZGVzIChieSB0ZXN0aW5nICRIXzA6IFxsb2dfMntGQ31fe2czbjEtZzFuMX09MFx0ZXh0eyB2cyB9SDE6IFxsb2dfMntGQ31fe2czbjEtZzFuMX1cbmVxIDAkKQoKLSBJcyB0aGUgS1BOQTIgZXhwcmVzc2lvbiBvbiBhdmVyYWdlIGRpZmZlcmVudCBpbiBncmFkZSAxIHR1bW9ycyBvZiBwYXRpZW50cyB3aXRoIGFmZmVjdGVkIGFuZCBwYXRpZW50cyB3aXRoIHVuYWZmZWN0ZWQgbHltcGggbm9kZXMgKGJ5IHRlc3RpbmcgJEhfMDogXGxvZ18ye0ZDfV97ZzFuMS1nMW4wfT0wXHRleHR7IHZzIH1IMTogXGxvZ18ye0ZDfV97ZzFuMS1nMW4wfVxuZXEgMCQpCgotIElzIHRoZSBLUE5BMiBleHByZXNzaW9uIG9uIGF2ZXJhZ2UgZGlmZmVyZW50IGluIGdyYWRlIDMgdHVtb3JzIG9mIHBhdGllbnRzIHdpdGggYWZmZWN0ZWQgYW5kIHBhdGllbnRzIHdpdGggdW5hZmZlY3RlZCBseW1waCBub2RlcyAoYnkgdGVzdGluZyAkSF8wOiBcbG9nXzJ7RkN9X3tnM24xLWczbjB9PTBcdGV4dHsgdnMgfUgxOiBcbG9nXzJ7RkN9X3tnM24xLWczbjB9XG5lcSAwJCkKCi0gSXMgdGhlIGZvbGQgY2hhbmdlIG9mIHRoZSBLUE5BMiBnZW5lIGJldHdlZW4gZ3JhZGUgMyBhbmQgZ3JhZGUgMSBkaWZmZXJlbnQgYWNjb3JkaW5nIHRvIHRoZSBseW1waCBub2RlIHN0YXR1cyBhbmQgdmljZSB2ZXJzYSAodGVzdGVkIGFscmVhZHkgYnkgYXNzZXNzaW5nIHRoZSBpbnRlcmFjdGlvbjogJEhfMDogXGxvZ18ye0ZDfV97ZzNuMC1nMW4wfT1cbG9nXzJ7RkN9X3tnM24xLWcxbjF9IFx0ZXh0eyB2cyB9SDE6XGxvZ18ye0ZDfV97ZzNuMC1nMW4wfVxuZXFcbG9nXzJ7RkN9X3tnM24xLWcxbjF9JCkuCgoKCiMgSW50ZXJwcmV0YXRpb24gb2YgbW9kZWwgcGFyYW1ldGVycyBhbmQgc3RhdGlzdGljYWwgdGVzdHMKCmBgYHtyfQpFeHBsb3JlTW9kZWxNYXRyaXg6OlZpc3VhbGl6ZURlc2lnbihrcG5hMix+Z3JhZGUqbm9kZSkkcGxvdGxpc3QKYGBgCgoKYGBge3J9CnN1bW1hcnkoZml0KQojQ2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciBwYXJhbWV0ZXJzIG9mIG1vZGVsCkNJZml0IDwtIGNvbmZpbnQoZml0KQojbG9nXzIgRkMgYmV0d2VlbiBnM24wLWcxbjAsIGcxbjEtZzFuMAojYW5kIGxvZ18yIGRpZmZlcmVuY2UgaW4gRkMgZzNuMS1nMW4xIGFuZCBGQyBnM24wLWcxbjAKQ0lmaXQKI1RyYW5zZm9ybSBwYXJhbWV0ZXJzIGFuZCB0aGUgQ0kgYmFjayB0byB0aGUgb3JpZ2luYWwgc2NhbGUKMl5maXQkY29lZgoyXkNJZml0CjJeLWZpdCRjb2VmWyJncmFkZTM6bm9kZTEiXQoyXi1DSWZpdFsiZ3JhZGUzOm5vZGUxIixdCmBgYAoKV2UgbW9kZWwgdGhlIGxvZyRfMiQtdHJhbnNmb3JtZWQgaW50ZW5zaXRpZXMgd2l0aCB0aGUgZm9sbG93aW5nIG1vZGVsOiAKJCQKeT1cYmV0YV8wK1xiZXRhX3tnM314X3tnM30rXGJldGFfe24xfXhfe24xfStcYmV0YV97ZzNuMX14X3tnM314X3tuMX0sCiQkCgp3aXRoICRcYmV0YV8wJCB0aGUgaW50ZXJjZXB0LCAkXGJldGFfe2czfSQgdGhlIG1haW4gZWZmZWN0IGZvciBncmFkZSwgJHhfe2czfSQgYSBkdW1teSB2YXJpYWJsZSBmb3IgZ3JhZGUgd2hpY2ggaXMgMCBmb3IgdGhlIGNvbnRyb2wgdHJlYXRtZW50IGluIHRoZSBhYnNlbmNlIG9mIGdyYWRlIGFuZCAxIGZvciB0aGUgdHJlYXRtZW50IHdpdGggZ3JhZGUsICRcYmV0YV97bjF9JCB0aGUgbWFpbiBlZmZlY3QgZm9yIG5vZGUsICR4X3tuMX0kIGEgZHVtbXkgdmFyaWFibGUgdGhhdCBpcyAwIGZvciB0aGUgbWVhc3VyZW1lbnRzIG9mIHBhdGllbnRzIHdpdGggdW5hZmZlY3RlZCBseW1waCBub2RlcyBhbmQgMSBmb3IgcGF0aWVudHMgZm9yIHdoaWNoIHRoZSBseW1waCBub2RlcyB3ZXJlIHJlbW92ZWQgYW5kICRcYmV0YV97ZzNuMX0kIHRoZSBpbnRlcmFjdGlvbiBlZmZlY3QgYmV0d2VlbiBncmFkZSBhbmQgbm9kZS4gClRvIGVhc2UgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzLCAkXGxvZ18yJCB0cmFuc2Zvcm1lZCBnZW9tZXRyaWMgbWVhbiBpbnRlbnNpdGllcyBhcmUgZ2l2ZW4gZm9yIGVhY2ggdHJlYXRtZW50IGdyb3VwIGFzIHdlbGwgYXMgY29ycmVzcG9uZGluZyBjb250cmFzdHMgYmV0d2VlbiB0cmVhdG1lbnRzLCB3aGljaCBoYXZlIGFuIGludGVycHJldGF0aW9uIGluIHRlcm1zIG9mICRcbG9nXzIkIHRyYW5zZm9ybWVkIGZvbGQgY2hhbmdlcyAoRkMpLiAKCi0gJFxsb2dfMlxoYXR7XG11fV97ZzFuMH09XGhhdFxiZXRhXzAkLCAkXGxvZ18yIFxoYXR7XG11fV97ZzNuMH09XGhhdFxiZXRhXzArXGhhdFxiZXRhX3tnM30kIC0tPiAkXGxvZ18yIFx3aWRlaGF0e0ZDfV97ZzNuMC1nMW4wfT1caGF0XGJldGFfe2czfSQKCi0gJFxsb2dfMiBcaGF0e1xtdX1fe2cxbjF9PVxoYXRcYmV0YV8wK1xoYXRcYmV0YV97bjF9JCwgJFxsb2dfMiBcaGF0IHtcbXV9X3tnM24xfT1caGF0XGJldGFfMCtcaGF0XGJldGFfe2czfStcaGF0XGJldGFfe24xfStcaGF0XGJldGFfe2czbjF9JCAtLT4gJFxsb2dfMiBcd2lkZWhhdHtGQ31fe2czbjEtZzFuMX09XGhhdCBcYmV0YV97ZzN9ICtcaGF0XGJldGFfe2czbjF9JAoKLSBTaW1pbGFybHksICRcbG9nXzIgXHdpZGVoYXR7RkN9X3tnMW4xLWcxbjB9PVxoYXRcYmV0YV97bjF9JCwgJFxsb2dfMiBcd2lkZWhhdHtGQ31fe2czbjEtZzNuMH09XGhhdFxiZXRhX3tuMX0rXGhhdFxiZXRhX3tnM24xfSQgCgotICRcbG9nXzJcZnJhY3tcd2lkZWhhdHtGQ31fe2czbjEtZzFuMX19e1x3aWRlaGF0e0ZDfV97ZzNuMC1nMW4wfX09XGxvZ18yXGZyYWN7XHdpZGVoYXR7RkN9X3tnM24xLWczbjB9fXtcd2lkZWhhdHtGQ31fe2cxbjEtZzFuMH19PVxoYXRcYmV0YV97ZzNuMX0kCgp3aXRoICRcbG9nXzJcaGF0e1xtdX1fe2cxbjB9JCwgJFxsb2dfMlxoYXR7XG11fV97ZzNuMH0kLCAkXGxvZ18yXGhhdCB7XG11fV97ZzFuMX0kIGFuZCAkXGxvZ18yXGhhdHtcbXV9X3tnM24xfSQgdGhlIGVzdGltYXRlZCBtZWFuICRcbG9nXzIkIHRyYW5zZm9ybWVkIGludGVuc2l0eSBmb3IgcGF0aWVudHMgd2l0aCBncmFkZSAxIGFuZCBub2RlIDAgc3RhdHVzLCBncmFkZSAzIGFuZCBub2RlIDAgc3RhdHVzLCBncmFkZSAxIGFuZCBub2RlIDEgc3RhdHVzIGFuZCBncmFkZSAzIGFuZCBub2RlIDEgc3RhdHVzLCByZXNwZWN0aXZlbHkuIFdpdGggJFxsb2dfMiBcd2lkZWhhdHtGQ31fe2ItYX0kIHdlIGluZGljYXRlICRcbG9nXzIkIHRyYW5zZm9ybWVkIGZvbGQgY2hhbmdlIGVzdGltYXRlcyBiZXR3ZWVuIHRyZWF0bWVudCBiIGFuZCB0cmVhdG1lbnQgYSwgaS5lLiAkXGxvZ18yIFx3aWRlaGF0e0ZDfV97Yi1hfT1cbG9nXzIgXGhhdHtcbXV9X3tifS1cbG9nXzIgXGhhdHtcbXV9X2E9XGxvZ18yIFxmcmFje1xoYXR7XG11fV97Yn19e1xoYXR7XG11fV97YX19JC4KClRoZSBtb2RlbCBpbW1lZGlhdGVseSBwcm92aWRlcyBzdGF0aXN0aWNhbCB0ZXN0cyBmb3IgYXNzZXNzaW5nIHRoZSBzaWduaWZpY2FuY2Ugb2YgZm9sZCBjaGFuZ2VzIGJldHdlZW4gZ3JhZGUgMyBhbmQgZ3JhZGUgMSBmb3IgcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGx5bXBoIG5vZGVzIChuPTApICRcbG9nXzIge0ZDfV97ZzNuMC1nMW4wfSQsICBmb2xkIGNoYW5nZXMgYmV0d2VlbiB0aGUgZ3JhZGUgMS1ub2RlIDEgcGF0aWVudHMgYW5kIGdyYWRlIDEtIG5vZGUgMCBwYXRpZW50cyAkXGxvZ18yIHtGQ31fe2cxbjEtZzNuMH0kIGFuZCBmb3IgZGlmZmVyZW5jZXMgaW4gZm9sZCBjaGFuZ2UgcmVsYXRlZCB0byBoaXN0b2xvZ2ljYWwgZ3JhZGUgZm9yIG5vZGUgMSBwYXRpZW50cyBhbmQgbm9kZSAwIHBhdGllbnRzLiAkXGxvZ18yXGZyYWN7e0ZDfV97ZzNuMS1nMW4xfX17e0ZDfV97ZzNuMC1nMW4wfX0kLCB0aGUgaW50ZXJhY3Rpb24gdGVybS4gCgpJbnRlcnByZXRhdGlvbiBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycyBpbiB0aGUgbW9kZWwgb3V0cHV0OgoKLSBUaGUgZ2VvbWV0cmljIG1lYW4gaW50ZW5zaXR5IGZvciBncmFkZSAxIHBhdGllbnRzIHdpdGggdW5hZmZlY3RlZCBseW1waCBub2RlcyBlcXVhbHMgJFxleHAoXGhhdCBcYmV0YV8wKSQ9CmByIHJvdW5kKDJeZml0JGNvZWZbIihJbnRlcmNlcHQpIl0sMilgLgoJLSBXaGVuIGx5bXBoIG5vZGVzIGFyZSB1bmFmZmVjdGVkLCB0aGUgZXhwcmVzc2lvbiBpcyBvbiBhdmVyYWdlIGByIHJvdW5kKDJeZml0JGNvZWZbImdyYWRlMyJdLDIpYCB0aW1lcyBoaWdoZXIgZm9yIHBhdGllbnRzIHdpdGggaGlzdG9sb2dpY2FsIGdyYWRlIDMgdGhhbiBwYXRpZW50cyB3aXRoIGhpc3RvbG9naWNhbCBncmFkZSAxLiAKCS0gVGhlIGdlbmUgZXhwcmVzc2lvbiBpbiBoaXN0b2xvZ2ljYWwgZ3JhZGUgMSBwYXRpZW50cyB3aXRoIGFmZmVjdGVkIGx5bXBoIG5vZGVzIGlzIG9uIGF2ZXJhZ2UgYHIgcm91bmQoMl5maXQkY29lZlsibm9kZTEiXSwyKWAgdGltZXMgaGlnaGVyIHRoYW4gZm9yIGdyYWRlIDEgcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGx5bXBoIG5vZGVzLgotIFRoZSBmb2xkIGNoYW5nZSBjb3JyZXNwb25kaW5nIHRvIGhpc3RvbG9naWNhbCBncmFkZSBpcyBvbiBhdmVyYWdlIGByIHJvdW5kKDEvMl5maXQkY29lZlsiZ3JhZGUzOm5vZGUxIl0sMilgIHRpbWVzIGxvd2VyIGluIHBhdGllbnRzIHdpdGggYWZmZWN0ZWQgbHltcGggbm9kZXMgYXMgY29tcGFyZWQgdG8gcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGx5bXBoIG5vZGUuCgoKRm9yIHRoZSByZW1haW5pbmcgaHlwb3RoZXNpcyBvZiBpbnRlcmVzdCB3ZSB3aWxsIGhhdmUgdG8gZGVmaW5lIGNvbnRyYXN0czogbGluZWFyIGNvbWJpbmF0aW9ucyBvZiB0aGUgbW9kZWwgcGFyYW1ldGVycyBhbmQgZXZhbHVhdGUgdGhlIGNvbnRyYXN0cyB3aXRoIHRoZSBtdWx0Y29tcCBwYWNrYWdlLgoKVGhlIEYtdGVzdCBzaG93ZWQgYW4gZXh0cmVtZWx5IHNpZ25pZmljYW50IGFzc29jaWF0aW9uIG9mIHRoZSBub2RlIHN0YXR1cywgaHlzdG9sb2dpY2FsIGdyYWRlIGFuZC9vciB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB0aGUgbm9kZSBzdGF0dXMgYW5kIHRoZSBncmFkZSAocDw8MC4wMDEpLiAKCiMgQXNzZXNzaW5nIHRoZSBzaWduaWZpY2FuY2Ugb2YgYWxsIGh5cG90aGVzaXMgb2YgaW50ZXJlc3QKV2UgY2FuIGFzc2VzcyBhbGwgY29udHJhc3RzIG9mIGludGVyZXN0IHVzaW5nIHRoZSBtdWx0Y29tcCBwYWNrYWdlLiBUaGlzIHdpbGwgYWxzbyBhbGxvdyB1cyB0byBjb3JyZWN0IGZvciBtdWx0aXBsZSB0ZXN0aW5nLCBzaW5jZSB3ZSBhc3Nlc3MgbXVsdGlwbGUgaHlwb3RoZXNlcyB0byBhbnN3ZXIgdGhlIHJlbGV2YW50IHJlc2VhcmNoIHF1ZXN0aW9uLgoKCi0gJEhfMDogXGxvZ18ye0ZDfV97ZzNuMC1nMW4wfT0gXGJldGFfe2czfT0wJCAkXHJpZ2h0YXJyb3ckICJncmFkZTMgPSAwIgotICRIXzA6IFxsb2dfMntGQ31fe2czbjEtZzFuMX09IFxiZXRhX3tnM30gKyBcaGF0XGJldGFfe2czbjF9PTAkICRccmlnaHRhcnJvdyQgImdyYWRlMytncmFkZTM6bm9kZTEgPSAwIgotICRIXzA6IFxsb2dfMntGQ31fe2cxbjEtZzFuMH09IFxiZXRhX3tuMX0kICAkXHJpZ2h0YXJyb3ckICJub2RlMSA9IDAiCi0gJEhfMDogXGxvZ18ye0ZDfV97ZzNuMS1nM24wfT0gXGJldGFfe24xfSArIFxoYXRcYmV0YV97ZzNuMX09MCQgJFxyaWdodGFycm93JCAibm9kZTErZ3JhZGUzOm5vZGUxID0gMCIKLSAkSF8wOiBcbG9nXzJ7RkN9X3tnM24xLWcxbjF9IC0gXGxvZ18ye0ZDfV97ZzNuMC1nMW4wfSA9IFxoYXRcYmV0YV97ZzNuMX09MCQsIG5vdGUgdGhhdCB0aGUgbGF0dGVyIGh5cG90aGVzaXMgaXMgYWxzbyBlcXVpdmFsZW50IHRvICRIXzA6IFxsb2dfMntGQ31fe2czbjEtZzNuMH0gLSBcbG9nXzJ7RkN9X3tnMW4xLWcxbjB9ID0gXGhhdFxiZXRhX3tnM24xfT0wJCAkXHJpZ2h0YXJyb3ckICJncmFkZTM6bm9kZTEgPSAwIgoKYGBge3J9CmxpYnJhcnkobXVsdGNvbXApCmZpdEdsaHQ8LSBnbGh0KGZpdCwgbGluZmN0ID0gYygiZ3JhZGUzID0gMCIsImdyYWRlMytncmFkZTM6bm9kZTEgPSAwIiwibm9kZTEgPSAwIiwibm9kZTErZ3JhZGUzOm5vZGUxID0gMCIsImdyYWRlMzpub2RlMSA9IDAiKSkKc3VtbWFyeShmaXRHbGh0KQpjb25maW50KGZpdEdsaHQpCjJeY29uZmludChmaXRHbGh0KSRjb25maW50CjJeLWNvbmZpbnQoZml0R2xodCkkY29uZmludFsiZ3JhZGUzOm5vZGUxIixdCmBgYAoKIyBDb25jbHVzaW9uCgotIFRoZXJlIGlzIGFuIGV4dHJlbWVseSBzaWduaWZpY2FudCBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSBLUE5BMiBleHByZXNzaW9uIGFuZCBoeXN0b2xvZ2ljYWwgZ3JhZGUgaW4gcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGFzIHdlbGwgYXMgaW4gcGF0aWVudHMgd2l0aCBhZmZlY3RlZCBseW1waCBub2RlcyAoYm90aCBwPDwwLjAwMSkuIAogIFdoZW4gbHltcGggbm9kZXMgYXJlIHVuYWZmZWN0ZWQsIHRoZSBleHByZXNzaW9uIGlzIG9uIGF2ZXJhZ2UgYHIgcm91bmQoMl5jb25maW50KGZpdEdsaHQpJGNvbmZpbnRbImdyYWRlMyIsMV0sMilgIHRpbWVzIGhpZ2hlciBmb3IgcGF0aWVudHMgd2l0aCBoaXN0b2xvZ2ljYWwgZ3JhZGUgMyB0aGFuIHBhdGllbnRzIHdpdGggaGlzdG9sb2dpY2FsIGdyYWRlIDEgKDk1JSBDSSBbYHIgcm91bmQoMl5jb25maW50KGZpdEdsaHQpJGNvbmZpbnRbImdyYWRlMyIsMjozXSwyKWBdKS4gCiAgRm9yIHBhdGllbnRzIHdpdGggYWZmZWN0ZWQgbHltcGggbm9kZXMgdGhlIGV4cHJlc3Npb24gaXMgb24gYXZlcmFnZSBgciByb3VuZCgyXmNvbmZpbnQoZml0R2xodCkkY29uZmludFsiZ3JhZGUzICsgZ3JhZGUzOm5vZGUxIiwxXSwyKWAgdGltZXMgaGlnaGVyIGZvciBwYXRpZW50cyB3aXRoIGhpc3RvbG9naWNhbCBncmFkZSAzIHR1bW9ycyB0aGFuIHBhdGllbnRzIHdpdGggaGlzdG9sb2dpY2FsIGdyYWRlIDEgdHVtb3JzICg5NSUgQ0kgW2ByIHJvdW5kKDJeY29uZmludChmaXRHbGh0KSRjb25maW50WyJncmFkZTMgKyBncmFkZTM6bm9kZTEiLDI6M10sMilgXSkuIAoKLSBUaGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgS1BOQTIgZXhwcmVzc2lvbiB3aXRoIHRoZSBseW1waCBub2RlIHN0YXR1cyBpbiBncmFkZSAxIHBhdGllbnRzIGlzIHZlcnkgc2lnbmlmaWNhbnQgKCRwPSQgYHIgZm9ybWF0KHN1bW1hcnkoZml0R2xodCkkdGVzdCRwdmFsdWVzWzNdLGRpZ2l0cz0yKWApLiAgClRoZSBLUE5BMiBleHByZXNzaW9uIGluIGhpc3RvbG9naWNhbCBncmFkZSAxIHBhdGllbnRzIHdpdGggYWZmZWN0ZWQgbHltcGggbm9kZXMgaXMgb24gYXZlcmFnZSBgciByb3VuZCgyXmNvbmZpbnQoZml0R2xodCkkY29uZmludFsibm9kZTEiLDFdLDIpYCB0aW1lcyBoaWdoZXIgdGhhbiBmb3IgZ3JhZGUgMSBwYXRpZW50cyB3aXRoIHVuYWZmZWN0ZWQgbHltcGggbm9kZXMgKDk1JSBDSSBbYHIgcm91bmQoMl5jb25maW50KGZpdEdsaHQpJGNvbmZpbnRbIm5vZGUxIiwyOjNdLDIpYF0pLiAKSW4gZ3JhZGUgMyBwYXRpZW50cywgaG93ZXZlciwgdGhpcyBhc3NvY2lhdGlvbiBpcyBub3Qgc2lnbmlmaWNhbnQgKCRwPSQgYHIgZm9ybWF0KHN1bW1hcnkoZml0R2xodCkkdGVzdCRwdmFsdWVzWzRdLGRpZ2l0cz0yKWAsIDk1JSBDSSBbYHIgcm91bmQoMl5jb25maW50KGZpdEdsaHQpJGNvbmZpbnRbIm5vZGUxICsgZ3JhZGUzOm5vZGUxIiwyOjNdLDIpYF0gKS4KCi0gVGhlcmUgaXMgYWxzbyBhIHNpZ25pZmljYW50IGludGVyYWN0aW9uIGJldHdlZW4gdGhlIGh5c3RvbG9naWNhbCBncmFkZSBhbmQgdGhlIGx5bXBoIG5vZGUgc3RhdHVzLiBTbyB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgS1BOQTIgZXhwcmVzc2lvbiBhbmQgdGhlIGhpc3RvbG9naWNhbCBncmFkZSBkZXBlbmRzIG9uIHRoZSBseW1waCBub2RlIHN0YXR1cyBhbmQgdmljZSB2ZXJzYSAoJHA9JCBgciBmb3JtYXQoc3VtbWFyeShmaXRHbGh0KSR0ZXN0JHB2YWx1ZXNbNV0sZGlnaXRzPTIpYCkuIFRoZSBmb2xkIGNoYW5nZSBjb3JyZXNwb25kaW5nIHRvIGhpc3RvbG9naWNhbCBncmFkZSBpcyBvbiBhdmVyYWdlIGByIHJvdW5kKDEvMl5jb25maW50KGZpdEdsaHQpJGNvbmZpbnRbImdyYWRlMzpub2RlMSIsMV0sMilgIHRpbWVzIGxvd2VyIGluIHBhdGllbnRzIHdpdGggYWZmZWN0ZWQgbHltcGggbm9kZXMgYXMgY29tcGFyZWQgdG8gcGF0aWVudHMgd2l0aCB1bmFmZmVjdGVkIGx5bXBoIG5vZGUgKDk1JSBDSSBbYHIgcm91bmQoMS8yXmNvbmZpbnQoZml0R2xodCkkY29uZmludFsiZ3JhZGUzOm5vZGUxIiwzOjJdLDIpYF0pLiAoU2ltaWxhcmx5LCB0aGUgZm9sZCBjaGFuZ2UgY29ycmVzcG9uZGluZyB0byB0aGUgbm9kZSBzdGF0dXMgaXMgb24gYXZlcmFnZSBgciByb3VuZCgxLzJeY29uZmludChmaXRHbGh0KSRjb25maW50WyJncmFkZTM6bm9kZTEiLDFdLDIpYCB0aW1lcyBsb3dlciBpbiBwYXRpZW50cyB3aXRoIGdyYWRlIDMgdHVtb3JzIGFzIGNvbXBhcmVkIHRvIHBhdGllbnRzIHdpdGggZ3JhZGUgMSB0dW1vcnMsIDk1JSBDSSBbYHIgcm91bmQoMS8yXmNvbmZpbnQoZml0R2xodCkkY29uZmludFsiZ3JhZGUzOm5vZGUxIiwzOjJdLDIpYF0pCgo=