Creative Commons License

library(ggplot2)
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.1 ──
## ✔ tibble  3.1.4     ✔ dplyr   1.0.7
## ✔ tidyr   1.1.3     ✔ stringr 1.4.0
## ✔ readr   1.4.0     ✔ forcats 0.5.1
## ✔ purrr   0.3.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

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 chirugically removed (1).

  • Redo data analysis (you can copy the results of the tutorial on multiple linear regression)
  • What is the power to pick up each of the contrasts when their real effect sizes would be equal to the effect sizes we observed in the study?
  • How does the power evolves if we have 2 upto 10 repeats for each factor combination of grade and node when their real effect sizes would be equal to the ones we observed in the study?
  • What is the power to pick up each of the contrasts when the FC for grade for patients with unaffected lymf nodes equals 1.5 (\(\beta_g = log2(1.5)\))?

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

Because histolic grade and lymph node status are both categorical variables, we model them both as factors.

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

As discussed in a previous exercise, it seems that there is both an effect of histologic grade and lymph node status on the gene expression. There also seems to be a different effect of lymph node status on the gene expression for the different histologic grades.

As we saw before, we can model this with a model that contains both histologic grade, lymph node status and the interaction term between both. When checking the linear model assumptions, we see that the variance is not equal. Therefore we model the gene expression with a log2-transformation, which makes that all the assumptions of the linear model are satisfied.

#Model with main effects for histological grade and node and grade x node interaction

Check if the interaction term is significant:

library(car)

As we are dealing with a factorial design, we can calculate the mean gene expression for each group by the following parameter summations.

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

The researchers want to know the power for testing following hypotheses (remark that we will have to adjust for multiple testing):

  • Log fold change between histologic grade 3 and histologic grade 1 for patients with unaffected lymph nodes (=0).

\[H_0: \log_2{FC}_{g3n0-g1n0} = \beta_{g3} = 0\]

  • Log fold change between histologic grade 3 and histologic grade 1 for patients with removed lymph nodes (=1).

\[H_0: \log_2{FC}_{g3n1-g1n1} = \beta_{g3} + \beta_{g3n1} = 0\]

  • Log fold change between unaffected and removed lymph nodes for patients of histologic grade 1.

\[H_0: \log_2{FC}_{g1n1-g1n0} = \beta_{n1} = 0\]

  • Log fold change between unaffected and removed lymph nodes for patients of histologic grade 3.

\[H_0: \log_2{FC}_{g3n1-g3n0} = \beta_{n1} + \beta_{g3n1} = 0\]

  • Difference in log fold change between patients of histological grade 3 and histological grade 1 with removed lymph nodes and log fold change between patients between patients of histological grade 3 and histological grade 1 with unaffected lymph nodes.

\[H_0: \log_2{FC}_{g3n1-g1n1} - \log_2{FC}_{g3n0-g1n0} = \beta_{g3n1} = 0\] which is an equivalent hypotheses with \[H_0: \log_2{FC}_{g3n1-g3n0} - \log_2{FC}_{g1n1-g1n0} = \beta_{g3n1} = 0\]

We can test this using multcomp, which controls for multiple testing.

library(multcomp)

We get a significant p-value for the first, second, third and fifth hypothesis. The fourth hypothesis is not significant at the overall 5% significance level.

3 Power of the tests for each of the contrasts

3.1 Simulation function

Function to simulate data similar to that of our experiment under our model assumptions.

simFastMultipleContrasts <- function(form, data, betas, sd, contrasts, alpha = .05, nSim = 10000, adjust = "bonferroni")
{
    ySim <- rnorm(nrow(data)*nSim,sd=sd)
    dim(ySim) <-c(nrow(data),nSim)
    design <- model.matrix(form, data)
    ySim <- ySim + c(design %*%betas)
    ySim <- t(ySim)
  
    ### Fitting
    fitAll <- limma::lmFit(ySim,design)
  
    ### Inference
    varUnscaled <- t(contrasts)%*%fitAll$cov.coefficients%*%contrasts
    contrasts <- fitAll$coefficients %*%contrasts
    seContrasts <- matrix(diag(varUnscaled)^.5,nrow=nSim,ncol=5,byrow = TRUE)*fitAll$sigma
    tstats <- contrasts/seContrasts
    pvals <- pt(abs(tstats),fitAll$df.residual,lower.tail = FALSE)*2
    pvals <- t(apply(pvals, 1, p.adjust, method = adjust))
    
    return(colMeans(pvals < alpha))
}

3.2 power of current experiment

power1 <- simFastMultipleContrasts(form = ...,
                                   data = ...,
                                   betas = ..., 
                                   sd = ...,
                                   contrasts = ...,
                                   alpha = ...,
                                   nSim = ...)
power1

We observe large powers for all contrasts, except for contrast nodeg3, which has a small effect size.

3.3 Power for increasing sample size

powers <- matrix(NA,nrow=9, ncol=6)
colnames(powers) <- c("n",colnames(contrasts))
powers[,1] <- 2:10

# Zorg hier voor 1 designpunt (of observatie) per groep. In de for-loop gaan we deze designpunten herhalen voor het aantal observaties.
dataAllComb <- data.frame(grade = ...,
                          node = ...) 
  
for (i in 1:nrow(powers))
{  
predData <- data.frame(grade = rep(dataAllComb$grade, powers[i,1]), 
                       node = rep(dataAllComb$node, powers[i,1]))

powers[i,-1] <- simFastMultipleContrasts(form = ...,
                                   data = predData,
                                   betas = ..., 
                                   sd = ...,
                                   contrasts = ...,
                                   alpha = ...,
                                   nSim = ...)
}
powers
powers %>% 
  as.data.frame %>% 
  gather(contrast, power, -n) %>% 
  ggplot(aes(n,power,color=contrast)) +
  geom_line()

3.4 Power when FC for grade in patients with unaffected lymph nodes equals 1.5 (\(\beta_g = log2(1.5)\))

power3 <- simFastMultipleContrasts(form = ...,
                                   data = ...,
                                   betas = ..., 
                                   sd = ...,
                                   contrasts = ...,
                                   alpha = ...,
                                   nSim = ...)
power3
LS0tCnRpdGxlOiAiRXhwZXJpbWVudGVlbCBEZXNpZ24gSUk6IHJlcGxpY2F0aWUgZW4gcG93ZXIgLSBLUE5BMiIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQgJiBBbGV4YW5kcmUgU2VnZXJzIgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCi0tLQoKCjxhIHJlbD0ibGljZW5zZSIgaHJlZj0iaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMCI+PGltZyBhbHQ9IkNyZWF0aXZlIENvbW1vbnMgTGljZW5zZSIgc3R5bGU9ImJvcmRlci13aWR0aDowIiBzcmM9Imh0dHBzOi8vaS5jcmVhdGl2ZWNvbW1vbnMub3JnL2wvYnktbmMtc2EvNC4wLzg4eDMxLnBuZyIgLz48L2E+CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBCYWNrZ3JvdW5kCgpIaXN0b2xvZ2ljIGdyYWRlIGluIGJyZWFzdCBjYW5jZXIgcHJvdmlkZXMgY2xpbmljYWxseSBpbXBvcnRhbnQgcHJvZ25vc3RpYyBpbmZvcm1hdGlvbi4gUmVzZWFyY2hlcnMgZXhhbWluZWQgd2hldGhlciBoaXN0b2xvZ2ljIGdyYWRlIHdhcyBhc3NvY2lhdGVkIHdpdGggZ2VuZSBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGJyZWFzdCBjYW5jZXJzIGFuZCB3aGV0aGVyIHN1Y2ggcHJvZmlsZXMgY291bGQgYmUgdXNlZCB0byBpbXByb3ZlIGhpc3RvbG9naWMgZ3JhZGluZy4gSW4gdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIGFzc2VzcyB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiBoaXN0b2xvZ2ljIGdyYWRlIGFuZCB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgS1BOQTIgZ2VuZSB0aGF0IGlzIGtub3duIHRvIGJlIGFzc29jaWF0ZWQgd2l0aCBwb29yIEJDIHByb2dub3Npcy4KVGhlIHBhdGllbnRzLCBob3dldmVyLCBkbyBub3Qgb25seSBkaWZmZXIgaW4gdGhlIGhpc3RvbG9naWMgZ3JhZGUsIGJ1dCBhbHNvIG9uIHRoZWlyIGx5bXBoIG5vZGUgc3RhdHVzLiAKVGhlIGx5bXBoIG5vZGVzIHdlcmUgbm90IGFmZmVjdGVkICgwKSBvciBjaGlydWdpY2FsbHkgcmVtb3ZlZCAoMSkuCgotIFJlZG8gZGF0YSBhbmFseXNpcyAoeW91IGNhbiBjb3B5IHRoZSByZXN1bHRzIG9mIHRoZSB0dXRvcmlhbCBvbiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbikKLSBXaGF0IGlzIHRoZSBwb3dlciB0byBwaWNrIHVwIGVhY2ggb2YgdGhlIGNvbnRyYXN0cyB3aGVuIHRoZWlyIHJlYWwgZWZmZWN0IHNpemVzIHdvdWxkIGJlIGVxdWFsIHRvIHRoZSBlZmZlY3Qgc2l6ZXMgd2Ugb2JzZXJ2ZWQgaW4gdGhlIHN0dWR5PyAKLSBIb3cgZG9lcyB0aGUgcG93ZXIgZXZvbHZlcyBpZiB3ZSBoYXZlIDIgdXB0byAxMCByZXBlYXRzIGZvciBlYWNoIGZhY3RvciBjb21iaW5hdGlvbiBvZiBncmFkZSBhbmQgbm9kZSB3aGVuIHRoZWlyIHJlYWwgZWZmZWN0IHNpemVzIHdvdWxkIGJlIGVxdWFsIHRvIHRoZSBvbmVzIHdlIG9ic2VydmVkIGluIHRoZSBzdHVkeT8gCi0gV2hhdCBpcyB0aGUgcG93ZXIgdG8gcGljayB1cCBlYWNoIG9mIHRoZSBjb250cmFzdHMgd2hlbiB0aGUgRkMgZm9yIGdyYWRlIGZvciBwYXRpZW50cyB3aXRoIHVuYWZmZWN0ZWQgbHltZiBub2RlcyBlcXVhbHMgMS41ICgkXGJldGFfZyA9IGxvZzIoMS41KSQpPyAKCgojIERhdGEgYW5hbHlzaXMKIyMgSW1wb3J0IEtQTkEyIGRhdGEgaW4gUgpgYGB7cn0Ka3BuYTIgPC0gcmVhZC50YWJsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9TR0EyMS9tYXN0ZXIvZGF0YS9rcG5hMi50eHQiLGhlYWRlcj1UUlVFKQprcG5hMgpgYGAKCkJlY2F1c2UgaGlzdG9saWMgZ3JhZGUgYW5kIGx5bXBoIG5vZGUgc3RhdHVzIGFyZSBib3RoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgd2UgbW9kZWwgdGhlbSBib3RoIGFzIGZhY3RvcnMuCgpgYGB7cixldmFsPUZBTFNFfQprcG5hMiRncmFkZSA8LSAuLi4Ka3BuYTIkbm9kZSA8LSAuLi4KYGBgCgpgYGB7cixldmFsPUZBTFNFfQprcG5hMiAlPiUgCiAgZ2dwbG90KGFlcyh4PW5vZGU6Z3JhZGUseT1nZW5lLGZpbGw9bm9kZTpncmFkZSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9qaXR0ZXIoKQpgYGAKCkFzIGRpc2N1c3NlZCBpbiBhIHByZXZpb3VzIGV4ZXJjaXNlLCBpdCBzZWVtcyB0aGF0IHRoZXJlIGlzIGJvdGggYW4gZWZmZWN0IG9mIGhpc3RvbG9naWMgZ3JhZGUgYW5kIGx5bXBoIG5vZGUgc3RhdHVzIG9uIHRoZSBnZW5lIGV4cHJlc3Npb24uIFRoZXJlIGFsc28gc2VlbXMgdG8gYmUgYSBkaWZmZXJlbnQgZWZmZWN0IG9mIGx5bXBoIG5vZGUgc3RhdHVzIG9uIHRoZSBnZW5lIGV4cHJlc3Npb24gZm9yIHRoZSBkaWZmZXJlbnQgaGlzdG9sb2dpYyBncmFkZXMuCgpBcyB3ZSBzYXcgYmVmb3JlLCB3ZSBjYW4gbW9kZWwgdGhpcyB3aXRoIGEgbW9kZWwgdGhhdCBjb250YWlucyBib3RoIGhpc3RvbG9naWMgZ3JhZGUsIGx5bXBoIG5vZGUgc3RhdHVzIGFuZCB0aGUgaW50ZXJhY3Rpb24gdGVybSBiZXR3ZWVuIGJvdGguIFdoZW4gY2hlY2tpbmcgdGhlIGxpbmVhciBtb2RlbCBhc3N1bXB0aW9ucywgd2Ugc2VlIHRoYXQgdGhlIHZhcmlhbmNlIGlzIG5vdCBlcXVhbC4gVGhlcmVmb3JlIHdlIG1vZGVsIHRoZSBnZW5lIGV4cHJlc3Npb24gd2l0aCBhIGxvZzItdHJhbnNmb3JtYXRpb24sIHdoaWNoIG1ha2VzIHRoYXQgYWxsIHRoZSBhc3N1bXB0aW9ucyBvZiB0aGUgbGluZWFyIG1vZGVsIGFyZSBzYXRpc2ZpZWQuCgoKYGBge3IsZXZhbD1GQUxTRX0KI01vZGVsIHdpdGggbWFpbiBlZmZlY3RzIGZvciBoaXN0b2xvZ2ljYWwgZ3JhZGUgYW5kIG5vZGUgYW5kIGdyYWRlIHggbm9kZSBpbnRlcmFjdGlvbgoKYGBgCgoKQ2hlY2sgaWYgdGhlIGludGVyYWN0aW9uIHRlcm0gaXMgc2lnbmlmaWNhbnQ6CgpgYGB7cixldmFsPUZBTFNFfQpsaWJyYXJ5KGNhcikKCmBgYAoKQXMgd2UgYXJlIGRlYWxpbmcgd2l0aCBhIGZhY3RvcmlhbCBkZXNpZ24sIHdlIGNhbiBjYWxjdWxhdGUgdGhlIG1lYW4gZ2VuZSBleHByZXNzaW9uIGZvciBlYWNoIGdyb3VwIGJ5IHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyIHN1bW1hdGlvbnMuCgpgYGB7cixldmFsPUZBTFNFfQpFeHBsb3JlTW9kZWxNYXRyaXg6OlZpc3VhbGl6ZURlc2lnbihrcG5hMix+Z3JhZGUqbm9kZSkkcGxvdGxpc3QKYGBgCgpUaGUgcmVzZWFyY2hlcnMgd2FudCB0byBrbm93IHRoZSBwb3dlciBmb3IgdGVzdGluZyBmb2xsb3dpbmcgaHlwb3RoZXNlcyAocmVtYXJrIHRoYXQgd2Ugd2lsbCBoYXZlIHRvIGFkanVzdCBmb3IgbXVsdGlwbGUgdGVzdGluZyk6CgotIExvZyBmb2xkIGNoYW5nZSBiZXR3ZWVuIGhpc3RvbG9naWMgZ3JhZGUgMyBhbmQgaGlzdG9sb2dpYyBncmFkZSAxIGZvciBwYXRpZW50cyB3aXRoIHVuYWZmZWN0ZWQgbHltcGggbm9kZXMgKD0wKS4KCiQkSF8wOiBcbG9nXzJ7RkN9X3tnM24wLWcxbjB9ID0gXGJldGFfe2czfSA9IDAkJAoKLSBMb2cgZm9sZCBjaGFuZ2UgYmV0d2VlbiBoaXN0b2xvZ2ljIGdyYWRlIDMgYW5kIGhpc3RvbG9naWMgZ3JhZGUgMSBmb3IgcGF0aWVudHMgd2l0aCByZW1vdmVkIGx5bXBoIG5vZGVzICg9MSkuIAoKJCRIXzA6IFxsb2dfMntGQ31fe2czbjEtZzFuMX0gPSBcYmV0YV97ZzN9ICsgXGJldGFfe2czbjF9ID0gMCQkCgotIExvZyBmb2xkIGNoYW5nZSBiZXR3ZWVuIHVuYWZmZWN0ZWQgYW5kIHJlbW92ZWQgbHltcGggbm9kZXMgZm9yIHBhdGllbnRzIG9mIGhpc3RvbG9naWMgZ3JhZGUgMS4KCiQkSF8wOiBcbG9nXzJ7RkN9X3tnMW4xLWcxbjB9ID0gXGJldGFfe24xfSA9IDAkJAoKLSBMb2cgZm9sZCBjaGFuZ2UgYmV0d2VlbiB1bmFmZmVjdGVkIGFuZCByZW1vdmVkIGx5bXBoIG5vZGVzIGZvciBwYXRpZW50cyBvZiBoaXN0b2xvZ2ljIGdyYWRlIDMuCgokJEhfMDogXGxvZ18ye0ZDfV97ZzNuMS1nM24wfSA9IFxiZXRhX3tuMX0gKyBcYmV0YV97ZzNuMX0gPSAwJCQKCi0gRGlmZmVyZW5jZSBpbiBsb2cgZm9sZCBjaGFuZ2UgYmV0d2VlbiBwYXRpZW50cyBvZiBoaXN0b2xvZ2ljYWwgZ3JhZGUgMyBhbmQgaGlzdG9sb2dpY2FsIGdyYWRlIDEgd2l0aCByZW1vdmVkIGx5bXBoIG5vZGVzIGFuZCBsb2cgZm9sZCBjaGFuZ2UgYmV0d2VlbiBwYXRpZW50cyBiZXR3ZWVuIHBhdGllbnRzIG9mIGhpc3RvbG9naWNhbCBncmFkZSAzIGFuZCBoaXN0b2xvZ2ljYWwgZ3JhZGUgMSB3aXRoIHVuYWZmZWN0ZWQgbHltcGggbm9kZXMuCgokJEhfMDogXGxvZ18ye0ZDfV97ZzNuMS1nMW4xfSAtIFxsb2dfMntGQ31fe2czbjAtZzFuMH0gPSBcYmV0YV97ZzNuMX0gPSAwJCQgd2hpY2ggaXMgYW4gZXF1aXZhbGVudCBoeXBvdGhlc2VzIHdpdGggJCRIXzA6IFxsb2dfMntGQ31fe2czbjEtZzNuMH0gLSBcbG9nXzJ7RkN9X3tnMW4xLWcxbjB9ID0gXGJldGFfe2czbjF9ID0gMCQkCgpXZSBjYW4gdGVzdCB0aGlzIHVzaW5nIG11bHRjb21wLCB3aGljaCBjb250cm9scyBmb3IgbXVsdGlwbGUgdGVzdGluZy4KCmBgYHtyLGV2YWw9RkFMU0V9CmxpYnJhcnkobXVsdGNvbXApCgpgYGAKCldlIGdldCBhIHNpZ25pZmljYW50IHAtdmFsdWUgZm9yIHRoZSBmaXJzdCwgc2Vjb25kLCB0aGlyZCBhbmQgZmlmdGggaHlwb3RoZXNpcy4gVGhlIGZvdXJ0aCBoeXBvdGhlc2lzIGlzIG5vdCBzaWduaWZpY2FudCBhdCB0aGUgb3ZlcmFsbCA1JSBzaWduaWZpY2FuY2UgbGV2ZWwuCgojIFBvd2VyIG9mIHRoZSB0ZXN0cyBmb3IgZWFjaCBvZiB0aGUgY29udHJhc3RzCgojIyBTaW11bGF0aW9uIGZ1bmN0aW9uCgpGdW5jdGlvbiB0byBzaW11bGF0ZSBkYXRhIHNpbWlsYXIgdG8gdGhhdCBvZiBvdXIgZXhwZXJpbWVudCB1bmRlciBvdXIgbW9kZWwgYXNzdW1wdGlvbnMuIAoKYGBge3IsZXZhbD1GQUxTRX0Kc2ltRmFzdE11bHRpcGxlQ29udHJhc3RzIDwtIGZ1bmN0aW9uKGZvcm0sIGRhdGEsIGJldGFzLCBzZCwgY29udHJhc3RzLCBhbHBoYSA9IC4wNSwgblNpbSA9IDEwMDAwLCBhZGp1c3QgPSAiYm9uZmVycm9uaSIpCnsKICAgIHlTaW0gPC0gcm5vcm0obnJvdyhkYXRhKSpuU2ltLHNkPXNkKQogICAgZGltKHlTaW0pIDwtYyhucm93KGRhdGEpLG5TaW0pCiAgICBkZXNpZ24gPC0gbW9kZWwubWF0cml4KGZvcm0sIGRhdGEpCiAgICB5U2ltIDwtIHlTaW0gKyBjKGRlc2lnbiAlKiViZXRhcykKICAgIHlTaW0gPC0gdCh5U2ltKQogIAogICAgIyMjIEZpdHRpbmcKICAgIGZpdEFsbCA8LSBsaW1tYTo6bG1GaXQoeVNpbSxkZXNpZ24pCiAgCiAgICAjIyMgSW5mZXJlbmNlCiAgICB2YXJVbnNjYWxlZCA8LSB0KGNvbnRyYXN0cyklKiVmaXRBbGwkY292LmNvZWZmaWNpZW50cyUqJWNvbnRyYXN0cwogICAgY29udHJhc3RzIDwtIGZpdEFsbCRjb2VmZmljaWVudHMgJSolY29udHJhc3RzCiAgICBzZUNvbnRyYXN0cyA8LSBtYXRyaXgoZGlhZyh2YXJVbnNjYWxlZCleLjUsbnJvdz1uU2ltLG5jb2w9NSxieXJvdyA9IFRSVUUpKmZpdEFsbCRzaWdtYQogICAgdHN0YXRzIDwtIGNvbnRyYXN0cy9zZUNvbnRyYXN0cwogICAgcHZhbHMgPC0gcHQoYWJzKHRzdGF0cyksZml0QWxsJGRmLnJlc2lkdWFsLGxvd2VyLnRhaWwgPSBGQUxTRSkqMgogICAgcHZhbHMgPC0gdChhcHBseShwdmFscywgMSwgcC5hZGp1c3QsIG1ldGhvZCA9IGFkanVzdCkpCiAgICAKICAgIHJldHVybihjb2xNZWFucyhwdmFscyA8IGFscGhhKSkKfQpgYGAKCiMjIHBvd2VyIG9mIGN1cnJlbnQgZXhwZXJpbWVudAoKYGBge3IsZXZhbD1GQUxTRX0KcG93ZXIxIDwtIHNpbUZhc3RNdWx0aXBsZUNvbnRyYXN0cyhmb3JtID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0YXMgPSAuLi4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRyYXN0cyA9IC4uLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IC4uLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuU2ltID0gLi4uKQpwb3dlcjEKYGBgCgpXZSBvYnNlcnZlIGxhcmdlIHBvd2VycyBmb3IgYWxsIGNvbnRyYXN0cywgZXhjZXB0IGZvciBjb250cmFzdCBub2RlZzMsIHdoaWNoIGhhcyBhIHNtYWxsIGVmZmVjdCBzaXplLiAKCiMjIFBvd2VyIGZvciBpbmNyZWFzaW5nIHNhbXBsZSBzaXplCgpgYGB7cixldmFsPUZBTFNFfQoKcG93ZXJzIDwtIG1hdHJpeChOQSxucm93PTksIG5jb2w9NikKY29sbmFtZXMocG93ZXJzKSA8LSBjKCJuIixjb2xuYW1lcyhjb250cmFzdHMpKQpwb3dlcnNbLDFdIDwtIDI6MTAKCiMgWm9yZyBoaWVyIHZvb3IgMSBkZXNpZ25wdW50IChvZiBvYnNlcnZhdGllKSBwZXIgZ3JvZXAuIEluIGRlIGZvci1sb29wIGdhYW4gd2UgZGV6ZSBkZXNpZ25wdW50ZW4gaGVyaGFsZW4gdm9vciBoZXQgYWFudGFsIG9ic2VydmF0aWVzLgpkYXRhQWxsQ29tYiA8LSBkYXRhLmZyYW1lKGdyYWRlID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5vZGUgPSAuLi4pIAogIApmb3IgKGkgaW4gMTpucm93KHBvd2VycykpCnsgIApwcmVkRGF0YSA8LSBkYXRhLmZyYW1lKGdyYWRlID0gcmVwKGRhdGFBbGxDb21iJGdyYWRlLCBwb3dlcnNbaSwxXSksIAogICAgICAgICAgICAgICAgICAgICAgIG5vZGUgPSByZXAoZGF0YUFsbENvbWIkbm9kZSwgcG93ZXJzW2ksMV0pKQoKcG93ZXJzW2ksLTFdIDwtIHNpbUZhc3RNdWx0aXBsZUNvbnRyYXN0cyhmb3JtID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwcmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZXRhcyA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2QgPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJhc3RzID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5TaW0gPSAuLi4pCn0KcG93ZXJzCmBgYAoKYGBge3IsZXZhbD1GQUxTRX0KcG93ZXJzICU+JSAKICBhcy5kYXRhLmZyYW1lICU+JSAKICBnYXRoZXIoY29udHJhc3QsIHBvd2VyLCAtbikgJT4lIAogIGdncGxvdChhZXMobixwb3dlcixjb2xvcj1jb250cmFzdCkpICsKICBnZW9tX2xpbmUoKQpgYGAKCiMjIFBvd2VyIHdoZW4gRkMgZm9yIGdyYWRlIGluIHBhdGllbnRzIHdpdGggdW5hZmZlY3RlZCBseW1waCBub2RlcyBlcXVhbHMgMS41ICgkXGJldGFfZyA9IGxvZzIoMS41KSQpCgpgYGB7cixldmFsPUZBTFNFfQpwb3dlcjMgPC0gc2ltRmFzdE11bHRpcGxlQ29udHJhc3RzKGZvcm0gPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IC4uLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZXRhcyA9IC4uLiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2QgPSAuLi4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJhc3RzID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gLi4uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5TaW0gPSAuLi4pCnBvd2VyMwpgYGAKCg==